Procházet zdrojové kódy

1.app在线升级功能 2.紧急呼叫功能

weizhengliang před 3 roky
rodič
revize
8b58121fe5

+ 63 - 1
callingbed/src/main/java/com/wdkl/app/ncs/callingbed/activity/AppUpdateActivity.kt

@@ -1,19 +1,26 @@
 package com.wdkl.app.ncs.callingbed.activity
 
+import android.text.TextUtils
+import android.util.Log
 import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
 import com.wdkl.app.ncs.callingbed.R
 import com.wdkl.app.ncs.callingbed.databinding.UpdateLayBinding
 import com.wdkl.app.ncs.callingbed.helper.AppUpdateHelper
+import com.wdkl.app.ncs.callingbed.helper.HttpHelper
 import com.wdkl.app.ncs.callingbed.launch.CallingbedLaunch
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.api.UrlManager
+import com.wdkl.ncs.android.middleware.common.Constant
 import com.wdkl.ncs.android.middleware.logic.contract.callingbed.AppUpdateContract
 import com.wdkl.ncs.android.middleware.logic.presenter.callingbed.AppUpdatePresenter
+import kotlinx.android.synthetic.main.update_lay.*
 
 @Router(path = "/callingbed/update")
 class AppUpdateActivity :BaseActivity<AppUpdatePresenter, UpdateLayBinding>(), AppUpdateContract.View {
+    private val TAG = "AppUpdateActivity"
 
     override fun getLayId(): Int {
         return R.layout.update_lay
@@ -24,7 +31,62 @@ class AppUpdateActivity :BaseActivity<AppUpdatePresenter, UpdateLayBinding>(), A
     }
 
     override fun init() {
-        //
+        if (!TextUtils.isEmpty(Constant.APP_PATH)) {
+            downLoadAPK(UrlManager.build().base + "/" + Constant.APP_PATH)
+        } else {
+            showMessage("下载路径异常")
+            finish()
+        }
+    }
+
+    /**
+     * 下载APK包
+     */
+    fun downLoadAPK(url: String) {
+        Log.d(TAG, "downLoadAPK  url==$url")
+        activity_appupdate_dialog_progressview.setCurProgress(0)
+        HttpHelper.download(url, object : HttpHelper.DownloadListener {
+            override fun onDownloadSuccess() {
+                Log.d("download", "onDownloadSuccess==" + "成功")
+                runOnUiThread {
+                    activity_calling_bed_text_download.text = "正在安装,请稍后..."
+                }
+                startInstallApk()
+            }
+
+            override fun onDownloading(progress: Int) {
+                runOnUiThread {
+                    activity_appupdate_dialog_progressview.setCurProgress(progress)
+                }
+            }
+
+            override fun onDownloadFailed() {
+                Log.d("download", "onDownloadFailed==" + "失败")
+                finish()
+            }
+        })
+    }
+
+    fun startInstallApk() {
+        Thread{
+            AppUpdateHelper.updateApp(this, object : AppUpdateHelper.UpdateCallBack {
+                override fun onFailed() {
+                    runOnUiThread {
+                        showMessage("升级失败")
+                        finish()
+                    }
+                }
+
+                override fun onSuccess() {
+                    runOnUiThread {
+                        showMessage("升级成功")
+                        //finish()
+                        android.os.Process.killProcess(android.os.Process.myPid())
+                        System.exit(0)
+                    }
+                }
+            })
+        }.start()
     }
 
     override fun bindEvent() {

+ 58 - 2
callingbed/src/main/java/com/wdkl/app/ncs/callingbed/activity/CallingbedActivity.kt

@@ -15,6 +15,7 @@ import android.view.animation.AnimationUtils
 import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
 import com.google.gson.Gson
+import com.wdkl.app.ncs.callingbed.BuildConfig
 //import com.wdkl.app.ncs.callingbed.sip.SipHelper
 //import com.wdkl.app.ncs.callingbed.sip.SipStatus
 //import com.vvsip.ansip.IVvsipServiceListener
@@ -34,7 +35,9 @@ import com.wdkl.core.socket.IUserState
 import com.wdkl.core.socket.SocketManager
 //import com.wdkl.app.ncs.sip.service.SipService
 import com.wdkl.ncs.android.lib.base.BaseActivity
+import com.wdkl.ncs.android.lib.utils.AppTool
 import com.wdkl.ncs.android.lib.utils.TimeHandle
+import com.wdkl.ncs.android.lib.utils.push
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.lib.vo.filter
 import com.wdkl.ncs.android.lib.widget.MenuDialog
@@ -43,6 +46,8 @@ import com.wdkl.ncs.android.middleware.common.MessageEvent
 import com.wdkl.ncs.android.middleware.common.SipStatus
 import com.wdkl.ncs.android.middleware.logic.contract.callingbed.CallingbedActivityContract
 import com.wdkl.ncs.android.middleware.logic.presenter.callingbed.CallingbedActivityPresenter
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.model.dos.DeviceDO
 import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
 import com.wdkl.ncs.android.middleware.model.dto.NurseConfigDto
 import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
@@ -88,12 +93,14 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
     private var configList = ArrayList<NurseConfigDto>()
     private var sipServiceRunning :Boolean = false
     private var initialized :Boolean = false
+    private var updating :Boolean = false
 
     //通话界面fragment
     private var skyCallFragment: Fragment? = null
 
     //网络异常计数
     private var netErrCount : Int = 0
+    private var clickSosTime : Long = 0
 
     //主信息
     private val mainFragment = "main_fragment"
@@ -275,6 +282,11 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
             return
         }
 
+        //加载分机所绑定紧急按钮设备信息
+        if (deviceInfo.frameId != null) {
+            presenter.getEmergencyDeviceInfo(deviceInfo.frameId)
+        }
+
         initialized = true
         presenter.loadPartSettings(Constant.PART_ID)
 
@@ -483,6 +495,15 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
         }
     }*/
 
+    fun checkAppVersion() {
+        Constant.APP_PATH = ""
+        //获取APP版本信息,5寸分机type=4
+        if (Constant.PART_ID != null) {
+            presenter.getAppVersion(Constant.PART_ID, 4)
+        }
+        Log.d("AppUpdate", "checkAppVersion =====>>  Constant.PART_ID: " + Constant.PART_ID)
+    }
+
     private fun updateSettings(forceSet: Boolean) {
         val  currentTimestamp = System.currentTimeMillis()
 
@@ -537,6 +558,28 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
         }
     }
 
+    override fun loadAppVersion(appInfo: AppVersionDO) {
+        Log.d("AppUpdate", "loadAppVersion111 =====>>  version_code: " + appInfo.versionCode + ", path: " + appInfo.appPath)
+        val newAppVersion = appInfo.versionCode.substring(1)
+        Log.d("AppUpdate", "loadAppVersion222 =====>>  newAppVersion: " + newAppVersion + ", curAppVersion: " + BuildConfig.VERSION_NAME)
+        showMessage("获取版本成功,当前版本: " + BuildConfig.VERSION_NAME + "_" + BuildConfig.VERSION_CODE + ", 服务器版本: " + newAppVersion + "_" + appInfo.versionNo)
+
+        //服务器版本和当前版本不一致才做升级操作
+        if (BuildConfig.VERSION_CODE != appInfo.versionNo && !updating) {
+            Constant.APP_PATH = appInfo.appPath
+            updating = true
+            AppTool.Time.delay(200) {
+                push("/callingbed/update")
+            }
+        }
+    }
+
+    override fun showEmergencyInfo(deviceInfo: DeviceDO) {
+        if (deviceInfo.id != null) {
+            Constant.EMERGENCY_ID = deviceInfo.id
+        }
+    }
+
     override fun serialPortBedOnclick(buffer: ByteArray) {
         Log.d("serialPortBedOnclick", "buffer[0]:" + buffer[0] + ", buffer[1]:" + buffer[1] + ", buffer[2]:" + buffer[2]
                 + ", buffer[3]:" + buffer[3] + ", buffer[4]:" + buffer[4] + ", buffer[5]:" + buffer[5] + ", buffer[6]:" + buffer[6]
@@ -597,6 +640,19 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
                 clickCall()
             }
         }
+
+        //紧急呼叫
+        else if (buffer[7].toInt() == 1 || buffer[7].toInt() == 2) {
+            //卫生间紧急按钮: 一定时间内重复多次按无效
+            if (System.currentTimeMillis() - clickSosTime > 10000) {
+                runOnUiThread {
+                    clickSosTime = System.currentTimeMillis()
+                    if (Constant.DEVICE_STATUS == 1 && Constant.TCP_CONNECTED) {
+                        SOSHelper.sosStart()
+                    }
+                }
+            }
+        }
     }
 
     override fun serialPortBedOnclickString(str: String) {
@@ -864,12 +920,12 @@ class CallingbedActivity :BaseActivity<CallingbedActivityPresenter, CallingbedMa
                     } else if (tcpModel.type == TcpType.SOS) {
                         if (tcpModel.action == TcpAction.SOSAction.CANCEL) {
                             //紧急呼叫已处理
-                            //SOSHelper.sosStop()
+                            SOSHelper.sosStop()
                         }
                     } else if (tcpModel.type == TcpType.DEVICE) {
                         //检查APP版本
                         if (tcpModel.getAction() == TcpAction.DeviceAction.APP_UPDATE) {
-                            //checkAppVersion()
+                            checkAppVersion()
                         } else if (tcpModel.action == TcpAction.DeviceAction.RESTART) {
                             //收到重启app指令,若当前处于正常待机状态则直接重启app,否则等待通话结束再重启
                             if (Constant.CALL_STATE == Constant.CALL_STANDBY) {

+ 60 - 0
callingbed/src/main/java/com/wdkl/app/ncs/callingbed/helper/AppUpdateHelper.java

@@ -31,6 +31,39 @@ public class AppUpdateHelper {
      */
     public static final String FILE_APK_NAME = "CallingBedAPK.apk";
 
+    public static void updateApp(Context context, UpdateCallBack callBack) {
+        if (checkApkExit(context)) {
+            Log.d(TAG, "文件存在");
+        } else {
+            Log.d(TAG, "文件不存在");
+            if (callBack != null) {
+                callBack.onFailed();
+            }
+            return;
+        }
+
+        String path = FILE_APK_PATH + "/" + FILE_APK_NAME;
+        //if (installApp(context.getPackageName(), path)) {
+        if (rootSilenceInstall(path)) {
+            Log.d(TAG, "安装成功");
+            if (callBack != null) {
+                callBack.onSuccess();
+            }
+        } else {
+            Log.d(TAG, "安装失败");
+            if (callBack != null) {
+                callBack.onFailed();
+            }
+        }
+
+        /*if (silentInstall(context, path)) {
+            Log.d(TAG, "app 安装成功");
+            if (callBack != null) {
+                callBack.onSuccess();
+            }
+        }*/
+    }
+
     public static void updateApp(Context context) {
         if (checkApkExit(context)) {
             Log.d(TAG, "文件存在");
@@ -97,6 +130,28 @@ public class AppUpdateHelper {
         return false;
     }
 
+    public static boolean rootSilenceInstall(String path) {
+        Process process;
+        PrintWriter printWriter;
+        try {
+            process = Runtime.getRuntime().exec("su");
+            printWriter = new PrintWriter(process.getOutputStream());
+            printWriter.println("pm install -r " + path);
+            printWriter.flush();
+            printWriter.close();
+            int res = process.waitFor();
+            Log.e(TAG, "silent install res: " + res);
+            if (res == 0) {
+                return true;
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "rootSilenceInstall e:" + e.getMessage());
+            return false;
+        }
+    }
+
 
     public static void reboot(Context context) {
         try {
@@ -125,4 +180,9 @@ public class AppUpdateHelper {
         System.exit(0);
     }
 
+
+    public interface UpdateCallBack {
+        void onFailed();
+        void onSuccess();
+    }
 }

+ 160 - 0
callingbed/src/main/java/com/wdkl/app/ncs/callingbed/helper/HttpHelper.java

@@ -0,0 +1,160 @@
+package com.wdkl.app.ncs.callingbed.helper;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+import static com.wdkl.app.ncs.callingbed.helper.AppUpdateHelper.FILE_APK_NAME;
+import static com.wdkl.app.ncs.callingbed.helper.AppUpdateHelper.FILE_APK_PATH;
+
+public class HttpHelper {
+
+    /**
+     * @param url   服务器地址
+     * @param file  所要上传的文件
+     */
+    public static void upload(String url, File file, final UploadCallback callback) {
+        OkHttpClient client = new OkHttpClient();
+        RequestBody requestBody = new MultipartBody.Builder()
+                .setType(MultipartBody.FORM)
+                .addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("multipart/form-data"), file))
+                .build();
+        Request request = new Request.Builder()
+                .url(url)
+                .post(requestBody)
+                .build();
+
+        client.newCall(request).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                if (callback != null) {
+                    callback.onFail();
+                }
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws IOException {
+                Log.d("wzlll", "voice msg response: " + response.toString());
+               if( response.code()==200 && response.body() != null) {
+                   String data = response.body().string();
+                   //voice msg response: upload/file/202104102037715.mp3
+                   if (callback != null) {
+                       callback.onSuccess(data);
+                   }
+               } else {
+                   if (callback != null) {
+                       callback.onFail();
+                   }
+               }
+            }
+        });
+    }
+
+    public static void download(String url, final DownloadListener listener) {
+        Request request = new Request.Builder().url(url).build();
+        OkHttpClient okHttpClient = new OkHttpClient();
+        okHttpClient.newCall(request).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                Log.d("download", "onFailure==" + e.toString());
+                if (listener != null) {
+                    listener.onDownloadFailed(); // 下载失败
+                }
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) {
+                Log.d("download", "response==" + response.body().contentLength());
+                InputStream is = null;
+                byte[] buf = new byte[2048];
+                int len;
+                FileOutputStream fos = null;
+                try {
+                    is = response.body().byteStream();
+                    long total = response.body().contentLength();
+                    File file = new File(isHaveExistDir(new File(FILE_APK_PATH), new File(FILE_APK_PATH + "/" + FILE_APK_NAME)), FILE_APK_NAME);
+                    fos = new FileOutputStream(file);
+                    long sum = 0;
+                    while ((len = is.read(buf)) != -1) {
+                        fos.write(buf, 0, len);
+                        sum = sum + (long) len;
+                        //int progress = (int) (sum * 1.0f / total * 100);
+                        float sp = (float) sum / (float) total;
+                        int progress = (int) (sp * 100);
+                        Log.d("download", "progress==" + progress);
+                        if (listener != null) {
+                            listener.onDownloading(progress);// 下载中
+                        }
+                    }
+                    fos.flush();
+                    if (listener != null) {
+                        listener.onDownloadSuccess(); // 下载完成
+                    }
+                } catch (Exception e) {
+                    Log.d("download", "Exception==");
+                    if (listener != null) {
+                        listener.onDownloadFailed();
+                    }
+                } finally {
+                    try {
+                        if (is != null)
+                            is.close();
+                        if (fos != null)
+                            fos.close();
+                    } catch (IOException e) {
+                        Log.d("download", "IOException==");
+                    }
+                }
+            }
+        });
+    }
+
+    private static String isHaveExistDir(File downloadFile, File sonFile) throws IOException {
+        Log.d("download", "downloadFile.mkdirs()==" + downloadFile.mkdirs());
+        Log.d("download", "sonFile.mkdir()==" + sonFile.mkdir());
+        if (!downloadFile.mkdirs()) {
+            downloadFile.createNewFile();
+        }
+        deleteAPKFile(sonFile);//只要文件名相同就可以自动替换(按道理此处不需要了,但为了保险起见还是先执行删除操作)。
+        return downloadFile.getAbsolutePath();
+    }
+
+    public static boolean deleteAPKFile(File downloadFile) {
+        return downloadFile.delete();
+    }
+
+
+    public interface UploadCallback{
+        void onFail();
+        void onSuccess(String data);
+    }
+
+    public interface DownloadListener {
+        /**
+         * 下载成功
+         */
+        void onDownloadSuccess();
+
+        /**
+         * @param progress 下载进度
+         */
+        void onDownloading(int progress);
+
+        /**
+         * 下载失败
+         */
+        void onDownloadFailed();
+    }
+}

+ 40 - 0
callingbed/src/main/java/com/wdkl/app/ncs/callingbed/helper/SOSHelper.java

@@ -0,0 +1,40 @@
+package com.wdkl.app.ncs.callingbed.helper;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.wdkl.ncs.android.middleware.common.Constant;
+import com.wdkl.ncs.android.middleware.tcp.channel.OtherUtil;
+
+
+/*
+ * 紧急按钮辅助类
+ */
+public class SOSHelper {
+
+    private final static Handler handler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            sosStop();
+        }
+    };
+
+    public static void sosStart() {
+        SerialPortHelper.setSosLight("2");
+        //如果该床位绑定了紧急按钮则直接通过紧急按钮设备id发送sos请求
+        if (Constant.EMERGENCY_ID != -1) {
+            OtherUtil.sendSosCall(Constant.EMERGENCY_ID);
+        } else {
+            OtherUtil.sendSosCall(Constant.DEVICE_ID);
+        }
+
+        //60s之后紧急按钮灯自动复位
+        handler.sendEmptyMessageDelayed(110, 120000);
+    }
+
+    public static void sosStop() {
+        handler.removeCallbacksAndMessages(null);
+        SerialPortHelper.setSosLight("0");
+    }
+}

+ 8 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/api/CallingbedApi.kt

@@ -36,4 +36,12 @@ interface CallingbedApi {
     //tcp服务器地址
     @GET("/ncs_url/getHostIP")
     fun getTcpServerHost(): Observable<ResponseBody>
+
+    //获取APP版本信息
+    @POST("/deviceBed/getAppVersion")
+    fun getAppVersion(@Query("part_id") part_id:Int, @Query("device_type") device_type:Int): Observable<ResponseBody>
+
+    //获取紧急按钮设备信息
+    @GET("/deviceBed/getEmergencyButtonByFrameId/{frame_id}")
+    fun getEmergencyInfo(@Path("frame_id") frame_id:Int): Observable<ResponseBody>
 }

+ 12 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/contract/callingbed/CallingbedActivityContract.kt

@@ -1,6 +1,8 @@
 package com.wdkl.ncs.android.middleware.logic.contract.callingbed
 
 import com.wdkl.ncs.android.lib.base.BaseContract
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.model.dos.DeviceDO
 import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
 import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
 import com.wdkl.ncs.android.middleware.model.vo.BedDeviceInfoVO
@@ -17,6 +19,10 @@ interface CallingbedActivityContract {
 
         fun setTcpServerHost(tcpSeverDTO: TcpSeverDTO)
 
+        fun loadAppVersion(appInfo: AppVersionDO)
+
+        fun showEmergencyInfo(deviceInfo: DeviceDO)
+
         fun onNoNet()
     }
 
@@ -30,5 +36,11 @@ interface CallingbedActivityContract {
 
         //获取tcp服务器地址
         fun loadTcpServerHost()
+
+        //获取APP版本信息
+        fun getAppVersion(partId: Int, deviceType: Int)
+
+        //获取紧急按钮设备信息
+        fun getEmergencyDeviceInfo(frameId: Int)
     }
 }

+ 37 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/presenter/callingbed/CallingbedActivityPresenter.kt

@@ -11,6 +11,8 @@ import com.wdkl.ncs.android.lib.utils.getJsonString
 import com.wdkl.ncs.android.middleware.api.CallingbedApi
 import com.wdkl.ncs.android.middleware.di.MiddlewareDaggerComponent
 import com.wdkl.ncs.android.middleware.logic.contract.callingbed.CallingbedActivityContract
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.model.dos.DeviceDO
 import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
 import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
 import com.wdkl.ncs.android.middleware.model.vo.BedDeviceInfoVO
@@ -51,6 +53,17 @@ class CallingbedActivityPresenter @Inject constructor() :RxPresenter<CallingbedA
                     providerView().complete("")
                     providerView().setTcpServerHost(result)
                 }
+
+                is AppVersionDO -> {
+                    //app版本信息
+                    providerView().complete("")
+                    providerView().loadAppVersion(result)
+                }
+
+                is DeviceDO -> {
+                    providerView().complete("")
+                    providerView().showEmergencyInfo(result)
+                }
             }
         }
 
@@ -103,4 +116,28 @@ class CallingbedActivityPresenter @Inject constructor() :RxPresenter<CallingbedA
                 .compose(ThreadFromUtils.defaultSchedulers())
                 .subscribe(observer)
     }
+
+    override fun getAppVersion(partId: Int, deviceType: Int) {
+        bedDeviceApi.getAppVersion(partId, deviceType)
+            .map {
+                val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                val appVersion = gson.fromJson(it.getJsonString(), AppVersionDO::class.java)
+
+                return@map appVersion
+            }
+            .compose(ThreadFromUtils.defaultSchedulers())
+            .subscribe(observer)
+    }
+
+    override fun getEmergencyDeviceInfo(frameId: Int) {
+        bedDeviceApi.getEmergencyInfo(frameId)
+            .map {
+                val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                val info = gson.fromJson(it.getJsonString(), DeviceDO::class.java)
+
+                return@map info
+            }
+            .compose(ThreadFromUtils.defaultSchedulers())
+            .subscribe(observer)
+    }
 }