소스 검색

Merge branch 'master' of lhm/ich-android into master

李慧明 3 년 전
부모
커밋
2eeaf0f982
25개의 변경된 파일625개의 추가작업 그리고 50개의 파일을 삭제
  1. 13 9
      app/src/main/AndroidManifest.xml
  2. 92 5
      app/src/main/java/com/emato/ich/MainActivity.java
  3. 1 1
      app/src/main/java/com/emato/ich/api/ICSPApi.java
  4. 18 1
      app/src/main/java/com/emato/ich/api/ICSPClient.java
  5. 1 1
      app/src/main/java/com/emato/ich/api/ICSPConstant.java
  6. 2 0
      app/src/main/java/com/emato/ich/api/SystemConfigConstant.java
  7. 1 8
      app/src/main/java/com/emato/ich/crash/CrashApplication.java
  8. 10 9
      app/src/main/java/com/emato/ich/crash/UncaughtExceptionHandlerImpl.java
  9. 29 3
      app/src/main/java/com/emato/ich/fragment/MainFragment.java
  10. 26 3
      app/src/main/java/com/emato/ich/fragment/TakeCodeFragment.java
  11. 28 0
      app/src/main/java/com/emato/ich/fragment/TakeFragment.java
  12. 1 1
      app/src/main/java/com/emato/ich/local/LocalStorage.java
  13. 2 0
      app/src/main/java/com/emato/ich/message/ICHTopic.java
  14. 127 0
      app/src/main/java/com/emato/ich/update/APKUpdateDownload.java
  15. 27 0
      app/src/main/java/com/emato/ich/update/InstallResultReceiver.java
  16. 23 0
      app/src/main/java/com/emato/ich/update/OnDownloadListener.java
  17. 16 0
      app/src/main/java/com/emato/ich/update/OnInstallListener.java
  18. 162 0
      app/src/main/java/com/emato/ich/update/PackageManagerCompat.java
  19. 31 0
      app/src/main/java/com/emato/ich/utils/BaseUtils.java
  20. 0 0
      app/src/main/res/drawable/e_mp_qrcode_8x8_backup.jpg
  21. 6 3
      app/src/main/res/layout/fragment_input_info.xml
  22. 1 1
      app/src/main/res/layout/fragment_main.xml
  23. 4 2
      app/src/main/res/layout/fragment_send.xml
  24. 1 1
      app/src/main/res/layout/fragment_take.xml
  25. 3 2
      app/src/main/res/layout/fragment_take_code.xml

+ 13 - 9
app/src/main/AndroidManifest.xml

@@ -1,10 +1,22 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    package="com.emato.ich">
+    package="com.emato.ich" >
 
+<!--    android:sharedUserId="android.uid.system"-->
     <!-- networkSecurityConfig: 这里配置为强制使用HTTP请求 -->
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permisson.INTERNETINTERNET"/>
+    <uses-permission android:name="android.permisson.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+    <!-- 自动更新需要的权限 -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><!-- android10+ 不能用 -->
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <permission android:name="android.permission.INSTALL_PACKAGES" />
     <application
         android:networkSecurityConfig="@xml/network_security_config"
         android:allowBackup="true"
@@ -28,12 +40,4 @@
         </activity>
         <service android:name="org.eclipse.paho.android.service.MqttService"/>
     </application>
-    <uses-permission android:name="android.permission.INTERNET"
-        tools:ignore="ManifestOrder" />
-    <uses-permission android:name="android.permisson.INTERNETINTERNET"/>
-    <uses-permission android:name="android.permisson.WAKE_LOCK"/>
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
-        tools:ignore="ProtectedPermissions" />
-
 </manifest>

+ 92 - 5
app/src/main/java/com/emato/ich/MainActivity.java

@@ -9,6 +9,7 @@ import com.cherry.sdk.controller.callback.OnScanListen;
 import com.cherry.sdk.controller.utils.ScanGunKeyEventHelper;
 import com.emato.ich.api.ICSPClient;
 import com.emato.ich.api.ICSPResponseCodeEnum;
+import com.emato.ich.api.SystemConfigConstant;
 import com.emato.ich.crash.CrashApplication;
 import com.emato.ich.crash.UncaughtExceptionHandlerImpl;
 import com.emato.ich.device.DeviceControl;
@@ -19,6 +20,8 @@ import com.emato.ich.fragment.InputInfoFragment;
 import com.emato.ich.message.ICHPublishClient;
 import com.emato.ich.message.ICHSubscribeClient;
 import com.emato.ich.message.ICHTopic;
+import com.emato.ich.update.APKUpdateDownload;
+import com.emato.ich.update.OnDownloadListener;
 import com.emato.ich.utils.BaseUtils;
 import com.emato.ich.utils.Md5Utils;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -53,17 +56,16 @@ import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
 import org.eclipse.paho.client.mqttv3.MqttMessage;
 import org.jetbrains.annotations.NotNull;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import okhttp3.Call;
 import okhttp3.Callback;
@@ -93,7 +95,7 @@ public class MainActivity extends AppCompatActivity  {
         NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
 
         // 自动重启
-        UncaughtExceptionHandlerImpl.getInstance().init(getApplication(), BuildConfig.DEBUG, true, 0, MainActivity.class);
+        UncaughtExceptionHandlerImpl.getInstance().init(getApplication(), BuildConfig.DEBUG, true, 1000, MainActivity.class);
 
         // 获取系统配置
         ICSPClient.getSystemConfig("", BaseUtils.getClientId(), new Callback() {
@@ -107,12 +109,90 @@ public class MainActivity extends AppCompatActivity  {
                     (@NotNull Call call, @NotNull Response response) throws IOException {
 
                 try {
+                    OnDownloadListener listener = new OnDownloadListener() {
+
+                        @Override
+                        public void onDownloadSuccess(File file) {
+
+                        }
+
+                        @Override
+                        public void onDownloading(int progress) {
+
+                        }
+
+                        @Override
+                        public void onDownloadFailed(Exception e) {
+
+                        }
+                    };
                     String parseResponse = ICSPClient.isSuccessfulAndParseResponse(response);
                     ObjectMapper objectMapper = new ObjectMapper();
                     ResponseData<Map<String, String>> readValue = objectMapper.readValue(parseResponse, new TypeReference<ResponseData<Map<String, String>>>() {
                     });
                     if (null != readValue && readValue.getCode().equals(ICSPResponseCodeEnum.OK.getCode())) {
                         configMap.putAll(readValue.getData());
+                        String qrcode_url = configMap.get(SystemConfigConstant.cabinet_take_object_qrcode_url);
+
+                        try {
+                            ICSPClient.download(qrcode_url, new Callback() {
+                                @Override
+                                public void onFailure(@NotNull Call call, @NotNull IOException e) {
+
+                                }
+
+                                @Override
+                                public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+                                    InputStream is = null;
+                                    byte[] buf = new byte[2048];
+                                    int len = 0;
+                                    FileOutputStream fos = null;
+                                    final String destFileDir = "res/drawable/";
+                                    String destFileName = "e_mp_qrcode_8x8.jpg";
+                                    //储存下载文件的目录
+                                    File dir = new File(getApplication().getFilesDir().getAbsolutePath() + "/" +destFileDir);
+                                    if (!dir.exists()) {
+                                        dir.mkdirs();
+                                    }
+//                destFileName = apk_url.substring(apk_url.lastIndexOf('/'), apk_url.length() - 1);
+                                    File file = new File(dir, destFileName);
+
+                                    try {
+
+                                        is = response.body().byteStream();
+                                        long total = response.body().contentLength();
+                                        fos = new FileOutputStream(file);
+                                        long sum = 0;
+                                        while ((len = is.read(buf)) != -1) {
+                                            fos.write(buf, 0, len);
+                                            sum += len;
+                                            int progress = (int) (sum * 1.0f / total * 100);
+                                            //下载中更新进度条
+                                            listener.onDownloading(progress);
+                                        }
+                                        fos.flush();
+                                        //下载完成
+                                        listener.onDownloadSuccess(file);
+                                    } catch (Exception e) {
+                                        listener.onDownloadFailed(e);
+                                    }finally {
+                                        try {
+                                            if (is != null) {
+                                                is.close();
+                                            }
+                                            if (fos != null) {
+                                                fos.close();
+                                            }
+                                        } catch (IOException e) {
+
+                                        }
+
+                                    }
+                                }
+                            }, listener);
+                        } catch (Exception e) {
+
+                        }
                         Log.i(TAG, "onResponse: 获取系统配置成功! ");
                     } else {
                         Log.w(TAG, "onResponse: code==>" + readValue.getCode() + ", msg==>" + readValue.getMsg());
@@ -225,6 +305,13 @@ public class MainActivity extends AppCompatActivity  {
         } catch (JsonProcessingException e) {
             Log.e(TAG, "onCreate: 解析成JSON失败!", e);
         }
+
+        // TODO 更新版本监听
+        ichPublishClient.subscribe(String.format(ICHTopic.APK_UPDATE_PATH, BaseUtils.getClientId()), (msgId, msg) -> {
+            String path = new String(msg.getPayload());
+            // 发起请求下载APK
+            APKUpdateDownload.getInstance().downloadAPK(MainActivity.this, getApplication(), path);
+        });
     }
 
     @Override

+ 1 - 1
app/src/main/java/com/emato/ich/api/ICSPApi.java

@@ -25,7 +25,7 @@ public enum  ICSPApi {
     /**
      * 取件接口
      */
-    TAKE_PARCEL(ICSPConstant.ICSP_SERVICE + "/order/collect/takeObject", "POST"),
+    TAKE_PARCEL(ICSPConstant.ICSP_SERVICE + "/order/collect/pickUpTakeObject", "POST"),
     /**
      * 获取配置
      */

+ 18 - 1
app/src/main/java/com/emato/ich/api/ICSPClient.java

@@ -5,6 +5,7 @@ import android.util.Log;
 import com.emato.ich.entity.vo.ConfirmOrderVo;
 import com.emato.ich.entity.vo.PreparedOrderVo;
 import com.emato.ich.entity.vo.TakeParcelVo;
+import com.emato.ich.update.OnDownloadListener;
 import com.emato.ich.utils.BaseUtils;
 import com.emato.ich.utils.StringUtils;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -15,6 +16,7 @@ import org.jetbrains.annotations.NotNull;
 import java.io.IOException;
 import java.util.Date;
 import java.util.Objects;
+import java.util.concurrent.TimeUnit;
 
 import okhttp3.Call;
 import okhttp3.Callback;
@@ -156,6 +158,20 @@ public class ICSPClient {
 
     }
 
+    /**
+     * 下载APK
+     * @param url               下载链接
+     * @param callback          请求回调
+     * @param listener          下载文件回调
+     */
+    public static void download(final String url, Callback callback, OnDownloadListener listener) {
+
+        Request request = getRequest(url, "");
+
+        client.newCall(request).enqueue(callback);
+
+    }
+
 
     public static String isSuccessfulAndParseResponse(Response response) {
 
@@ -190,7 +206,8 @@ public class ICSPClient {
 
     static class ICSPClientInnerClass{
 
-        private static final OkHttpClient CLIENT = new OkHttpClient();
+        private static final OkHttpClient CLIENT = new OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS)
+                .readTimeout(60, TimeUnit.SECONDS).build();
 
     }
 }

+ 1 - 1
app/src/main/java/com/emato/ich/api/ICSPConstant.java

@@ -5,5 +5,5 @@ public class ICSPConstant {
     public static final String ICSP_SERVICE = "http://68rfyvi.nat.ipyingshe.com";
 //    public static final String ICSP_SERVICE = "http://icsp-testend.ds-bay.com";
 
-    public static final String MQTT_SERVER_ADDRESS = "tcp://183.3.221.143:1883";
+    public static final String MQTT_SERVER_ADDRESS = "tcp://8.135.102.238:1883";
 }

+ 2 - 0
app/src/main/java/com/emato/ich/api/SystemConfigConstant.java

@@ -7,5 +7,7 @@ public class SystemConfigConstant {
 
     public static final String cabinet_take_object_reopen_time = "cabinet_take_object_reopen_time";
 
+    public static final String cabinet_take_object_qrcode_url = "cabinet_take_object_qrcode_url";
+
 
 }

+ 1 - 8
app/src/main/java/com/emato/ich/crash/CrashApplication.java

@@ -7,14 +7,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 public class CrashApplication extends Application {
-    private List<Activity> mActivityList;
-
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mActivityList = new ArrayList<>();
-    }
+    private final List<Activity> mActivityList = new ArrayList<>();
 
     /**
      * 添加单个Activity

+ 10 - 9
app/src/main/java/com/emato/ich/crash/UncaughtExceptionHandlerImpl.java

@@ -75,17 +75,18 @@ public class UncaughtExceptionHandlerImpl implements Thread.UncaughtExceptionHan
             try {
                 Thread.sleep(2000L);
             } catch (InterruptedException var6) {
-                Log.e("CrashHandler", "error : ", var6);
+                Log.e(TAG, "error : ", var6);
             }
 
             if (this.mIsRestartApp) {
                 Intent intent = new Intent(this.mContext.getApplicationContext(), this.mRestartActivity);
-                @SuppressLint("WrongConstant") AlarmManager mAlarmManager = (AlarmManager)this.mContext.getSystemService("alarm");
-                @SuppressLint("WrongConstant") PendingIntent restartIntent = PendingIntent.getActivity(this.mContext.getApplicationContext(), 0, intent, 268435456);
-                mAlarmManager.set(1, System.currentTimeMillis() + this.mRestartTime, restartIntent);
+                @SuppressLint("WrongConstant") AlarmManager mAlarmManager = (AlarmManager)this.mContext.getSystemService(Context.ALARM_SERVICE);
+                @SuppressLint("WrongConstant") PendingIntent restartIntent = PendingIntent.getActivity(this.mContext.getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+                mAlarmManager.set(AlarmManager.RTC, System.currentTimeMillis() + this.mRestartTime, restartIntent);
             }
 
-            ((CrashApplication)this.mContext.getApplicationContext()).removeAllActivity();
+            android.os.Process.killProcess(android.os.Process.myPid());
+//            ((CrashApplication)this.mContext.getApplicationContext()).removeAllActivity();
         }
 
     }
@@ -137,7 +138,7 @@ public class UncaughtExceptionHandlerImpl implements Thread.UncaughtExceptionHan
                 this.infos.put("versionCode", versionCode);
             }
         } catch (PackageManager.NameNotFoundException var9) {
-            Log.e("CrashHandler", "an error occured when collect package info", var9);
+            Log.e(TAG, "an error occured when collect package info", var9);
         }
 
         Field[] fields = Build.class.getDeclaredFields();
@@ -150,9 +151,9 @@ public class UncaughtExceptionHandlerImpl implements Thread.UncaughtExceptionHan
             try {
                 field.setAccessible(true);
                 this.infos.put(field.getName(), field.get((Object)null).toString());
-                Log.d("CrashHandler", field.getName() + " : " + field.get((Object)null));
+                Log.d(TAG, field.getName() + " : " + field.get((Object)null));
             } catch (Exception var8) {
-                Log.e("CrashHandler", "an error occured when collect crash info", var8);
+                Log.e(TAG, "an error occured when collect crash info", var8);
             }
         }
 
@@ -200,7 +201,7 @@ public class UncaughtExceptionHandlerImpl implements Thread.UncaughtExceptionHan
 
             return fileName;
         } catch (Exception var14) {
-            Log.e("CrashHandler", "an error occured while writing file...", var14);
+            Log.e(TAG, "an error occured while writing file...", var14);
             return null;
         }
     }

+ 29 - 3
app/src/main/java/com/emato/ich/fragment/MainFragment.java

@@ -1,5 +1,6 @@
 package com.emato.ich.fragment;
 
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -9,9 +10,15 @@ import androidx.annotation.NonNull;
 import androidx.fragment.app.Fragment;
 import androidx.navigation.fragment.NavHostFragment;
 
+import com.emato.ich.MainActivity;
 import com.emato.ich.R;
+import com.emato.ich.api.SystemConfigConstant;
 import com.emato.ich.databinding.FragmentMainBinding;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
 public class MainFragment extends Fragment {
 
     private FragmentMainBinding binding;
@@ -29,12 +36,31 @@ public class MainFragment extends Fragment {
 
     public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
+        MainActivity activity = (MainActivity) getActivity();
+        String url = activity.getConfigMap().get(SystemConfigConstant.cabinet_take_object_qrcode_url);
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    InputStream is = (InputStream) new URL(url).getContent();
+                    final Drawable d = Drawable.createFromStream(is, "src");
+                    is.close();
+                    activity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            binding.fastTakeSend.setBackground(d);
+                        }
+                    });
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }).start();
+
         // 投递页面跳转
         binding.sendBtn.setOnClickListener(view12 -> NavHostFragment.findNavController(MainFragment.this)
                 .navigate(R.id.action_mainFragment_to_sendFragment));
-//        binding.sendBtn.setOnClickListener(view12 -> {
-//            throw new RuntimeException("测试Crash");
-//        });
         // 取件页面跳转
         binding.takeBtn.setOnClickListener(view1 -> NavHostFragment.findNavController(MainFragment.this)
                 .navigate(R.id.action_mainFragment_to_takeFragment));

+ 26 - 3
app/src/main/java/com/emato/ich/fragment/TakeCodeFragment.java

@@ -1,5 +1,6 @@
 package com.emato.ich.fragment;
 
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.Editable;
 import android.util.Log;
@@ -17,11 +18,10 @@ import com.emato.ich.MainActivity;
 import com.emato.ich.R;
 import com.emato.ich.api.ICSPClient;
 import com.emato.ich.api.ICSPResponseCodeEnum;
+import com.emato.ich.api.SystemConfigConstant;
 import com.emato.ich.databinding.FragmentTakeCodeBinding;
-import com.emato.ich.device.DeviceControl;
 import com.emato.ich.entity.vo.ResponseData;
 import com.emato.ich.entity.vo.TakeParcelVo;
-import com.emato.ich.local.LocalStorage;
 import com.emato.ich.utils.BaseUtils;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
@@ -31,7 +31,8 @@ import com.google.android.material.snackbar.Snackbar;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.IOException;
-import java.util.Arrays;
+import java.io.InputStream;
+import java.net.URL;
 
 import okhttp3.Call;
 import okhttp3.Callback;
@@ -58,6 +59,28 @@ public class TakeCodeFragment extends Fragment {
     public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
+//        binding.textView.setBackgroundResource(R.drawable.e_mp_qrcode_8x8_backup);
+        MainActivity activity = (MainActivity) getActivity();
+        String url = activity.getConfigMap().get(SystemConfigConstant.cabinet_take_object_qrcode_url);
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    InputStream is = (InputStream) new URL(url).getContent();
+                    final Drawable d = Drawable.createFromStream(is, "src");
+                    is.close();
+                    activity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            binding.textView.setBackground(d);
+                        }
+                    });
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }).start();
         // 选中输入框
 //        binding.editTextTextPersonName6.requestFocus();
 

+ 28 - 0
app/src/main/java/com/emato/ich/fragment/TakeFragment.java

@@ -1,5 +1,6 @@
 package com.emato.ich.fragment;
 
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -10,11 +11,17 @@ import androidx.annotation.Nullable;
 import androidx.fragment.app.Fragment;
 import androidx.navigation.fragment.NavHostFragment;
 
+import com.emato.ich.MainActivity;
 import com.emato.ich.R;
+import com.emato.ich.api.SystemConfigConstant;
 import com.emato.ich.databinding.FragmentTakeBinding;
 
 import org.jetbrains.annotations.NotNull;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
 /**
  * 取件提示页面
  */
@@ -34,6 +41,27 @@ public class TakeFragment extends Fragment {
     public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
 
+        MainActivity activity = (MainActivity) getActivity();
+        String url = activity.getConfigMap().get(SystemConfigConstant.cabinet_take_object_qrcode_url);
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    InputStream is = (InputStream) new URL(url).getContent();
+                    final Drawable d = Drawable.createFromStream(is, "src");
+                    is.close();
+                    activity.runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            binding.textView.setBackground(d);
+                        }
+                    });
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }).start();
         // TODO 对接微信
 
         // 跳转输入取件码

+ 1 - 1
app/src/main/java/com/emato/ich/local/LocalStorage.java

@@ -79,7 +79,7 @@ public class LocalStorage {
         }
 
         public void setToken(String token) {
-            this.token = token;
+            this.token = token.replace("\n", "").trim();
         }
 
         public long getTime() {

+ 2 - 0
app/src/main/java/com/emato/ich/message/ICHTopic.java

@@ -14,4 +14,6 @@ public class ICHTopic {
     public static final String CALLBACK_SUCCESS = "/icsp-client/msg/callback/success/%s";
 
     public static final String CALLBACK_FAILED = "/icsp-client/msg/callback/fail/%s";
+
+    public static final String APK_UPDATE_PATH = "/icsp-server/apk/upgrade/%s";
 }

+ 127 - 0
app/src/main/java/com/emato/ich/update/APKUpdateDownload.java

@@ -0,0 +1,127 @@
+package com.emato.ich.update;
+
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.Log;
+
+import com.emato.ich.api.ICSPClient;
+import com.emato.ich.utils.BaseUtils;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.Response;
+
+public class APKUpdateDownload {
+
+    private static final String TAG = APKUpdateDownload.class.getName();
+    private static final String destFileDir = "res/apk/";
+    private static String destFileName = "ich-android.apk";
+    private static final APKUpdateDownload INSTANCE = APKUpdateDownloadInnerClass.apkUpdateDownload;
+
+    private APKUpdateDownload(){}
+
+    public static APKUpdateDownload getInstance(){
+        return INSTANCE;
+    }
+
+    static class APKUpdateDownloadInnerClass{
+        private static final APKUpdateDownload apkUpdateDownload = new APKUpdateDownload();
+    }
+
+    public void downloadAPK(Activity activity, Context context, String apk_url) {
+        OnDownloadListener listener = new OnDownloadListener() {
+            @Override
+            public void onDownloadSuccess(File file) {
+                Log.i(TAG, "onDownloadSuccess: 下载完成! ");
+                // 文件路径
+                String absolutePath = file.getAbsolutePath();
+                // 需要自动安装并重启
+                PackageManagerCompat.install(context, absolutePath, context.getPackageManager(), new OnInstallListener() {
+                    @Override
+                    public void onInstallException(Exception e) {
+                        Log.e(TAG, "onInstallException: 安装出现异常! ", e);
+                    }
+
+                    @Override
+                    public void onInstallSuccess() {
+                        // TODO 重启应用 重启失败? 一直重启?
+                        PackageManagerCompat.restartApp(context);
+                    }
+                });
+            }
+
+            @Override
+            public void onDownloading(int progress) {
+                Log.i(TAG, "onDownloading: 下载进度========>" + progress + "%");
+            }
+
+            @Override
+            public void onDownloadFailed(Exception e) {
+                Log.e(TAG, "onDownloadFailed: 下载失败! 原因==> ", e);
+            }
+        };
+        ICSPClient.download(apk_url, new Callback() {
+            @Override
+            public void onFailure(@NotNull Call call, @NotNull IOException e) {
+                listener.onDownloadFailed(e);
+            }
+
+            @Override
+            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
+                InputStream is = null;
+                byte[] buf = new byte[2048];
+                int len = 0;
+                FileOutputStream fos = null;
+
+                //储存下载文件的目录
+                File dir = new File(context.getFilesDir().getAbsolutePath() + "/" +destFileDir);
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+//                destFileName = apk_url.substring(apk_url.lastIndexOf('/'), apk_url.length() - 1);
+                File file = new File(dir, destFileName);
+
+                try {
+
+                    is = response.body().byteStream();
+                    long total = response.body().contentLength();
+                    fos = new FileOutputStream(file);
+                    long sum = 0;
+                    while ((len = is.read(buf)) != -1) {
+                        fos.write(buf, 0, len);
+                        sum += len;
+                        int progress = (int) (sum * 1.0f / total * 100);
+                        //下载中更新进度条
+                        listener.onDownloading(progress);
+                    }
+                    fos.flush();
+                    //下载完成
+                    listener.onDownloadSuccess(file);
+                } catch (Exception e) {
+                    listener.onDownloadFailed(e);
+                }finally {
+                    try {
+                        if (is != null) {
+                            is.close();
+                        }
+                        if (fos != null) {
+                            fos.close();
+                        }
+                    } catch (IOException e) {
+
+                    }
+
+                }
+            }
+        }, listener);
+    }
+
+}

+ 27 - 0
app/src/main/java/com/emato/ich/update/InstallResultReceiver.java

@@ -0,0 +1,27 @@
+package com.emato.ich.update;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.util.Log;
+
+public class InstallResultReceiver extends BroadcastReceiver {
+
+    private static final String TAG = InstallResultReceiver.class.getName();
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive: "+intent.getIntExtra(PackageInstaller.EXTRA_STATUS,PackageInstaller.STATUS_FAILURE));
+        if (intent != null) {
+            final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,PackageInstaller.STATUS_FAILURE);
+            if (status == PackageInstaller.STATUS_SUCCESS) {
+                // TODO 安装成功 启动应用
+                PackageManagerCompat.restartApp(context);
+            } else {
+                // 失败
+                Log.e(TAG, "onReceive: 自动安装失败! ");
+            }
+        }
+    }
+
+}

+ 23 - 0
app/src/main/java/com/emato/ich/update/OnDownloadListener.java

@@ -0,0 +1,23 @@
+package com.emato.ich.update;
+
+import java.io.File;
+
+public interface OnDownloadListener {
+
+    /**
+     * 下载成功之后的文件
+     */
+    void onDownloadSuccess(File file);
+
+    /**
+     * 下载进度
+     */
+    void onDownloading(int progress);
+
+    /**
+     * 下载异常信息
+     */
+
+    void onDownloadFailed(Exception e);
+
+}

+ 16 - 0
app/src/main/java/com/emato/ich/update/OnInstallListener.java

@@ -0,0 +1,16 @@
+package com.emato.ich.update;
+
+public interface OnInstallListener {
+
+    /**
+     * 安装异常回调
+     * @param e     异常
+     */
+    public void onInstallException(Exception e);
+
+    /**
+     * 安装成功回调
+     */
+    public void onInstallSuccess();
+
+}

+ 162 - 0
app/src/main/java/com/emato/ich/update/PackageManagerCompat.java

@@ -0,0 +1,162 @@
+package com.emato.ich.update;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import java.io.BufferedReader;
+import java.io.Closeable;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class PackageManagerCompat {
+
+    private static final String TAG = PackageManagerCompat.class.getName();
+
+    /*public static void install(Activity activity, File apkFile, OnInstallListener listener){
+        try{
+            Intent intent =new Intent();
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            //Uri uri = Uri.fromFile(apkFile);
+            Uri uri = null;
+            //todo N FileProvider
+            //todo O install permission
+            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
+                uri = androidx.core.content.FileProvider.getUriForFile(activity,activity.getPackageName()+".fileprovider", apkFile);
+                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+            }else{
+                uri = Uri.fromFile(apkFile);
+            }
+            intent.setDataAndType(uri, "application/vnd.android.package-archive");
+            activity.startActivity(intent);
+            listener.onInstallSuccess();
+        } catch (Exception e) {
+            listener.onInstallException(e);
+        }
+    }*/
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    public static void install(Context context, String apkFilePath, PackageManager packageManager, OnInstallListener listener) {
+        try {
+            Log.i(TAG, "install: 应用程序开始安装! " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+            File apkFile = new File(apkFilePath);
+            PackageInstaller packageInstaller = packageManager.getPackageInstaller();
+            PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+            sessionParams.setSize(apkFile.length());
+
+            int sessionId = createSession(packageInstaller, sessionParams);
+            if (sessionId != -1) {
+//                boolean copySuccess = copyInstallFile(packageInstaller, sessionId, apkFilePath, listener);
+//                if (copySuccess) {
+                    execInstallCommand(context, packageInstaller, sessionId, listener);
+//                }
+            }
+        } catch (Exception e) {
+            listener.onInstallException(e);
+        }
+        Log.i(TAG, "install: 应用程序安装完成! " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    private static int createSession(PackageInstaller packageInstaller,
+                                     PackageInstaller.SessionParams sessionParams) {
+        int sessionId = -1;
+        try {
+            sessionId = packageInstaller.createSession(sessionParams);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return sessionId;
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    private static boolean copyInstallFile(PackageInstaller packageInstaller,
+                                           int sessionId, String apkFilePath, OnInstallListener listener) {
+        InputStream in = null;
+        OutputStream out = null;
+        PackageInstaller.Session session = null;
+        boolean success = false;
+        try {
+            File apkFile = new File(apkFilePath);
+            session = packageInstaller.openSession(sessionId);
+            out = session.openWrite("base.apk", 0, apkFile.length());
+            in = new FileInputStream(apkFile);
+            int total = 0, c;
+            byte[] buffer = new byte[65536];
+            while ((c = in.read(buffer)) != -1) {
+                total += c;
+                out.write(buffer, 0, c);
+            }
+            session.fsync(out);
+            success = true;
+        } catch (IOException e) {
+            listener.onInstallException(e);
+        } finally {
+            closeQuietly(out, listener);
+            closeQuietly(in, listener);
+            closeQuietly(session, listener);
+        }
+        return success;
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+    private static void execInstallCommand(Context context, PackageInstaller packageInstaller, int sessionId, OnInstallListener listener) {
+        PackageInstaller.Session session = null;
+        try {
+            session = packageInstaller.openSession(sessionId);
+            Intent intent = new Intent(context, InstallResultReceiver.class);
+            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+            session.commit(pendingIntent.getIntentSender());
+            listener.onInstallSuccess();
+        } catch (IOException e) {
+            listener.onInstallException(e);
+        } finally {
+            closeQuietly(session, listener);
+        }
+    }
+    private static void closeQuietly(Closeable c, OnInstallListener listener) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException ignored) {
+                listener.onInstallException(ignored);
+            }
+        }
+    }
+    /**
+     * 重启
+     * @param context   应用上下文
+     */
+    public static void restartApp(Context context) {
+        Log.i(TAG, "restartApp: 应用程序重启中! 日期=====>" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+        Intent intent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
+        PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
+        AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {// 6.0及以上
+            mgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 10000, restartIntent);
+
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {// 4.4及以上
+            mgr.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 10000, restartIntent);
+        }
+        Log.i(TAG, "restartApp: 应用程序重启完成! 日期=====>" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
+    }
+}

+ 31 - 0
app/src/main/java/com/emato/ich/utils/BaseUtils.java

@@ -231,4 +231,35 @@ public class BaseUtils {
         }
         return "";
     }
+
+
+    public static String getIp(){
+
+        Runtime runtime = Runtime.getRuntime();
+        String command = "netcfg";
+        Process proc = null;        //这句话就是shell与高级语言间的调用
+        try {
+            proc = runtime.exec(command);
+
+            // 如果有参数的话可以用另外一个被重载的exec方法
+            // 实际上这样执行时启动了一个子进程,它没有父进程的控制台
+            // 也就看不到输出,所以我们需要用输出流来得到shell执行后的输出
+            InputStream inputstream = proc.getInputStream();
+            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
+            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
+            // read the ls output
+            String line = "";
+            StringBuilder sb = new StringBuilder(line);
+            while ((line = bufferedreader.readLine()) != null) {
+                //System.out.println(line);
+                sb.append(line);
+            }
+            return sb.toString();
+        } catch (IOException e) {
+            return "";
+        }
+
+    }
+
+
 }

+ 0 - 0
app/src/main/res/drawable/e_mp_qrcode_8x8.jpg → app/src/main/res/drawable/e_mp_qrcode_8x8_backup.jpg


+ 6 - 3
app/src/main/res/layout/fragment_input_info.xml

@@ -22,7 +22,8 @@
         android:layout_x="52dp"
         android:layout_y="1164dp"
         android:ems="10"
-        android:textSize="40dp"
+        android:longClickable="false"
+        android:textSize="30dp"
         android:hint="@string/input_express_number"
         android:inputType="none" />
 
@@ -141,7 +142,8 @@
         android:layout_x="52dp"
         android:layout_y="1264dp"
         android:ems="10"
-        android:textSize="40dp"
+        android:longClickable="false"
+        android:textSize="30dp"
         android:hint="@string/input_recipients_phone"
         android:inputType="phone" />
 
@@ -151,7 +153,8 @@
         android:layout_height="80dp"
         android:layout_x="52dp"
         android:layout_y="1364dp"
-        android:textSize="40dp"
+        android:longClickable="false"
+        android:textSize="30dp"
         android:ems="10"
         android:hint="@string/input_recipients_phone_confirm"
         android:inputType="phone" />

+ 1 - 1
app/src/main/res/layout/fragment_main.xml

@@ -46,7 +46,7 @@
         android:layout_height="460dp"
         android:layout_x="573dp"
         android:layout_y="1055dp"
-        android:background="@drawable/e_mp_qrcode_8x8" />
+        android:background="@drawable/e_mp_qrcode_8x8_backup" />
 
 
 </AbsoluteLayout>

+ 4 - 2
app/src/main/res/layout/fragment_send.xml

@@ -26,7 +26,8 @@
         android:layout_height="80dp"
         android:layout_x="37dp"
         android:layout_y="1102dp"
-        android:textSize="40dp"
+        android:textSize="30dp"
+        android:longClickable="false"
         android:ems="15"
         android:hint="@string/input_send_phone"
         android:inputType="number" />
@@ -144,7 +145,8 @@
         android:layout_width="300dp"
         android:layout_height="80dp"
         android:layout_x="37dp"
-        android:textSize="40dp"
+        android:textSize="30dp"
+        android:longClickable="false"
         android:layout_y="1303dp"
         android:ems="15"
         android:hint="@string/input_send_password"

+ 1 - 1
app/src/main/res/layout/fragment_take.xml

@@ -61,6 +61,6 @@
         android:layout_height="460dp"
         android:layout_x="53dp"
         android:layout_y="1057dp"
-        android:background="@drawable/e_mp_qrcode_8x8" />
+        android:background="@drawable/e_mp_qrcode_8x8_backup" />
 
 </AbsoluteLayout>

+ 3 - 2
app/src/main/res/layout/fragment_take_code.xml

@@ -32,7 +32,7 @@
         android:layout_height="460dp"
         android:layout_x="48dp"
         android:layout_y="1029dp"
-        android:background="@drawable/e_mp_qrcode_8x8" />
+        android:background="@drawable/e_mp_qrcode_8x8_backup" />
 
     <Button
         android:id="@+id/take_input_button_cancel"
@@ -131,7 +131,8 @@
         android:layout_x="551dp"
         android:layout_y="893dp"
         android:ems="15"
-        android:textSize="40dp"
+        android:longClickable="false"
+        android:textSize="30dp"
         android:hint="@string/input_take_code"
         android:inputType="phone" />