[Android]第四次做业

 

1、团队成员html

李怡龙 学号:1600802046 博客地址:https://www.cnblogs.com/lee-li/android

刘显云 学号:1600802048 博客地址:https://www.cnblogs.com/lxy-y/git

刘志祥 学号:1600802049 博客地址:https://www.cnblogs.com/love-love/github

2、APK下载地址redis

Android:https://github.com/leeli73/Windroid/releases/download/1.0/Windroid.apk数据库

PC:https://github.com/leeli73/Windroid_Server_PC/releases/download/1.0/Windroid_PC.exe安全

Server:https://github.com/leeli73/Windroid_Server_PC/releases/download/1.0/Windroid_Server.exe服务器

3、项目地址微信

Android APP:https://github.com/leeli73/Windroid.gitide

Server PC:https://github.com/leeli73/Windroid_Server_PC.git

4、项目介绍

名称:Windroid

功能:主要用于共享Windows系统和Android手机的剪切板,用户不用在经过QQ、微信发信息给PC端,手机复制的信息能够共享给PC,PC复制的信息亦能够共享给手机。

主要构成:Windows端应用程序、Android端程序、Server端程序

4.1 团队项目的整体效果截图

Android登陆界面

 

Android设置界面

 

PC登陆界面

 

PC工做界面

PC端当登陆成功后,便会自动隐藏窗口,在后台运行

Server工做界面

 

4.2 实现的功能及其效果的描述

登陆

当用户输入用户名密码后,点击登陆,验证经过后便可进入设置页面

点击注册后,便可进行注册

设置信息(目前测试有BUG、在某些状况下会闪退,好比快速上下滑动)

在点击容许修改的表项后,就会弹出下面的输入框

输入完成后,点击肯定便可更新数据

 

5、项目中的关键代码

HTTP请求

使用OkHttp3库进行请求,主要用于登陆、注册、数据交换

  String url = "http://192.168.0.102:6888/SetData";
                            final OkHttpClient okHttpClient=new OkHttpClient();
                            RequestBody body = new FormBody.Builder()
                                    .add("UserID", Base64.encodeToString(StrUserID.getBytes(), Base64.DEFAULT))
                                    .add("Data",Base64.encodeToString(Data.getBytes(),Base64.DEFAULT))
                                    .build();
                            final Request request=new Request.Builder().url(url).post(body).build();
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        Response response=okHttpClient.newCall(request).execute();
                                        if (response.isSuccessful()){
                                            String body=response.body().string();
                                            Log.i("test",body);
                                        }
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }).start();

 

listview生成

每行都有一个指定的view与其对应,方便修改数据等操做

     AllInfo = findViewById(R.id.AllInfo);
        adapter = new BaseAdapter() {
            @Override
            public int getCount() {
                return 13;
            }

            @Override
            public Object getItem(int position) {
                return null;
            }

            @Override
            public long getItemId(int position) {
                return 0;
            }

            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
                LinearLayout linearLayout = new LinearLayout(body.this);
                TextView tital = new TextView(body.this);
                linearLayout.setOrientation(LinearLayout.VERTICAL);
                tital.setTextSize(25);
                switch (position)
                {
                    case 0:
                        tital.setText("用户信息");
                        tital.setGravity(LinearLayout.TEXT_ALIGNMENT_CENTER);
                        tital.setTextSize(30);
                        linearLayout.addView(tital);
                        break;
                    case 1:
                        tital.setText("用户名ID");
                        linearLayout.addView(tital);
                        linearLayout.addView(UserID);
                        break;
                    case 2:
                        tital.setText("用户名");
                        linearLayout.addView(tital);
                        linearLayout.addView(UserName);
                        break;
                    case 3:
                        tital.setText("电子邮箱");
                        linearLayout.addView(tital);
                        linearLayout.addView(Email);
                        break;
                    case 4:
                        tital.setText("密码");
                        linearLayout.addView(tital);
                        linearLayout.addView(PassWord);
                        break;
                    case 5:
                        tital.setText("设置");
                        tital.setGravity(LinearLayout.TEXT_ALIGNMENT_CENTER);
                        tital.setTextSize(30);
                        linearLayout.addView(tital);
                        break;
                    case 6:
                        tital.setText("最大数据长度/K");
                        linearLayout.addView(tital);
                        linearLayout.addView(MaxDataLength);
                        break;
                    case 7:
                        tital.setText("远程存储时间/s(<3600s)");
                        linearLayout.addView(tital);
                        linearLayout.addView(RomoteDataSaveDate);
                        break;
                    case 8:
                        tital.setText("本地存储时间/s(<3600s)");
                        linearLayout.addView(tital);
                        linearLayout.addView(LocalDataSaveTime);
                        break;
                    case 9:
                        tital.setText("局域网链接");
                        tital.setGravity(LinearLayout.TEXT_ALIGNMENT_CENTER);
                        tital.setTextSize(30);
                        linearLayout.addView(tital);
                        break;
                    case 10:
                        tital.setText("自动扫描");
                        linearLayout.addView(tital);
                        linearLayout.addView(LANAutoScan);
                        break;
                    case 11:
                        tital.setText("局域网IP");
                        linearLayout.addView(tital);
                        linearLayout.addView(LANIP);
                        break;
                    case 12:
                        tital.setText("端口");
                        linearLayout.addView(tital);
                        linearLayout.addView(LANPort);
                        break;
                }
                return linearLayout;
            }
        };
        AllInfo.setAdapter(adapter);

 

初始化数据

将asset目录下的配置读取并处理

     try
        {
            InputStreamReader inputStreamReader = new InputStreamReader(getResources().getAssets().open("UserInfo.data"));
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line="";
            while((line=bufferedReader.readLine())!=null)
            {
                String Temp[] = line.split(":");
                if(Temp[0].equals("Username"))
                {
                    StrUserName = new String(Temp[1]);
                }
                else if(Temp[0].equals("Password"))
                {
                    StrPassWord = new String(Temp[1]);
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        try
        {
            InputStreamReader inputStreamReader = new InputStreamReader(getResources().getAssets().open("Setting.data"));
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line="";
            while((line=bufferedReader.readLine())!=null)
            {
                String Temp[] = line.split(":");
                if(Temp[0].equals("MaxDataLength"))
                {
                    StrMaxDataLength = new String(Temp[1]);
                }
                else if(Temp[0].equals("LocalDataSaveTime"))
                {
                    StrLocalDataSaveTime = new String(Temp[1]);
                }
                else if(Temp[0].equals("RemoteDataSaveTime"))
                {
                    StrRomoteDataSaveDate = new String(Temp[1]);
                }
                else if(Temp[0].equals("UserID"))
                {
                    StrUserID = new String(Temp[1]);
                }
                else if(Temp[0].equals("Email"))
                {
                    StrEmail = new String(Temp[1]);
                }
                else if(Temp[0].equals("LANIP"))
                {
                    StrLANIP = new String(Temp[1]);
                }
                else if(Temp[0].equals("LANPort"))
                {
                    StrLANPort = new String(Temp[1]);
                }
                else if(Temp[0].equals("LANAutoScan"))
                {
                    StrLANAutoScan = new String(Temp[1]);
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

 

listview点击弹出提示

根据点击位置读取输入和判断是否容许修改

AllInfo.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, final int position, long id) {
                final EditText MyInput = new EditText(body.this);
                AlertDialog.Builder builder = new AlertDialog.Builder(body.this);
                builder.setTitle("请输入信息").setIcon(android.R.drawable.ic_dialog_alert).setView(MyInput).setNegativeButton("取消",null);
                builder.setPositiveButton("肯定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        switch (position)
                        {
                            case 0:
                                //用户信息
                                break;
                            case 1:
                                //用户名ID
                                Toast.makeText(body.this,"用户ID不容许修改",Toast.LENGTH_SHORT).show();
                                break;
                            case 2:
                                //用户名
                                Toast.makeText(body.this,"用户名不容许修改",Toast.LENGTH_SHORT).show();
                                break;
                            case 3:
                                //电子邮箱
                                StrEmail = MyInput.getText().toString();
                                Email.setText(StrEmail);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 4:
                                //密码
                                StrPassWord = MyInput.getText().toString();
                                PassWord.setText(StrPassWord);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 5:
                                //设置
                                break;
                            case 6:
                                //最大数据长度
                                StrMaxDataLength = MyInput.getText().toString();
                                MaxDataLength.setText(StrMaxDataLength);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 7:
                                //远程存储时间/s(<3600s)
                                StrRomoteDataSaveDate = MyInput.getText().toString();
                                RomoteDataSaveDate.setText(StrRomoteDataSaveDate);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 8:
                                //本地存储时间/s(<3600s)
                                StrLocalDataSaveTime = MyInput.getText().toString();
                                LocalDataSaveTime.setText(StrLocalDataSaveTime);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 9:
                                //局域网链接
                                break;
                            case 10:
                                //自动扫描
                                StrLANAutoScan = MyInput.getText().toString();
                                LANAutoScan.setText(StrLANAutoScan);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 11:
                                //局域网IP
                                StrLANIP = MyInput.getText().toString();
                                LANIP.setText(StrLANIP);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                            case 12:
                                //端口
                                StrLANPort = MyInput.getText().toString();
                                LANPort.setText(StrLANPort);
                                Toast.makeText(body.this,"修改为功",Toast.LENGTH_SHORT).show();
                                break;
                        }
                    }
                });
                builder.show();
            }
        });

 

本地剪切板监控线程(存在问题,彻底按照官方API编写的读写剪切板,可是会闪退,API11以前和以后的方法所有尝试,依旧没法解决)

启动一个线程,循环监控本地剪切板

new Thread(new Runnable() {
            @Override
            public void run() {
                try
                {
                    while (true)
                    {
                        String url = "http://192.168.0.102:6888/GetData";
                        final OkHttpClient okHttpClient=new OkHttpClient();
                        RequestBody body = new FormBody.Builder()
                                .add("UserID", Base64.encodeToString(StrUserID.getBytes(), Base64.DEFAULT))
                                .build();
                        final Request request=new Request.Builder().url(url).post(body).build();
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Response response=okHttpClient.newCall(request).execute();
                                    if (response.isSuccessful()){
                                        String body=response.body().string();
                                        String Temp[] = body.split("@");
                                        if(Temp[0].equals("New"))
                                        {
                                            /*//获取剪贴板管理器:
                                            ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
                                            // 建立普通字符型ClipData
                                            ClipData mClipData = ClipData.newPlainText("Label", Temp[1]);
                                            // 将ClipData内容放到系统剪贴板里。
                                            cm.setPrimaryClip(mClipData);*/
                                            Log.i("test","Get New Data "+ Temp[1]);
                                        }
                                        else
                                        {
                                            Log.i("test","No New Data");
                                        }
                                    }
                                    else
                                    {
                                        Log.i("test","No New Data");
                                    }
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }).start();
                        Thread.sleep(1000);
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }).start();

 

远程服务器获取数据线程(存在问题,彻底按照官方API编写的读写剪切板,可是会闪退,API11以前和以后的方法所有尝试,依旧没法解决)

启动一个线程,循环监控远程剪切板

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String OldData = "";
                    while (true)
                    {
                        Log.i("test","Set");
                        //ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
                        //String Data = cm.getText().toString().trim();
                        //Log.i("test",Data);
                        String Data = "123";
                        if(!Data.equals(OldData))
                        {
                            String url = "http://192.168.0.102:6888/SetData";
                            final OkHttpClient okHttpClient=new OkHttpClient();
                            RequestBody body = new FormBody.Builder()
                                    .add("UserID", Base64.encodeToString(StrUserID.getBytes(), Base64.DEFAULT))
                                    .add("Data",Base64.encodeToString(Data.getBytes(),Base64.DEFAULT))
                                    .build();
                            final Request request=new Request.Builder().url(url).post(body).build();
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        Response response=okHttpClient.newCall(request).execute();
                                        if (response.isSuccessful()){
                                            String body=response.body().string();
                                            Log.i("test",body);
                                        }
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }).start();
                        }
                        Thread.sleep(1000);
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }).start();

更换返回键的功能为回到桌面

     Intent intent = new Intent();
        intent.setAction(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        startActivity(intent);
        Toast.makeText(body.this,"Windroid进入后台运行",Toast.LENGTH_SHORT).show();

 

6、心目中的前五名

一、季澈组 http://www.javashuo.com/article/p-myzmpmmy-ka.html

相似网易云音乐界面美观优雅的音乐播放器

项目优势:自动搜集本地音乐,有上一曲,下一曲,开始暂停,顺序播放,随机播放,单首播放,音量的控制,进度条,歌词,能够删除歌曲

项目缺点:没法加载云端的音乐,不能说是一个完美的音乐播放器。没有用户机制,没法保存本身的歌曲列表。

个人设想:支持播放云端的音乐,爬虫现有几个音乐播放器的资源。加入用户机制。

二、贺鸿琨组 http://www.javashuo.com/article/p-ycxizxoy-kg.html

相似QQ音乐界面,选择图片资源很优秀

项目优势:完成了歌曲列表与播放界面之间的切换,完成了播放过程当中图片旋转状态与歌曲播放状态的绑定,还完成了歌曲进度条与歌曲进度的绑定。

项目缺点:没法加载云端的音乐,没有用户机制

个人设想:支持播放云端的音乐,爬虫现有几个音乐播放器的资源

三、李凌龙组 http://www.javashuo.com/article/p-ereceufw-hq.html

对于我这样爱忘事者,这是一个刚需,简单使用

项目优势:功能齐全,界面简单

项目缺点:没有批量删除功能

个人设想:支持语音助手,一句话就能设定好

四、田光欣组 http://www.javashuo.com/article/p-nkglnrye-gy.html

简单使用,没有花里胡哨功能的记事本

项目优势:界面简单,功能齐全

项目缺点:数据存储在本地,更换手机后没法使用

个人设想:支持语音助手,一句话记下文本,支持图片、音频、视频的记录。将数据加密存储在服务器上。

五、李钊组 http://www.javashuo.com/article/p-nxdfkfxp-eq.html

功能齐全的乒乓球社区,乒乓球爱好者的必备

项目优势:功能齐全,不管是视频、排名、照片等都能一次性了解到

项目缺点:没法联网实时获取最新的数据

个人设想:APP自动去互联网上爬去最新的信息,向用户展现最新的数据

7、遇到的问题及解决方案

7.1 读写剪切板(任然未解决)

李怡龙 1600802046

使用最新的API,程序运行至此处会闪退

    //获取剪贴板管理器:
      ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
      // 建立普通字符型ClipData
      ClipData mClipData = ClipData.newPlainText("Label", Temp[1]);
      // 将ClipData内容放到系统剪贴板里。
      cm.setPrimaryClip(mClipData);

    换用老版API,程序依旧闪退

    cm.setText()

 

7.2 乱码问题

李怡龙 1600802046

咱们发如今传输中文的过程当中,会出现乱码的问题,由于咱们采用POST的形式,传输数据,若是有&等符号也会出现问题

因此咱们决定对全部通信过程当中的数据进行BASE64编码

Android端

RequestBody body = new FormBody.Builder()
                                .add("UserID", Base64.encodeToString(StrUserID.getBytes(), Base64.DEFAULT))
                                .build();

PC端

   Base64 base64;
    Username = base64.encode(Username.toLatin1());
    Password = base64.encode(Password.toLatin1());

Server端

RealUsernameBase64,err := base64.StdEncoding.DecodeString(Username[0])
    if err != nil{
        w.Write([]byte("error"))
        return 
    }
    RealPasswordBase64,err := base64.StdEncoding.DecodeString(Password[0])
    if err != nil{
        w.Write([]byte("error"))
        return 
    }
    RealUsername := string(RealUsernameBase64)
    RealPassword := string(RealPasswordBase64)

7.3 读取本地Asset目录下的配置文件

李怡龙 1600802046

由于咱们存储的数据相对较少,并且不敏感,因此采用文本的形式存储

起初咱们准备使用JSON的形式存储,可是在解析的JSON的过程当中有部分问题,最后换用自定义格式的文本

        InputStreamReader inputStreamReader = new InputStreamReader(getResources().getAssets().open("UserInfo.data"));
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line="";
            while((line=bufferedReader.readLine())!=null)
            {}

7.4 Server中Redis的使用

李怡龙 1600802046

由于咱们的数据存在必定的时效性,并且要求访问必须作到低延时,因此咱们决定使用Redis 内存K-V型数据库

在查阅文档后,咱们采用了"github.com/garyburd/redigo/redis"包进行redis的各种操做

并创建两张哈希表

第一张为用户信息表

key:Username value:UserID Password Email PhoneNumber SaveTime
_, err := RedisClient.Do("HMSET",Username,"UserID",UserID,"UserPassword",Password,"Email",Email,"PhoneNumber",PhoneNumber,"SaveTime",SaveTime)
    if err != nil {
        fmt.Println("redis hset error:", err)
        return false
    } else {
        //_,err := RedisClient.Do("expire","myKey","10")
        return true
    }

 

第二张为数据表

key :UserID value:Data

      _,err1 := RedisClient.Do("HMSET",RealUserID,"Data",RealData)
        if err1 != nil {
            fmt.Println("redis hset error:", err)
            w.Write([]byte("SetError"))
        } else {
            //_,err := RedisClient.Do("expire","myKey","10")
            w.Write([]byte("SetSuccess"))
        }

7.5 由于技术上的问题,咱们最先想要实现的局域网自动扫描没有编写出来,因此如今全部的功能必须经由服务器

 

8、分工

姓名      分工                                                                 工做比例      分数

李怡龙  服务器、PC端、安卓端POST、剪切板操做    50%             5

刘显云  安卓端登陆UI设计、数据读取、存储               25%             2.5

刘志祥  安卓端listview设计及响应                                25%             2.5

9、运行演示

启动Server及PC(Server实际运行于服务器,这里只是用于演示)

Server工做中交换数据的输出(实际工做中不输出,这里只用于演示效果)

Windroid安卓端

不知什么缘由,登陆界面的动画在模拟器中没法显示,因此这里在小米MIX(Android8.0)环境下演示

演示途中的黑屏,是应为调起小米安全键盘时,MIUI系统不容许录制该部分,因此自动进行了遮挡

博客园限制没法上传20M以上的图片,因此这里图片存于公共图床,可能加载相对较慢时点击这个连接前往腾讯云对象存储下载(流量贼啦贵,不到万不得已不要下)

相关文章
相关标签/搜索