[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以上的圖片,因此這裏圖片存於公共圖牀,可能加載相對較慢時點擊這個連接前往騰訊雲對象存儲下載(流量賊啦貴,不到萬不得已不要下)

相關文章
相關標籤/搜索