瀏覽代碼

<增加APP在线升级功能>

weizhengliang 4 年之前
父節點
當前提交
021b958693
共有 35 個文件被更改,包括 1391 次插入27 次删除
  1. 139 0
      app/keystore_wuyuqing_3128/keytool-importkeypair
  2. 1 0
      app/keystore_wuyuqing_3128/password.txt
  3. 二進制
      app/keystore_wuyuqing_3128/platform.pk8
  4. 24 0
      app/keystore_wuyuqing_3128/platform.x509.pem
  5. 二進制
      app/keystore_wuyuqing_3128/rk3128.jks
  6. 7 0
      app/keystore_wuyuqing_3128/rk3128_generate_key.sh
  7. 139 0
      app/keystore_wuyuqing_3128/rk3128_signapk/keytool-importkeypair
  8. 二進制
      app/keystore_wuyuqing_3128/rk3128_signapk/platform.pk8
  9. 24 0
      app/keystore_wuyuqing_3128/rk3128_signapk/platform.x509.pem
  10. 二進制
      app/keystore_wuyuqing_3128/rk3128_signapk/rk3128.jks
  11. 7 0
      app/keystore_wuyuqing_3128/rk3128_signapk/rk3128_generate_key.sh
  12. 二進制
      app/keystore_wuyuqing_3128/rk3128_signapk/signapk.jar
  13. 二進制
      app/keystore_wuyuqing_3128/signapk.jar
  14. 357 0
      common/src/main/code/com/wdkl/ncs/android/lib/widget/ProgressView.kt
  15. 12 0
      common/src/main/res/values/attrs.xml
  16. 2 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/api/ApiManager.kt
  17. 6 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/api/NurseHomeApi.kt
  18. 2 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constants.kt
  19. 5 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/di/ApiProvides.kt
  20. 1 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/di/ApplicationComponent.kt
  21. 2 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/di/PresenterComponent.kt
  22. 13 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/contract/nursehome/AppUpdateContract.kt
  23. 6 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/contract/nursehome/NurseHomeActivityContract.kt
  24. 12 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/presenter/nursehome/AppUpdatePresenter.kt
  25. 22 18
      middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/presenter/nursehome/NurseHomeActivityPresenter.kt
  26. 23 8
      middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/AppVersionDO.java
  27. 4 0
      nursehome/src/main/AndroidManifest.xml
  28. 138 0
      nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/AppUpdateActivity.kt
  29. 21 0
      nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/NurseHomeActivity.kt
  30. 2 0
      nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/di/NurseHomeComponent.kt
  31. 6 0
      nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/SystemSettingsFragment.kt
  32. 220 0
      nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/AppUpdateHelper.java
  33. 160 0
      nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/HttpHelper.java
  34. 1 1
      nursehome/src/main/res/layout/fragment_system_settings.xml
  35. 35 0
      nursehome/src/main/res/layout/update_lay.xml

+ 139 - 0
app/keystore_wuyuqing_3128/keytool-importkeypair

@@ -0,0 +1,139 @@
+#! /bin/bash
+#
+# This file is part of keytool-importkeypair.
+#
+# keytool-importkeypair is free software: you can redistribute it
+# and/or modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# keytool-importkeypair is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with keytool-importkeypair.  If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+DEFAULT_KEYSTORE=$HOME/.keystore
+keystore=$DEFAULT_KEYSTORE
+pk8=""
+cert=""
+alias=""
+passphrase=""
+tmpdir=""
+
+scriptname=`basename $0`
+
+usage() {
+cat << EOF
+usage: ${scriptname} [-k keystore] [-p storepass]
+-pk8 pk8 -cert cert -alias key_alias
+
+This script is used to import a key/certificate pair
+into a Java keystore.
+
+If a keystore is not specified then the key pair is imported into
+~/.keystore in the user's home directory.
+
+The passphrase can also be read from stdin.
+EOF
+}
+
+cleanup() {
+if [ ! -z "${tmpdir}" -a -d ${tmpdir} ]; then
+   rm -fr ${tmpdir}
+fi
+}
+
+while [ $# -gt 0 ]; do
+        case $1
+        in
+                -p | --passphrase | -passphrase)
+                        passphrase=$2
+                        shift 2
+        ;;
+                -h | --help)
+                        usage
+                        exit 0
+        ;;
+                -k | -keystore | --keystore)
+                        keystore=$2
+                        shift 2
+        ;;
+                -pk8 | --pk8 | -key | --key)
+                        pk8=$2
+                        shift 2
+        ;;
+                -cert | --cert | -pem | --pem)
+                        cert=$2
+                        shift 2
+        ;;
+                -a | -alias | --alias)
+                        alias=$2
+                        shift 2
+        ;;
+                *)
+                        echo "${scriptname}: Unknown option $1, exiting" 1>&2
+                        usage
+                        exit 1
+        ;;
+        esac
+done
+
+if [ -z "${pk8}" -o -z "${cert}" -o -z "${alias}" ]; then
+   echo "${scriptname}: Missing option, exiting..." 1>&2
+   usage
+   exit 1
+fi
+
+
+for f in "${pk8}" "${cert}"; do
+    if [ ! -f "$f" ]; then
+       echo "${scriptname}: Can't find file $f, exiting..." 1>&2
+       exit 1
+    fi
+done
+
+if [ ! -f "${keystore}" ]; then
+   storedir=`dirname "${keystore}"`
+   if [ ! -d "${storedir}" -o ! -w "${storedir}" ]; then
+      echo "${scriptname}: Can't access ${storedir}, exiting..." 1>&2
+      exit 1
+   fi
+fi
+
+# Create temp directory ofr key and pkcs12 bundle
+tmpdir=`mktemp -q -d "/tmp/${scriptname}.XXXX"`
+
+if [ $? -ne 0 ]; then
+   echo "${scriptname}: Can't create temp directory, exiting..." 1>&2
+   exit 1
+fi
+
+key="${tmpdir}/key"
+p12="${tmpdir}/p12"
+
+if [ -z "${passphrase}" ]; then
+   # Request a passphrase
+  read -p "Enter a passphrase: " -s passphrase
+  echo ""
+fi
+
+# Convert PK8 to PEM KEY
+openssl pkcs8 -inform DER -nocrypt -in "${pk8}" -out "${key}"
+
+# Bundle CERT and KEY
+openssl pkcs12 -export -in "${cert}" -inkey "${key}" -out "${p12}" -password pass:"${passphrase}" -name "${alias}"
+
+# Print cert
+echo -n "Importing \"${alias}\" with "
+openssl x509 -noout -fingerprint -in "${cert}"
+
+# Import P12 in Keystore
+keytool -importkeystore -deststorepass "${passphrase}" -destkeystore "${keystore}" -srckeystore "${p12}" -srcstoretype PKCS12 -srcstorepass "${passphrase}" 
+
+# Cleanup
+cleanup

+ 1 - 0
app/keystore_wuyuqing_3128/password.txt

@@ -0,0 +1 @@
+111111

二進制
app/keystore_wuyuqing_3128/platform.pk8


+ 24 - 0
app/keystore_wuyuqing_3128/platform.x509.pem

@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID+zCCAuOgAwIBAgIJAP8GQTI8+VUSMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
+VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE
+AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0xNDEyMjMwNjQzNDFaFw00MjA1MTAwNjQzNDFaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+hvcNAQEBBQADggENADCCAQgCggEBAKHY8Fl4XZzpJvgHoezYjhmKPvdq9DGYVc/X
+9NQO5oUlYIA/Ci5rzvljz13KbNve/KxuEu9HN8SzsLg9EBhghOko8JxEg7I8W6uP
+VOoRngNCMvXzjf6av77vxPqphlgq++Y4MIC+fiOxLd+gpYq0p6W7RWxEgrzLHnWi
+CX0dRmWDs+ey2t4f1WKzGoRQQS0Tn21CViThrVEe+zNwANnhErUcvoQB2m4/PQot
+uij7LZNccHJvUOUf5/4wIZd8JOgO3VLwzFO/HhrqUjafCvkpKTjW8oQmHLUz5m40
+ljETGEjqQ6AuAwmaeFT+Bwj1DUaYg+m7AzalJ2aAtHVX0FftRUkCAQOjUDBOMB0G
+A1UdDgQWBBQi+LgbyFfWSoWCbQ+NVDF4ZKTPCjAfBgNVHSMEGDAWgBQi+LgbyFfW
+SoWCbQ+NVDF4ZKTPCjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBH
+1kIQlSBjXRMuQdaDLytr8ZaJXIN1HApg2QA8azYQXOS/B16gwm6tBfh1dL86LL/7
+w09oM9LZv8WwtQyFNjM97vvQvkaOGW/ubhrKOk3+8HTHkEx4n7H6tYGOLdpmWepD
+fBSEFuLwq6yqG6wZFdd7IKO+sPlCxqUpqg40YAb4WOwzDbiuJnswDftP3wIaaJPh
+li6OIjRKyd3Sgw1MtffHOy+WSwqHLkGNgH6GAgvZlvhPA/yim+rjnE9oKV5G6Pyg
+QK7kJJjS/LdeqxE7M7pNRYPhcLT7qhE7MiuBuyqwAMTTBoU8u3lTdOZwNErbRT5t
+SXkgVMffkfN7wBNqpSSY
+-----END CERTIFICATE-----

二進制
app/keystore_wuyuqing_3128/rk3128.jks


+ 7 - 0
app/keystore_wuyuqing_3128/rk3128_generate_key.sh

@@ -0,0 +1,7 @@
+#!/bin/sh
+# 转换系统签名命令
+./keytool-importkeypair -k rk3128.jks -p 111111 -pk8 platform.pk8 -cert platform.x509.pem -alias rk3128
+# rk3128.jks : 签名文件
+# 111111 : 签名文件密码
+# platform.pk8、platform.x509.pem : 系统签名文件
+# rk3128 : 签名文件别名

+ 139 - 0
app/keystore_wuyuqing_3128/rk3128_signapk/keytool-importkeypair

@@ -0,0 +1,139 @@
+#! /bin/bash
+#
+# This file is part of keytool-importkeypair.
+#
+# keytool-importkeypair is free software: you can redistribute it
+# and/or modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# keytool-importkeypair is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with keytool-importkeypair.  If not, see
+# <http://www.gnu.org/licenses/>.
+#
+
+DEFAULT_KEYSTORE=$HOME/.keystore
+keystore=$DEFAULT_KEYSTORE
+pk8=""
+cert=""
+alias=""
+passphrase=""
+tmpdir=""
+
+scriptname=`basename $0`
+
+usage() {
+cat << EOF
+usage: ${scriptname} [-k keystore] [-p storepass]
+-pk8 pk8 -cert cert -alias key_alias
+
+This script is used to import a key/certificate pair
+into a Java keystore.
+
+If a keystore is not specified then the key pair is imported into
+~/.keystore in the user's home directory.
+
+The passphrase can also be read from stdin.
+EOF
+}
+
+cleanup() {
+if [ ! -z "${tmpdir}" -a -d ${tmpdir} ]; then
+   rm -fr ${tmpdir}
+fi
+}
+
+while [ $# -gt 0 ]; do
+        case $1
+        in
+                -p | --passphrase | -passphrase)
+                        passphrase=$2
+                        shift 2
+        ;;
+                -h | --help)
+                        usage
+                        exit 0
+        ;;
+                -k | -keystore | --keystore)
+                        keystore=$2
+                        shift 2
+        ;;
+                -pk8 | --pk8 | -key | --key)
+                        pk8=$2
+                        shift 2
+        ;;
+                -cert | --cert | -pem | --pem)
+                        cert=$2
+                        shift 2
+        ;;
+                -a | -alias | --alias)
+                        alias=$2
+                        shift 2
+        ;;
+                *)
+                        echo "${scriptname}: Unknown option $1, exiting" 1>&2
+                        usage
+                        exit 1
+        ;;
+        esac
+done
+
+if [ -z "${pk8}" -o -z "${cert}" -o -z "${alias}" ]; then
+   echo "${scriptname}: Missing option, exiting..." 1>&2
+   usage
+   exit 1
+fi
+
+
+for f in "${pk8}" "${cert}"; do
+    if [ ! -f "$f" ]; then
+       echo "${scriptname}: Can't find file $f, exiting..." 1>&2
+       exit 1
+    fi
+done
+
+if [ ! -f "${keystore}" ]; then
+   storedir=`dirname "${keystore}"`
+   if [ ! -d "${storedir}" -o ! -w "${storedir}" ]; then
+      echo "${scriptname}: Can't access ${storedir}, exiting..." 1>&2
+      exit 1
+   fi
+fi
+
+# Create temp directory ofr key and pkcs12 bundle
+tmpdir=`mktemp -q -d "/tmp/${scriptname}.XXXX"`
+
+if [ $? -ne 0 ]; then
+   echo "${scriptname}: Can't create temp directory, exiting..." 1>&2
+   exit 1
+fi
+
+key="${tmpdir}/key"
+p12="${tmpdir}/p12"
+
+if [ -z "${passphrase}" ]; then
+   # Request a passphrase
+  read -p "Enter a passphrase: " -s passphrase
+  echo ""
+fi
+
+# Convert PK8 to PEM KEY
+openssl pkcs8 -inform DER -nocrypt -in "${pk8}" -out "${key}"
+
+# Bundle CERT and KEY
+openssl pkcs12 -export -in "${cert}" -inkey "${key}" -out "${p12}" -password pass:"${passphrase}" -name "${alias}"
+
+# Print cert
+echo -n "Importing \"${alias}\" with "
+openssl x509 -noout -fingerprint -in "${cert}"
+
+# Import P12 in Keystore
+keytool -importkeystore -deststorepass "${passphrase}" -destkeystore "${keystore}" -srckeystore "${p12}" -srcstoretype PKCS12 -srcstorepass "${passphrase}" 
+
+# Cleanup
+cleanup

二進制
app/keystore_wuyuqing_3128/rk3128_signapk/platform.pk8


+ 24 - 0
app/keystore_wuyuqing_3128/rk3128_signapk/platform.x509.pem

@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID+zCCAuOgAwIBAgIJAP8GQTI8+VUSMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
+VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE
+AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0xNDEyMjMwNjQzNDFaFw00MjA1MTAwNjQzNDFaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+hvcNAQEBBQADggENADCCAQgCggEBAKHY8Fl4XZzpJvgHoezYjhmKPvdq9DGYVc/X
+9NQO5oUlYIA/Ci5rzvljz13KbNve/KxuEu9HN8SzsLg9EBhghOko8JxEg7I8W6uP
+VOoRngNCMvXzjf6av77vxPqphlgq++Y4MIC+fiOxLd+gpYq0p6W7RWxEgrzLHnWi
+CX0dRmWDs+ey2t4f1WKzGoRQQS0Tn21CViThrVEe+zNwANnhErUcvoQB2m4/PQot
+uij7LZNccHJvUOUf5/4wIZd8JOgO3VLwzFO/HhrqUjafCvkpKTjW8oQmHLUz5m40
+ljETGEjqQ6AuAwmaeFT+Bwj1DUaYg+m7AzalJ2aAtHVX0FftRUkCAQOjUDBOMB0G
+A1UdDgQWBBQi+LgbyFfWSoWCbQ+NVDF4ZKTPCjAfBgNVHSMEGDAWgBQi+LgbyFfW
+SoWCbQ+NVDF4ZKTPCjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBH
+1kIQlSBjXRMuQdaDLytr8ZaJXIN1HApg2QA8azYQXOS/B16gwm6tBfh1dL86LL/7
+w09oM9LZv8WwtQyFNjM97vvQvkaOGW/ubhrKOk3+8HTHkEx4n7H6tYGOLdpmWepD
+fBSEFuLwq6yqG6wZFdd7IKO+sPlCxqUpqg40YAb4WOwzDbiuJnswDftP3wIaaJPh
+li6OIjRKyd3Sgw1MtffHOy+WSwqHLkGNgH6GAgvZlvhPA/yim+rjnE9oKV5G6Pyg
+QK7kJJjS/LdeqxE7M7pNRYPhcLT7qhE7MiuBuyqwAMTTBoU8u3lTdOZwNErbRT5t
+SXkgVMffkfN7wBNqpSSY
+-----END CERTIFICATE-----

二進制
app/keystore_wuyuqing_3128/rk3128_signapk/rk3128.jks


+ 7 - 0
app/keystore_wuyuqing_3128/rk3128_signapk/rk3128_generate_key.sh

@@ -0,0 +1,7 @@
+#!/bin/sh
+# 转换系统签名命令
+./keytool-importkeypair -k rk3128.jks -p 111111 -pk8 platform.pk8 -cert platform.x509.pem -alias rk3128
+# rk3128.jks : 签名文件
+# 111111 : 签名文件密码
+# platform.pk8、platform.x509.pem : 系统签名文件
+# rk3128 : 签名文件别名

二進制
app/keystore_wuyuqing_3128/rk3128_signapk/signapk.jar


二進制
app/keystore_wuyuqing_3128/signapk.jar


+ 357 - 0
common/src/main/code/com/wdkl/ncs/android/lib/widget/ProgressView.kt

@@ -0,0 +1,357 @@
+package com.wdkl.ncs.android.lib.widget
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ObjectAnimator
+import android.content.Context
+import android.graphics.*
+import android.os.Looper
+import android.text.TextPaint
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+import com.wdkl.ncs.android.lib.R
+
+class ProgressView :View{
+
+    private var mPaint: Paint? = null
+    private var mTextPaint: TextPaint? = null
+
+    private var mRadius: Int
+    private var mBorderColor: Int
+    private var mProgressColor: Int = 0
+    private var mProgressDescColor: Int
+    private var mMax: Int = 0
+    private var mProgress: Int = 0
+    private var mBorderWidth: Int
+    private var mIsShowDesc: Boolean = false
+    private var mHaveChangeColor: Boolean = false
+
+    private val DEFAULT_MAX = 10
+    private val DEFAULT_PROGRESS = 0
+    var changeColor = arrayOf("#CDFDCB", "#65FDCC", "#65FDCC", "#55CA7C", "#55CA7C", "#52CD53", "#52CD53", "#52CD53", "#3D9A5C", "#3D9A5C")
+
+    private val DEFAULT_RADIUS = TypedValue.applyDimension(
+            TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
+    private val DEFAULT_BORDER_COLOR = Color.parseColor("#FD0005")
+    private val DEFAULT_PROGRESS_COLOR = Color.parseColor("#77D178")
+    private val DEFAULT_PROGRESS_DESC_COLOR = Color.parseColor("#000000")
+    private val DEFAULT_BORDER_WIDTH = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics).toInt()
+    private val DEFAULT_ISSHOWDESC = true
+
+    private var mWidth: Int = 0
+    private var mHeight: Int = 0
+    private var mTextBounds: Rect? = null
+    private var mProgressDesc: String? = ""
+
+    private var mOnFinishedListener: OnFinishedListener? = null
+    private var mOnAnimationEndListener: OnAnimationEndListener? = null
+
+    constructor(context: Context): this(context, null)
+
+    constructor(context: Context, attrs: AttributeSet?): this(context, attrs, 0)
+
+    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr) {
+        val a = context.obtainStyledAttributes(attrs, R.styleable.ProgressView)
+        mMax = a.getInt(R.styleable.ProgressView_max, DEFAULT_MAX)
+        mProgress = a.getInt(R.styleable.ProgressView_progress, DEFAULT_PROGRESS)
+        mRadius = a.getDimension(R.styleable.ProgressView_progressRadius, DEFAULT_RADIUS.toFloat()).toInt()
+        mBorderColor = a.getColor(R.styleable.ProgressView_borderColor, DEFAULT_BORDER_COLOR)
+        mProgressColor = a.getColor(R.styleable.ProgressView_progressColor, DEFAULT_PROGRESS_COLOR)
+        mProgressDescColor = a.getColor(R.styleable.ProgressView_progressDescColor, DEFAULT_PROGRESS_DESC_COLOR)
+        mBorderWidth = a.getDimension(R.styleable.ProgressView_borderWidth, DEFAULT_BORDER_WIDTH.toFloat()).toInt()
+        mProgressDesc = a.getString(R.styleable.ProgressView_progressDesc)
+        mIsShowDesc = a.getBoolean(R.styleable.ProgressView_isShowDesc, DEFAULT_ISSHOWDESC)
+        mHaveChangeColor = a.getBoolean(R.styleable.ProgressView_haveChangeColor, true)
+        a.recycle()
+        init()
+    }
+
+    private fun init() {
+        mPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+        mTextBounds = Rect()
+        mTextPaint = TextPaint(Paint.ANTI_ALIAS_FLAG)
+        mTextPaint!!.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics)
+        mTextPaint!!.color = mProgressDescColor
+    }
+
+    /**
+     * 设置结束监听
+     *
+     * @param onFinishedListener
+     */
+    fun setOnFinishedListener(onFinishedListener: OnFinishedListener) {
+        mOnFinishedListener = onFinishedListener
+    }
+
+    /**
+     * 设置进度停止监听
+     *
+     * @param onAnimationEndListener
+     */
+    fun setOnAnimationEndListener(onAnimationEndListener: OnAnimationEndListener) {
+        mOnAnimationEndListener = onAnimationEndListener
+    }
+
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(w, h, oldw, oldh)
+        mWidth = w
+        mHeight = h
+    }
+
+    override fun onDraw(canvas: Canvas) {
+        drawBorder(canvas)
+        drawProgress(canvas)
+        if (mIsShowDesc) drawProgressDesc(canvas)
+    }
+
+    private fun drawBorder(canvas: Canvas) {
+        mPaint!!.reset()
+        mPaint!!.style = Paint.Style.STROKE
+        mPaint!!.strokeJoin = Paint.Join.MITER
+        mPaint!!.isAntiAlias = true
+        mPaint!!.color = mBorderColor
+        mPaint!!.strokeWidth = mBorderWidth.toFloat()
+
+        val left = mBorderWidth / 2
+        val top = mBorderWidth / 2
+        val right = mWidth - mBorderWidth / 2
+        val bottom = mHeight - mBorderWidth / 2
+
+        val path = Path()
+        path.moveTo((left + mRadius).toFloat(), top.toFloat())
+        path.lineTo((right - mRadius).toFloat(), top.toFloat())
+        path.arcTo(RectF((right - 2 * mRadius).toFloat(), top.toFloat(), right.toFloat(), (top + 2 * mRadius).toFloat()), -90f, 90f)
+        path.lineTo(right.toFloat(), (bottom - mRadius).toFloat())
+        path.arcTo(RectF((right - 2 * mRadius).toFloat(), (bottom - 2 * mRadius).toFloat(), right.toFloat(), bottom.toFloat()), 0f, 90f)
+        path.lineTo((left + mRadius).toFloat(), bottom.toFloat())
+        path.arcTo(RectF(left.toFloat(), (bottom - 2 * mRadius).toFloat(), (left + 2 * mRadius).toFloat(), bottom.toFloat()), 90f, 90f)
+        path.lineTo(left.toFloat(), (top + mRadius).toFloat())
+        path.arcTo(RectF(left.toFloat(), top.toFloat(), (left + 2 * mRadius).toFloat(), (top + 2 * mRadius).toFloat()), 180f, 90f)
+        path.close()
+        canvas.drawPath(path, mPaint!!)
+    }
+
+    private fun drawProgress(canvas: Canvas) {
+
+        mPaint!!.reset()
+        mPaint!!.style = Paint.Style.FILL
+        mPaint!!.isAntiAlias = true
+        mPaint!!.color = mProgressColor
+        mPaint!!.strokeWidth = mBorderWidth.toFloat()
+
+        val left = mBorderWidth * .5f
+        val top = mBorderWidth * .5f
+        val right = mWidth - mBorderWidth * .5f
+        val bottom = mHeight - mBorderWidth * .5f
+
+        val path = Path()
+        path.moveTo(left, top + mRadius)
+        val scale = mProgress * 1f / mMax / (mRadius * 1f / (right - left))
+        val scale2 = mProgress * 1f / mMax / ((right - mRadius) * 1f / (right - left))
+        if (scale <= 1) {
+            val a = scale * mRadius
+            val angle = Math.acos(((mRadius - a) / mRadius).toDouble())
+            path.arcTo(RectF(left, top, left + 2 * mRadius, top + 2 * mRadius), 180f, (angle * 180 / Math.PI).toFloat())
+            val y = (Math.pow(Math.pow(mRadius.toDouble(), 2.0) - Math.pow((a - mRadius).toDouble(), 2.0), 0.5) + bottom - mRadius).toFloat()
+            path.lineTo(left + a, y)
+            path.arcTo(RectF(left, bottom - 2 * mRadius, left + 2 * mRadius, bottom), 180 - (angle * 180 / Math.PI).toFloat(),
+                    (angle * 180 / Math.PI).toFloat())
+            path.close()
+            canvas.drawPath(path, mPaint!!)
+        } else if (scale2 <= 1) {
+            path.arcTo(RectF(left, top, left + 2 * mRadius, top + 2 * mRadius), 180f, 90f)
+            path.lineTo(left + mProgress * 1f / mMax * (right - left), top)
+            path.lineTo(left + mProgress * 1f / mMax * (right - left), bottom)
+            path.lineTo(left + mRadius, bottom)
+            path.arcTo(RectF(left, bottom - 2 * mRadius, left + 2 * mRadius, bottom), 90f, 90f)
+            path.close()
+            canvas.drawPath(path, mPaint!!)
+        } else {
+            val a = mProgress * 1f / mMax * (right - left) - (right - mRadius)
+            val angle = Math.asin((a / mRadius).toDouble())
+            path.arcTo(RectF(left, top, left + 2 * mRadius, top + 2 * mRadius), 180f, 90f)
+            path.lineTo(right - mRadius, top)
+            path.arcTo(RectF(right - 2 * mRadius, top, right, top + 2 * mRadius), -90f, (angle * 180 / Math.PI).toFloat())
+            val y = Math.pow(Math.pow(mRadius.toDouble(), 2.0) - Math.pow(a.toDouble(), 2.0), .5) + top.toDouble() + mRadius.toDouble()
+
+            path.lineTo(right - mRadius + a, y.toFloat())
+            path.arcTo(RectF(right - 2 * mRadius, bottom - 2 * mRadius,
+                    right, bottom), (90 - angle * 180 / Math.PI).toFloat(),
+                    (angle * 180 / Math.PI).toFloat())
+            path.lineTo(left + mRadius, bottom)
+            path.arcTo(RectF(left, bottom - 2 * mRadius,
+                    left + 2 * mRadius, bottom), 90f, 90f)
+            path.close()
+
+            canvas.drawPath(path, mPaint!!)
+        }
+    }
+
+    private fun drawProgressDesc(canvas: Canvas) {
+        var finalProgressDesc = ""
+        if (100 == mMax) {
+            finalProgressDesc = "$mProgressDesc$mProgress %"
+        } else {
+            finalProgressDesc = "$mProgressDesc$mProgress/$mMax"
+        }
+        mTextPaint!!.getTextBounds(finalProgressDesc, 0, finalProgressDesc.length, mTextBounds)
+        canvas.drawText(finalProgressDesc, (mWidth / 2.0 - mTextBounds!!.width() / 2.0).toInt().toFloat(), (mHeight / 2.0 - (mTextPaint!!.ascent() + mTextPaint!!.descent()) / 2.0f).toInt().toFloat(), mTextPaint!!)
+    }
+
+
+    private fun setProgress(progress: Int) {
+
+        mProgress = if (progress > mMax) mMax else progress
+        invalidateView()
+
+        if (mProgress >= mMax && mOnFinishedListener != null) {
+            mOnFinishedListener!!.onFinish()
+        }
+
+    }
+
+    /**
+     * 得到ProgressBar的最大进度
+     *
+     * @return
+     */
+    fun getMax(): Int {
+        return mMax
+    }
+
+    /**
+     * 获取当前ProgressBar的进度
+     *
+     * @return
+     */
+    fun getProgress(): Int {
+        return mProgress
+    }
+
+    fun setMaxProgress(max: Int) {
+        this.mMax = if (max < 0) 0 else max
+        invalidateView()
+    }
+
+    fun setProgressDesc(desc: String) {
+        mProgressDesc = desc
+        invalidateView()
+    }
+
+    /**
+     * 设置当前进度条的进度(默认动画时间1.5s)
+     *
+     * @param progress
+     */
+    fun setCurProgress(progress: Int) {
+        if (mHaveChangeColor) {
+            setProgressColor(getProgressChangeColor(progress.toFloat() / mMax.toFloat()))
+        } else {
+            setProgressColor(DEFAULT_PROGRESS_COLOR)
+        }
+        val animator = ObjectAnimator.ofInt(this, "progress", progress).setDuration(0)
+        animator.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator) {
+                super.onAnimationEnd(animation)
+                if (mOnAnimationEndListener != null) {
+                    mOnAnimationEndListener!!.onAnimationEnd()
+                }
+            }
+        })
+        animator.start()
+    }
+
+    /**
+     * 设置当前进度条的进度
+     *
+     * @param progress 目标进度
+     * @param duration 动画时长
+     */
+    fun setCurProgress(progress: Int, duration: Long) {
+        if (mHaveChangeColor) {
+            setProgressColor(getProgressChangeColor(progress.toFloat() / mMax.toFloat()))
+        } else {
+            setProgressColor(DEFAULT_PROGRESS_COLOR)
+        }
+        val animator = ObjectAnimator.ofInt(this, "progress", progress).setDuration(duration)
+        animator.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator) {
+                super.onAnimationEnd(animation)
+                if (mOnAnimationEndListener != null) {
+                    mOnAnimationEndListener!!.onAnimationEnd()
+                }
+            }
+        })
+        animator.start()
+    }
+
+    private fun getProgressChangeColor(percentage: Float): Int {
+        if (0 < percentage && percentage <= 0.1) {
+            return Color.parseColor(changeColor[0])
+        } else if (0.1 < percentage && percentage <= 0.2) {
+            return Color.parseColor(changeColor[1])
+        } else if (0.2 < percentage && percentage <= 0.3) {
+            return Color.parseColor(changeColor[2])
+        } else if (0.3 < percentage && percentage <= 0.4) {
+            return Color.parseColor(changeColor[3])
+        } else if (0.4 < percentage && percentage <= 0.5) {
+            return Color.parseColor(changeColor[4])
+        } else if (0.5 < percentage && percentage <= 0.6) {
+            return Color.parseColor(changeColor[5])
+        } else if (0.6 < percentage && percentage <= 0.7) {
+            return Color.parseColor(changeColor[6])
+        } else if (0.7 < percentage && percentage <= 0.8) {
+            return Color.parseColor(changeColor[7])
+        } else if (0.8 < percentage && percentage <= 0.9) {
+            return Color.parseColor(changeColor[8])
+        } else if (0.9 < percentage && percentage <= 1.0) {
+            return Color.parseColor(changeColor[9])
+        }
+        return DEFAULT_PROGRESS_COLOR
+    }
+
+    /**
+     * 设置ProgressBar的颜色
+     *
+     * @param color
+     */
+    fun setProgressColor(color: Int) {
+        mProgressColor = color
+        invalidateView()
+    }
+
+    /**
+     * 设置是否显示当前进度
+     *
+     * @param isShowDesc true:显示
+     */
+    fun setIsShowDesc(isShowDesc: Boolean) {
+        mIsShowDesc = isShowDesc
+        invalidateView()
+    }
+
+    /**
+     * 设置是否需要颜色渐变
+     */
+    fun setHaveChangeColor(haveChangeColor: Boolean) {
+        mHaveChangeColor = haveChangeColor
+        invalidateView()
+    }
+
+    private fun invalidateView() {
+        if (Looper.getMainLooper() == Looper.myLooper()) {
+            invalidate()
+        } else {
+            postInvalidate()
+        }
+    }
+
+    interface OnFinishedListener {
+        fun onFinish()
+    }
+
+    interface OnAnimationEndListener {
+        fun onAnimationEnd()
+    }
+}

+ 12 - 0
common/src/main/res/values/attrs.xml

@@ -137,4 +137,16 @@
         <attr name="smallValue" />
     </declare-styleable>
 
+    <declare-styleable name="ProgressView">
+        <attr name="progressColor" format="color" />
+        <attr name="borderColor" format="color" />
+        <attr name="progressDescColor" format="color" />
+        <attr name="progressRadius" format="dimension" />
+        <attr name="max" format="integer" />
+        <attr name="progress" format="integer" />
+        <attr name="borderWidth" format="dimension" />
+        <attr name="progressDesc" format="string" />
+        <attr name="isShowDesc" format="boolean" />
+        <attr name="haveChangeColor" format="boolean" />
+    </declare-styleable>
 </resources>

+ 2 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/api/ApiManager.kt

@@ -192,6 +192,8 @@ object ApiManager {
          */
         val API_Setting:SettingApi by lazy { apiFactory.createService(SettingApi::class.java, urlManager.buyer)}
 
+        val API_NurseHome:NurseHomeApi by lazy { apiFactory.createService(NurseHomeApi::class.java, urlManager.buyer) }
+
         /**
          * @author LDD
          * @From   ApiManager

+ 6 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/api/NurseHomeApi.kt

@@ -3,7 +3,9 @@ package com.wdkl.ncs.android.middleware.api
 import io.reactivex.Observable
 import okhttp3.ResponseBody
 import retrofit2.http.GET
+import retrofit2.http.POST
 import retrofit2.http.Path
+import retrofit2.http.Query
 
 /**
  * 护士主机首页数据api
@@ -13,4 +15,8 @@ interface NurseHomeApi{
 //    @GET("goods/categories/{parent_id}/children")
    @GET("填写请求路径")
     fun getCategories(@Path("parent_id") parentId:Int): Observable<ResponseBody>
+
+    //获取APP版本信息
+    @POST("/deviceNurse/getAppVersion")
+    fun getAppVersion(@Query("part_id") part_id:Int, @Query("device_type") device_type:Int): Observable<ResponseBody>
 }

+ 2 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constants.kt

@@ -13,6 +13,8 @@ class Constants {
         var tcp_ip: String? =""//tcpip
         var tcp_port: String? =""//tcp端口
         var reader_idle_time: String? =""//tcp心跳
+        var app_path: String =""//apk下载路径
+
         var call_type: Int = 0 //通话类型: 0-语音, 1-视频
         //来电设备id
         var fromId: Int? = -1

+ 5 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/di/ApiProvides.kt

@@ -264,4 +264,9 @@ class ApiProvides {
         return  ApiManager.API_Setting
     }
 
+    @Provides
+    fun provideNurseHomeApi() : NurseHomeApi {
+        return ApiManager.API_NurseHome
+    }
+
 }

+ 1 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/di/ApplicationComponent.kt

@@ -205,4 +205,5 @@ interface ApplicationComponent {
      */
     fun provideSettingApi(): SettingApi
 
+    fun provideNurseHomeApi() : NurseHomeApi
 }

+ 2 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/di/PresenterComponent.kt

@@ -82,4 +82,6 @@ interface PresenterComponent {
 
     fun inject(presenter: CallRecordsFragmentPresenter)
     fun inject(presenter: BedsInTheWardPresenter)
+
+    fun inject(presenter: AppUpdatePresenter)
 }

+ 13 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/contract/nursehome/AppUpdateContract.kt

@@ -0,0 +1,13 @@
+package com.wdkl.ncs.android.middleware.logic.contract.nursehome
+
+import com.wdkl.ncs.android.lib.base.BaseContract
+
+interface AppUpdateContract {
+    interface View : BaseContract.BaseView {
+
+    }
+
+    interface Presenter : BaseContract.BasePresenter {
+
+    }
+}

+ 6 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/contract/nursehome/NurseHomeActivityContract.kt

@@ -2,6 +2,7 @@ package com.wdkl.ncs.android.middleware.logic.contract.nursehome
 
 import com.wdkl.ncs.android.lib.base.BaseContract
 import com.wdkl.ncs.android.middleware.model.ChildCategoryShell
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
 
 /**
  * 获取设备数据接口
@@ -12,6 +13,8 @@ interface NurseHomeActivityContract{
      */
     interface View :BaseContract.BaseView{
         fun showData(categoryList: ArrayList<ChildCategoryShell>)
+
+        fun showAppVersion(appInfo: AppVersionDO)
     }
 
     /**
@@ -19,5 +22,8 @@ interface NurseHomeActivityContract{
      */
     interface Presenter :BaseContract.BasePresenter{
         fun loadData(parentId:Int)
+
+        //获取APP版本信息
+        fun getAppVersion(partId: Int, deviceType: Int)
     }
 }

+ 12 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/presenter/nursehome/AppUpdatePresenter.kt

@@ -0,0 +1,12 @@
+package com.wdkl.ncs.android.middleware.logic.presenter.nursehome
+
+import com.wdkl.ncs.android.lib.base.RxPresenter
+import com.wdkl.ncs.android.middleware.di.MiddlewareDaggerComponent
+import com.wdkl.ncs.android.middleware.logic.contract.nursehome.AppUpdateContract
+import javax.inject.Inject
+
+class AppUpdatePresenter @Inject constructor() : RxPresenter<AppUpdateContract.View>(), AppUpdateContract.Presenter {
+    override fun bindDagger() {
+        MiddlewareDaggerComponent.component.inject(this)
+    }
+}

+ 22 - 18
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/presenter/nursehome/NurseHomeActivityPresenter.kt

@@ -3,6 +3,8 @@ package com.wdkl.ncs.android.middleware.logic.presenter.nursehome
 import com.enation.javashop.net.engine.plugin.connection.ConnectionQuality
 import com.enation.javashop.net.engine.plugin.exception.ExceptionHandle
 import com.enation.javashop.net.engine.utils.ThreadFromUtils
+import com.google.gson.FieldNamingPolicy
+import com.google.gson.GsonBuilder
 import com.wdkl.ncs.android.lib.base.RxPresenter
 import com.wdkl.ncs.android.lib.utils.ConnectionObserver
 import com.wdkl.ncs.android.lib.utils.getJsonString
@@ -13,6 +15,7 @@ import com.wdkl.ncs.android.middleware.di.MiddlewareDaggerComponent
 import com.wdkl.ncs.android.middleware.logic.contract.hello.HelloActivityContract
 import com.wdkl.ncs.android.middleware.logic.contract.nursehome.NurseHomeActivityContract
 import com.wdkl.ncs.android.middleware.model.ChildCategoryShell
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
 import io.reactivex.disposables.Disposable
 import org.json.JSONArray
 import javax.inject.Inject
@@ -26,7 +29,7 @@ class NurseHomeActivityPresenter @Inject constructor() :RxPresenter<NurseHomeAct
      * 首页api 预留 这个是测试的
      */
     @Inject
-    protected lateinit var helloApi: HelloApi
+    protected lateinit var nurseHomeApi: NurseHomeApi
 
 
     /**
@@ -39,15 +42,18 @@ class NurseHomeActivityPresenter @Inject constructor() :RxPresenter<NurseHomeAct
     /**
      * 数据监听者
      */
-    private val observer = object : ConnectionObserver<ArrayList<*>>() {
+    private val observer = object : ConnectionObserver<Any>() {
         override fun onStartWithConnection() {
             providerView().start()
         }
 
-        override fun onNextWithConnection(result: ArrayList<*>, connectionQuality: ConnectionQuality) {
+        override fun onNextWithConnection(result: Any, connectionQuality: ConnectionQuality) {
             providerView().complete()
-            result.getOrNull(0)?.more { data ->
-                providerView().showData(result as ArrayList<ChildCategoryShell>)
+            when (result) {
+                is AppVersionDO -> {
+                    //app版本信息
+                    providerView().showAppVersion(result)
+                }
             }
         }
 
@@ -64,20 +70,18 @@ class NurseHomeActivityPresenter @Inject constructor() :RxPresenter<NurseHomeAct
      * 获取首页数据 待处理
      */
     override fun loadData(parentId: Int) {
-        helloApi.getCategories(parentId).map { response->
-            var jsonResult = response.getJsonString()
-            /**初始化Result数据*/
-            val result = ArrayList<Any>()
+        //
+    }
 
-            if (jsonResult != ""){
-                val jsonArray = JSONArray(jsonResult)
-                for (i in 0..(jsonArray.length() - 1)){
-                    val item = ChildCategoryShell.map(jsonArray.getJSONObject(i))
-                    result.add(item)
-                }
-            }
+    override fun getAppVersion(partId: Int, deviceType: Int) {
+        nurseHomeApi.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 result
-        }.compose(ThreadFromUtils.defaultSchedulers()).subscribe(observer)
+                    return@map appVersion
+                }
+                .compose(ThreadFromUtils.defaultSchedulers())
+                .subscribe(observer)
     }
 }

+ 23 - 8
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/dos/AppVersionDO.java

@@ -25,30 +25,38 @@ public class AppVersionDO implements Serializable {
     @Id(name = "id")
     private Integer id;
     /**
-     * null
+     * 科室Id
      */
     @Column(name = "part_id")
-    @ApiModelProperty(value = "null", required = false)
+    @ApiModelProperty(value = "科室Id", required = false)
     private Integer partId;
     /**
-     * null
+     * 版本号
      */
     @Column(name = "version_no")
-    @ApiModelProperty(value = "null", required = false)
+    @ApiModelProperty(value = "版本号", required = false)
     private Integer versionNo;
     /**
-     * null
+     * 版本信息
      */
     @Column(name = "version_code")
-    @ApiModelProperty(value = "null", required = false)
+    @ApiModelProperty(value = "版本信息", required = false)
     private String versionCode;
     /**
-     * null
+     * 安装文件路径
      */
     @Column(name = "app_path")
-    @ApiModelProperty(value = "null", required = false)
+    @ApiModelProperty(value = "安装文件路径", required = false)
     private String appPath;
 
+    /**
+     * 适用设备类型
+     */
+    @Column(name = "device_type")
+    @ApiModelProperty(value = "适用设备类型(1:护士主机,2:医生主机,3:门口机,4:病床分机)", required = false)
+    private Integer deviceType;
+
+
 
     @PrimaryKeyField
     public Integer getId() {
@@ -95,4 +103,11 @@ public class AppVersionDO implements Serializable {
         this.appPath = appPath;
     }
 
+    public Integer getDeviceType() {
+        return deviceType;
+    }
+
+    public void setDeviceType(Integer deviceType) {
+        this.deviceType = deviceType;
+    }
 }

+ 4 - 0
nursehome/src/main/AndroidManifest.xml

@@ -85,6 +85,10 @@
         <activity android:name=".activity.NurseHomeActivity">
         </activity>
 
+        <activity android:name=".activity.AppUpdateActivity"
+            android:screenOrientation="landscape"
+            android:launchMode="singleTask"/>
+
         <service
             android:name="com.vvsip.ansip.VvsipService"
             android:icon="@drawable/ic_launcher"></service>

+ 138 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/AppUpdateActivity.kt

@@ -0,0 +1,138 @@
+package com.wdkl.ncs.android.component.nursehome.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.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.common.Constants
+import com.wdkl.ncs.android.component.nursehome.databinding.UpdateLayBinding
+import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
+import com.wdkl.ncs.android.component.nursehome.util.AppUpdateHelper
+import com.wdkl.ncs.android.component.nursehome.util.HttpHelper
+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.logic.contract.nursehome.AppUpdateContract
+import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.AppUpdatePresenter
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.utils.MessageEvent
+import kotlinx.android.synthetic.main.update_lay.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+@Router(path = "/nursehome/update")
+class AppUpdateActivity :BaseActivity<AppUpdatePresenter, UpdateLayBinding>(), AppUpdateContract.View {
+    private val TAG = "AppUpdateActivity"
+
+    private val urlManager = UrlManager.build()
+
+    override fun getLayId(): Int {
+        return R.layout.update_lay
+    }
+
+    override fun bindDagger() {
+        NurseHomeLaunch.component.inject(this)
+    }
+
+    override fun init() {
+        if (!TextUtils.isEmpty(Constants.app_path)) {
+            downLoadAPK(urlManager.buyer + "/" + Constants.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() {
+        AppUpdateHelper.installAPK(this);
+
+        /*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() {
+        //
+    }
+
+    override fun destory() {
+        //
+    }
+
+    //数据加载错误
+    override fun onError(message: String, type: Int) {
+        //
+    }
+
+    //数据加载完成
+    override fun complete(message: String, type: Int) {
+        //
+    }
+
+    //开始获取数据
+    override fun start() {
+        //
+    }
+
+    //网络监听
+    override fun networkMonitor(state: NetState) {
+        state.filter(onMobile = {
+
+        },onWifi = {
+
+        },offline = {
+
+        })
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        //
+    }
+}

+ 21 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/activity/NurseHomeActivity.kt

@@ -31,6 +31,7 @@ import com.wdkl.core.consts.Urls
 import com.wdkl.core.socket.IUserState
 import com.wdkl.core.socket.SocketManager
 import com.wdkl.core.voip.CallSingleActivity
+import com.wdkl.ncs.android.component.nursehome.BuildConfig
 import com.wdkl.ncs.android.component.nursehome.R
 import com.wdkl.ncs.android.component.nursehome.SipUtil.SipCallBack
 import com.wdkl.ncs.android.component.nursehome.SipUtil.SipHelperUtil
@@ -46,11 +47,13 @@ import com.wdkl.ncs.android.component.nursehome.util.VoiceManagerUtil
 import com.wdkl.ncs.android.lib.base.BaseActivity
 import com.wdkl.ncs.android.lib.utils.AppTool
 import com.wdkl.ncs.android.lib.utils.debugLog
+import com.wdkl.ncs.android.lib.utils.push
 import com.wdkl.ncs.android.lib.utils.showMessage
 import com.wdkl.ncs.android.middleware.logic.contract.nursehome.NurseHomeActivityContract
 import com.wdkl.ncs.android.middleware.logic.presenter.nursehome.NurseHomeActivityPresenter
 import com.wdkl.ncs.android.middleware.model.ChildCategoryShell
 import com.wdkl.ncs.android.middleware.model.bean.SettingConfiguration
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
 import com.wdkl.ncs.android.middleware.model.vo.CallingHistoryVO
 import com.wdkl.ncs.android.middleware.model.vo.FrameBedVO
 import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
@@ -231,6 +234,10 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
         SettingConfig.setDoorPhoneVolume(this, SettingConfiguration.getInstance().dayDoorVol)
     }
 
+    fun checkApp() {
+        presenter.getAppVersion(Constants.part_id, 101) //护士主机可视版
+    }
+
     /**
      *绑定事件
      */
@@ -312,6 +319,20 @@ class NurseHomeActivity  : BaseActivity<NurseHomeActivityPresenter, ActivityNurs
         showMessage("点击了病床分机")
     }
 
+    override fun showAppVersion(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 + ", 服务器版本: " + newAppVersion)
+
+        //服务器版本和当前版本不一致才做升级操作
+        if (!BuildConfig.VERSION_NAME.equals(newAppVersion)) {
+            Constants.app_path = appInfo.appPath
+            AppTool.Time.delay(200) {
+                push("/nursehome/update")
+            }
+        }
+    }
 
 
     override fun onClick(p0: View) {

+ 2 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/di/NurseHomeComponent.kt

@@ -1,5 +1,6 @@
 package com.wdkl.ncs.android.component.nursehome.di
 
+import com.wdkl.ncs.android.component.nursehome.activity.AppUpdateActivity
 import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
 import com.wdkl.ncs.android.component.nursehome.activity.RegisterActivity
 import com.wdkl.ncs.android.component.nursehome.fragment.*
@@ -27,4 +28,5 @@ interface NurseHomeComponent{
 
     fun inject(activity: CallRecordsFragment)
     fun inject(activity: InpatientWardFragment)
+    fun inject(activity: AppUpdateActivity)
 }

+ 6 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/fragment/SystemSettingsFragment.kt

@@ -9,7 +9,9 @@ import android.util.Log
 import android.view.View
 import android.widget.SeekBar
 import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.ncs.android.component.nursehome.BuildConfig
 import com.wdkl.ncs.android.component.nursehome.R
+import com.wdkl.ncs.android.component.nursehome.activity.NurseHomeActivity
 import com.wdkl.ncs.android.component.nursehome.databinding.FragmentSystemSettingsBinding
 import com.wdkl.ncs.android.component.nursehome.launch.NurseHomeLaunch
 import com.wdkl.ncs.android.component.nursehome.settingconfig.SettingConfig
@@ -156,6 +158,10 @@ class SystemSettingsFragment:BaseFragment<SystemSettingsPresenter,FragmentSystem
         //todo 根据白天晚上设置门口通话音量
 
 
+        software_and_information_tv.setText("软件版本: V" + BuildConfig.VERSION_NAME)
+        software_and_information_tv.setOnClickListener {
+            (activity as NurseHomeActivity).checkApp()
+        }
     }
     /**
      *绑定事件

+ 220 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/AppUpdateHelper.java

@@ -0,0 +1,220 @@
+package com.wdkl.ncs.android.component.nursehome.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.support.v4.content.FileProvider;
+import android.util.Log;
+
+import com.wdkl.ncs.android.component.nursehome.common.Constants;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class AppUpdateHelper {
+    private final static String TAG = "AppUpdate";
+
+    /**
+     * 下载的APK文件绝对路径
+     */
+    public static final String FILE_APK_PATH = Environment.getExternalStorageDirectory() + "/CallingBed2";
+    /**
+     * 下载的APK文件的文件名
+     */
+    public static final String FILE_APK_NAME = "CallingBed2APK.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;
+        }
+
+        /*if (!checkApkAvailable(context, DownloadUtil.FILE_APK_PATH + "/" + DownloadUtil.FILE_APK_NAME)) {
+            ToastUtil.showToast("apk文件不匹配,升级失败!");
+            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 installAPK(Context context) {
+        try {
+            File apkFile = new File(FILE_APK_PATH + "/" + FILE_APK_NAME);
+            if (!apkFile.exists()) {
+                return;
+            }
+
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//安装完成后打开新版本
+            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 给目标应用一个临时授权
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//判断版本大于等于7.0
+                //如果SDK版本>=24,即:Build.VERSION.SDK_INT >= 24,使用FileProvider兼容安装apk
+                String packageName = context.getApplicationContext().getPackageName();
+                String authority = new StringBuilder(packageName).append(".fileprovider").toString();
+                Uri apkUri = FileProvider.getUriForFile(context, authority, apkFile);
+                intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
+            } else {
+                intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
+            }
+            context.startActivity(intent);
+        } catch (Exception e) {
+        }
+    }
+
+    private static boolean checkApkExit(Context context) {
+        File file = new File(FILE_APK_PATH + "/" + FILE_APK_NAME);
+        return file.exists();
+    }
+
+    private static boolean checkApkAvailable(Context context, String path) {
+        try {
+            PackageManager pm = context.getPackageManager();
+            PackageInfo info = pm.getPackageArchiveInfo(path, 0);
+            ApplicationInfo applicationInfo = info.applicationInfo;
+            String newPkg = applicationInfo.packageName;
+            String curPkg = context.getPackageName();
+            Log.d(TAG, "new package: " + newPkg + ", cur package: " + curPkg);
+            if (curPkg.equals(newPkg)) {
+                return true;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return false;
+    }
+
+    public static boolean silentInstall(Context context, String apkPath) {
+        PackageManager packageManager = context.getPackageManager();
+        Class pmClz = packageManager.getClass();
+        try {
+            if (Build.VERSION.SDK_INT >= 21) {
+                Log.d(TAG, "apk path: " + apkPath);
+                Class aClass = Class.forName("android.app.PackageInstallObserver");
+                Constructor constructor = aClass.getDeclaredConstructor();
+                constructor.setAccessible(true);
+                Object installObserver = constructor.newInstance();
+                Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, aClass, int.class, String.class);
+                method.setAccessible(true);
+                method.invoke(packageManager, Uri.fromFile(new File(apkPath)), installObserver, 2, null);
+            } else {
+                Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, Class.forName("android.content.pm.IPackageInstallObserver"), int.class, String.class);
+                method.setAccessible(true);
+                method.invoke(packageManager, Uri.fromFile(new File(apkPath)), null, 2, null);
+            }
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, e.toString());
+        }
+        return false;
+    }
+
+    public static boolean installApp(String packageName, String apkPath) {
+        Process process = null;
+        BufferedReader successResult = null;
+        BufferedReader errorResult = null;
+        StringBuilder successMsg = new StringBuilder();
+        StringBuilder errorMsg = new StringBuilder();
+        Log.e(TAG, "install package: " + packageName + ", apkPath: " + apkPath);
+        try {
+            process = new ProcessBuilder("pm", "install", "-i", packageName, "-r", apkPath).start();
+            successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+            String s;
+            while ((s = successResult.readLine()) != null) {
+                successMsg.append(s);
+            }
+            while ((s = errorResult.readLine()) != null) {
+                errorMsg.append(s);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (successResult != null) {
+                    successResult.close();
+                }
+                if (errorResult != null) {
+                    errorResult.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            if (process != null) {
+                process.destroy();
+            }
+        }
+        Log.e(TAG, "" + errorMsg.toString());
+        //如果含有“success”认为安装成功
+        return successMsg.toString().equalsIgnoreCase("success");
+    }
+
+    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) {
+        Intent intent = new Intent(Intent.ACTION_REBOOT);
+        intent.putExtra("nowait", 1);
+        intent.putExtra("interval", 1);
+        intent.putExtra("window", 0);
+        context.sendBroadcast(intent);
+    }
+
+    public interface UpdateCallBack {
+        void onFailed();
+        void onSuccess();
+    }
+}

+ 160 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/HttpHelper.java

@@ -0,0 +1,160 @@
+package com.wdkl.ncs.android.component.nursehome.util;
+
+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.ncs.android.component.nursehome.util.AppUpdateHelper.FILE_APK_NAME;
+import static com.wdkl.ncs.android.component.nursehome.util.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();
+    }
+}

+ 1 - 1
nursehome/src/main/res/layout/fragment_system_settings.xml

@@ -1322,7 +1322,7 @@
                         android:id="@+id/software_and_information_tv"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:text="软件版本:1.78"
+                        android:text=""
                         android:textColor="#000000"
                         android:textSize="14px" />
 

+ 35 - 0
nursehome/src/main/res/layout/update_lay.xml

@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#ffffff"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/activity_calling_bed_text_download"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:text="正在下载中,请勿操作..."
+            android:textColor="#3D3D63"
+            android:textSize="30sp" />
+
+        <com.wdkl.ncs.android.lib.widget.ProgressView
+            android:id="@+id/activity_appupdate_dialog_progressview"
+            android:layout_width="match_parent"
+            android:layout_height="20dp"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="30dp"
+            android:layout_marginLeft="50dp"
+            android:layout_marginRight="50dp"
+            app:haveChangeColor="true"
+            app:isShowDesc="false"
+            app:max="100"
+            app:progress="0"
+            app:progressDesc="进度:"
+            app:progressRadius="10dp" />
+
+    </RelativeLayout>
+</layout>