Browse Source

# 基于分机版本初始化转换盒项目

weizhengliang 3 years ago
commit
3be7631394
100 changed files with 9869 additions and 0 deletions
  1. 11 0
      .gitignore
  2. 1 0
      app/.gitignore
  3. 152 0
      app/build.gradle
  4. 139 0
      app/keystore_wuyuqing_3128/keytool-importkeypair
  5. 1 0
      app/keystore_wuyuqing_3128/password.txt
  6. BIN
      app/keystore_wuyuqing_3128/platform.pk8
  7. 24 0
      app/keystore_wuyuqing_3128/platform.x509.pem
  8. BIN
      app/keystore_wuyuqing_3128/rk3128.jks
  9. 7 0
      app/keystore_wuyuqing_3128/rk3128_generate_key.sh
  10. 139 0
      app/keystore_wuyuqing_3128/rk3128_signapk/keytool-importkeypair
  11. BIN
      app/keystore_wuyuqing_3128/rk3128_signapk/platform.pk8
  12. 24 0
      app/keystore_wuyuqing_3128/rk3128_signapk/platform.x509.pem
  13. BIN
      app/keystore_wuyuqing_3128/rk3128_signapk/rk3128.jks
  14. 7 0
      app/keystore_wuyuqing_3128/rk3128_signapk/rk3128_generate_key.sh
  15. BIN
      app/keystore_wuyuqing_3128/rk3128_signapk/signapk.jar
  16. BIN
      app/keystore_wuyuqing_3128/signapk.jar
  17. 592 0
      app/proguard-rules.pro
  18. BIN
      app/release/app-release.apk
  19. 1 0
      app/release/output.json
  20. 26 0
      app/src/androidTest/java/com/enation/javashop/android/ExampleInstrumentedTest.java
  21. 87 0
      app/src/main/AndroidManifest.xml
  22. 4 0
      app/src/main/assets/config.javashop
  23. 24 0
      app/src/main/code/com/wdkl/app/ncs/activity/SchemeActivity.kt
  24. 109 0
      app/src/main/code/com/wdkl/app/ncs/application/Application.kt
  25. 63 0
      app/src/main/res/layout/notification_lay.xml
  26. 17 0
      app/src/test/java/com/enation/javashop/android/ExampleUnitTest.java
  27. 1 0
      bedlib/.gitignore
  28. 23 0
      bedlib/build.gradle
  29. 25 0
      bedlib/proguard-rules.pro
  30. 26 0
      bedlib/src/androidTest/java/serialcommunication/wdkl/com/bedlib/ExampleInstrumentedTest.java
  31. 10 0
      bedlib/src/main/AndroidManifest.xml
  32. 74 0
      bedlib/src/main/java/android_serialport_api/SerialPort.java
  33. 496 0
      bedlib/src/main/java/serialporttest/utils/SerialPortUtil.java
  34. 1081 0
      bedlib/src/main/java/serialporttest/utils/StringUtils.java
  35. 11 0
      bedlib/src/main/jni/Android.mk
  36. 1 0
      bedlib/src/main/jni/Application.mk
  37. 167 0
      bedlib/src/main/jni/SerialPort.c
  38. 29 0
      bedlib/src/main/jni/SerialPort.h
  39. 3 0
      bedlib/src/main/jni/gen_SerialPort_h.sh
  40. BIN
      bedlib/src/main/jniLibs/armeabi-v7a/libserial_port.so
  41. BIN
      bedlib/src/main/jniLibs/armeabi/libserial_port.so
  42. BIN
      bedlib/src/main/jniLibs/x86/libserial_port.so
  43. 3 0
      bedlib/src/main/res/values/strings.xml
  44. 17 0
      bedlib/src/test/java/serialcommunication/wdkl/com/bedlib/ExampleUnitTest.java
  45. 137 0
      build.gradle
  46. 1 0
      callingbed2/.gitignore
  47. 127 0
      callingbed2/build.gradle
  48. 21 0
      callingbed2/proguard-rules.pro
  49. 24 0
      callingbed2/src/androidTest/java/com/wdkl/app/ncs/callingbed2/ExampleInstrumentedTest.kt
  50. 24 0
      callingbed2/src/main/AndroidManifest.xml
  51. 127 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/activity/AppUpdateActivity.kt
  52. 784 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/activity/Callingbed2Activity.kt
  53. 77 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/ActionAdapter.kt
  54. 248 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/CallRecordsItemAdapter.kt
  55. 95 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/CostItemAdapter.kt
  56. 67 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/EventHistoryAdapter.kt
  57. 60 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/NurseConfigAdpter.kt
  58. 12 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/agreement/Callingbed2Agreement.kt
  59. 24 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/di/Callingbed2Component.kt
  60. 148 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/BaseCallFragment.kt
  61. 362 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/CallFragment.kt
  62. 75 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/CostFragment.kt
  63. 142 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/MainFragment.kt
  64. 108 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/QrCodeFragment.kt
  65. 519 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/SkyCallFragment.kt
  66. 138 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/VoiceMsgFragment.kt
  67. 174 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/AnrFcExceptionUtil.java
  68. 213 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/AppUpdateHelper.java
  69. 88 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/CallDialogHelper.java
  70. 161 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/HttpHelper.java
  71. 219 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/MediaPlayHelper.java
  72. 497 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/NetHelper.java
  73. 90 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/RecordHelper.java
  74. 24 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/RingPlayHelper.java
  75. 35 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/SOSHelper.java
  76. 81 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/ScreenManagerUtil.kt
  77. 37 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/SerialPortHelper.java
  78. 82 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/SoundPoolManager.java
  79. 17 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/Utils.java
  80. 184 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/VoiceManagerUtil.java
  81. 50 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/WarningDialogHelper.java
  82. 20 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/launch/Callingbed2Launch.kt
  83. 354 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/settings/SettingConfig.java
  84. 579 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/sip/SipHelper.java.bak
  85. 17 0
      callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/sip/SipStatus.java.bak
  86. 7 0
      callingbed2/src/main/res/anim/slide_down_in.xml
  87. 7 0
      callingbed2/src/main/res/anim/slide_left_in.xml
  88. 7 0
      callingbed2/src/main/res/anim/slide_right_out.xml
  89. 7 0
      callingbed2/src/main/res/anim/slide_up_out.xml
  90. 34 0
      callingbed2/src/main/res/drawable-v24/ic_launcher_foreground.xml
  91. BIN
      callingbed2/src/main/res/drawable/ic_answer.png
  92. BIN
      callingbed2/src/main/res/drawable/ic_answer_normal.png
  93. BIN
      callingbed2/src/main/res/drawable/ic_answer_press.png
  94. BIN
      callingbed2/src/main/res/drawable/ic_hangup.png
  95. BIN
      callingbed2/src/main/res/drawable/ic_hangup_normal.png
  96. BIN
      callingbed2/src/main/res/drawable/ic_hangup_press.png
  97. 170 0
      callingbed2/src/main/res/drawable/ic_launcher_background.xml
  98. BIN
      callingbed2/src/main/res/drawable/ic_nurse.png
  99. BIN
      callingbed2/src/main/res/drawable/ic_nurse_call.png
  100. 0 0
      callingbed2/src/main/res/drawable/selector_action_button_bg.xml

+ 11 - 0
.gitignore

@@ -0,0 +1,11 @@
+*.iml
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+/gradle
+.externalNativeBuild
+/.gradle
+/.idea

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 152 - 0
app/build.gradle

@@ -0,0 +1,152 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+//apply plugin: 'com.enation.javashop.aspectjrt'
+
+kapt {
+    arguments {
+        arg("moduleName", project.getName())
+    }
+}
+android {
+    // wuyq add for sign begin
+    signingConfigs {
+        release {
+            storeFile file("keystore_wuyuqing_3128/rk3128.jks")
+            storePassword "111111"
+            keyAlias "rk3128"
+            keyPassword "111111"
+        }
+        debug {
+            storeFile file("keystore_wuyuqing_3128/rk3128.jks")
+            storePassword "111111"
+            keyAlias "rk3128"
+            keyPassword "111111"
+        }
+    }
+    // wuyq add for sign end
+
+    compileSdkVersion target_sdk_version
+    buildToolsVersion build_tools_version
+    aaptOptions.cruncherEnabled = false
+    aaptOptions.useNewCruncher = false
+    aaptOptions.noCompress("mp3","wav")
+
+    defaultConfig {
+        applicationId "com.wdkl.app.ncs.callingbed2"
+        minSdkVersion min_sdk_version
+        targetSdkVersion target_sdk_version
+        versionCode app_version_code
+        versionName app_version
+        multiDexEnabled true
+        dataBinding {
+            enabled = true
+        }
+        //ndk {
+            //选择要添加的对应cpu类型的.so库。
+        //    abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a' ,'x86', 'x86_64', 'mips', 'mips64'
+        //}
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            signingConfig signingConfigs.release
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+        debug {
+            signingConfig signingConfigs.debug
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    sourceSets {
+        main.java.srcDirs += 'src/main/code'
+    }
+    lintOptions{
+        checkReleaseBuilds false
+        abortOnError false
+    }
+    dexOptions {
+
+        javaMaxHeapSize "4g"
+
+        jumboMode=true
+
+        preDexLibraries=true
+
+        threadCount=8
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+}
+
+dependencies {
+    //compile fileTree(include: ['*.jar'], dir: 'libs')
+    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    /**
+     * 单元测试
+     */
+    testCompile 'junit:junit:4.12'
+    /**
+     * Dagger编译依赖
+     */
+    kapt 'com.google.dagger:dagger-compiler:2.7'
+    /**
+     * 内存泄漏监控
+     */
+    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
+    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+    /**
+     * Kotlin依赖
+     */
+    kapt 'com.android.databinding:compiler:2.3.3'
+    /**
+     * 路由注解处理器
+     */
+    kapt "com.enation.geamtear:jrouter-compiler:$router_version"
+    /**
+     * 外部化Moudle配置
+     * moudle独立编译配置
+     */
+    if (!componentTag) {
+        compile project(':welcome') // ===> 开始模块  建议在模块内添加广告 欢迎页 等页面
+        compile project(':home')   // ===> 主页模块  里面一般是App的首页 分类楼层页面
+        //compile project(':shop')      // ===> 店铺模块 店铺列表 详细 等
+        //compile project(':setting')   // ===> 设置模块 设置 缓存 App分享等
+        compile project(':extra')    // ===> 额外的一些页面 比如二维码扫描等一些附加功能
+        //compile project(':hello')
+        compile project(':callingbed2')
+        //compile project(':sip')
+    }
+    /**
+     * JavaShopAndroid 中间件依赖库
+     */
+    compile project(':middleware')
+
+    /**
+     *  constraint-layout布局依赖
+     */
+    compile 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
+
+}
+
+///**
+// * kawo组件化框架配置
+// */
+//kawo {
+//    /**
+//     * Aop注解排除Jar
+//     */
+//    aspectExcludeJarFilter 'com.enation.geamtear.pay','AlipaySdk'
+//}
+//
+

+ 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

BIN
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-----

BIN
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

BIN
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-----

BIN
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 : 签名文件别名

BIN
app/keystore_wuyuqing_3128/rk3128_signapk/signapk.jar


BIN
app/keystore_wuyuqing_3128/signapk.jar


+ 592 - 0
app/proguard-rules.pro

@@ -0,0 +1,592 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/Android/MAC_SDK/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+#############################################
+#
+# 对于一些基本指令的添加
+#
+#############################################
+# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
+-optimizationpasses 5
+
+# 混合时不使用大小写混合,混合后的类名为小写
+-dontusemixedcaseclassnames
+
+# 指定不去忽略非公共库的类
+-dontskipnonpubliclibraryclasses
+
+# 这句话能够使我们的项目混淆后产生映射文件
+# 包含有类名->混淆后类名的映射关系
+-verbose
+
+# 指定不去忽略非公共库的类成员
+-dontskipnonpubliclibraryclassmembers
+
+# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
+-dontpreverify
+
+# 保留Annotation不混淆
+-keepattributes *Annotation*,InnerClasses
+
+# 避免混淆泛型
+-keepattributes Signature
+
+# 抛出异常时保留代码行号
+-keepattributes SourceFile,LineNumberTable
+
+# 指定混淆是采用的算法,后面的参数是一个过滤器
+# 这个过滤器是谷歌推荐的算法,一般不做更改
+-optimizations !code/simplification/cast,!field/*,!class/merging/*
+
+
+#############################################
+#
+# Android开发中一些需要保留的公共部分
+#
+#############################################
+
+# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
+# 因为这些子类都有可能被外部调用
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Appliction
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class * extends android.view.View
+-keep public class com.android.vending.licensing.ILicensingService
+
+
+# 保留support下的所有类及其内部类
+-keep class android.support.** {*;}
+
+# 保留继承的
+-keep public class * extends android.support.v4.**
+-keep public class * extends android.support.v7.**
+-keep public class * extends android.support.annotation.**
+
+# 保留R下面的资源
+-keep class **.R$* {*;}
+
+# 保留本地native方法不被混淆
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# 保留在Activity中的方法参数是view的方法,
+# 这样以来我们在layout中写的onClick就不会被影响
+-keepclassmembers class * extends android.app.Activity{
+    public void *(android.view.View);
+}
+
+# 保留枚举类不被混淆
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+# 保留我们自定义控件(继承自View)不被混淆
+-keep public class * extends android.view.View{
+    *** get*();
+    void set*(***);
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+# 保留Parcelable序列化类不被混淆
+-keep class * implements android.os.Parcelable {
+    public static final android.os.Parcelable$Creator *;
+}
+
+# 保留Serializable序列化的类不被混淆
+-keepnames class * implements java.io.Serializable
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private static final java.io.ObjectStreamField[] serialPersistentFields;
+    !static !transient <fields>;
+    !private <fields>;
+    !private <methods>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
+-keepclassmembers class * {
+    void *(**On*Event);
+    void *(**On*Listener);
+}
+
+# webView处理,项目中没有使用到webView忽略即可
+-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+    public *;
+}
+-keepclassmembers class * extends android.webkit.webViewClient {
+    public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
+    public boolean *(android.webkit.WebView, java.lang.String);
+}
+-keepclassmembers class * extends android.webkit.webViewClient {
+    public void *(android.webkit.webView, jav.lang.String);
+}
+
+# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
+# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
+# 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
+#-assumenosideeffects class android.util.Log {
+#    public static int v(...);
+#    public static int i(...);
+#    public static int w(...);
+#    public static int d(...);
+#    public static int e(...);
+#}
+
+#############################################
+#
+# 项目中特殊处理部分
+#
+#############################################
+
+#-----------处理反射类---------------
+
+
+
+#-----------处理js交互---------------
+
+
+
+#-----------处理实体类---------------
+# 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了。
+-keep class com.wdkl.ncs.android.middleware.model.**{ *; }
+
+-keep class com.enation.app.javashop.**{ *; }
+
+-keep class com.enation.app.javashop.**{ *; }
+
+-keep class com.enation.javashop.android.component.member.adapterPostCommentGoodsAdapter
+-keep class com.wdkl.ncs.android.component.shop.adapter.ShopAllGoodsAdapter
+-keep class com.enation.javashop.android.component.member.adapter.CouponAdapter
+-keep class com.enation.javashop.android.component.order.adapter.OrderItemAdapter
+
+
+#-----------处理第三方依赖库---------
+
+
+
+# AndroidEventBus
+-keep class org.simple.** { *; }
+-keep interface org.simple.** { *; }
+-keepclassmembers class * {
+    @org.simple.eventbus.Subscriber <methods>;
+}
+
+
+# BRVAH
+-keep class com.chad.library.adapter.** { *; }
+-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
+-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
+-keepclassmembers public class * extends com.chad.library.adapter.base.BaseViewHolder {
+    <init>(android.view.View);
+}
+
+
+# Bugly
+-dontwarn com.tencent.bugly.**
+-keep class com.tencent.bugly.** {*;}
+
+
+# ButterKnife
+-keep public class * implements butterknife.Unbinder {
+    public <init>(**, android.view.View);
+}
+-keep class butterknife.*
+-keepclasseswithmembernames class * {
+    @butterknife.* <methods>;
+}
+-keepclasseswithmembernames class * {
+    @butterknife.* <fields>;
+}
+
+
+# Dagger2
+-dontwarn com.google.errorprone.annotations.*
+
+
+# EventBus
+-keepattributes *Annotation*
+-keepclassmembers class ** {
+    @org.greenrobot.eventbus.Subscribe <methods>;
+}
+-keep enum org.greenrobot.eventbus.ThreadMode { *; }
+
+
+# Facebook
+-keep class com.facebook.** {*;}
+-keep interface com.facebook.** {*;}
+-keep enum com.facebook.** {*;}
+
+
+# FastJson
+-dontwarn com.alibaba.fastjson.**
+-keep class com.alibaba.fastjson.** { *; }
+-keepattributes Signature
+-keepattributes *Annotation*
+
+
+# Fresco
+-keep class com.facebook.fresco.** {*;}
+-keep interface com.facebook.fresco.** {*;}
+-keep enum com.facebook.fresco.** {*;}
+
+
+# 高德相关依赖
+# 集合包:3D地图3.3.2 导航1.8.0 定位2.5.0
+-dontwarn com.amap.api.**
+-dontwarn com.autonavi.**
+-keep class com.amap.api.**{*;}
+-keep class com.autonavi.**{*;}
+# 地图服务
+-dontwarn com.amap.api.services.**
+-keep class com.map.api.services.** {*;}
+# 3D地图
+-dontwarn com.amap.api.mapcore.**
+-dontwarn com.amap.api.maps.**
+-dontwarn com.autonavi.amap.mapcore.**
+-keep class com.amap.api.mapcore.**{*;}
+-keep class com.amap.api.maps.**{*;}
+-keep class com.autonavi.amap.mapcore.**{*;}
+# 定位
+-dontwarn com.amap.api.location.**
+-dontwarn com.aps.**
+-keep class com.amap.api.location.**{*;}
+-keep class com.aps.**{*;}
+# 导航
+-dontwarn com.amap.api.navi.**
+-dontwarn com.autonavi.**
+-keep class com.amap.api.navi.** {*;}
+-keep class com.autonavi.** {*;}
+
+
+# Glide
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
+  **[] $VALUES;
+  public *;
+}
+
+
+### greenDAO 3
+-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
+    public static java.lang.String TABLENAME;
+}
+-keep class **$Properties
+
+# If you do not use SQLCipher:
+-dontwarn org.greenrobot.greendao.database.**
+# If you do not use RxJava:
+-dontwarn rx.**
+
+
+# Gson
+-keepattributes Signature
+-keepattributes *Annotation*
+-keep class sun.misc.Unsafe { *; }
+-keep class com.google.gson.stream.** { *; }
+# 使用Gson时需要配置Gson的解析对象及变量都不混淆。不然Gson会找不到变量。
+# 将下面替换成自己的实体类
+#-keep class com.example.bean.** { *; }
+
+
+# Jackson
+-dontwarn org.codehaus.jackson.**
+-dontwarn com.fasterxml.jackson.databind.**
+-keep class org.codehaus.jackson.** { *;}
+-keep class com.fasterxml.jackson.** { *; }
+
+
+# 极光推送
+-dontoptimize
+-dontpreverify
+-dontwarn cn.jpush.**
+-keep class cn.jpush.** { *; }
+
+
+# OkHttp
+-dontwarn okio.**
+-dontwarn okhttp3.**
+-dontwarn javax.annotation.Nullable
+-dontwarn javax.annotation.ParametersAreNonnullByDefault
+
+
+# Okio
+-dontwarn com.squareup.**
+-dontwarn okio.**
+-keep public class org.codehaus.* { *; }
+-keep public class java.nio.* { *; }
+
+
+# OrmLite
+-keepattributes *DatabaseField*
+-keepattributes *DatabaseTable*
+-keepattributes *SerializedName*
+-keep class com.j256.**
+-keepclassmembers class com.j256.** { *; }
+-keep enum com.j256.**
+-keepclassmembers enum com.j256.** { *; }
+-keep interface com.j256.**
+-keepclassmembers interface com.j256.** { *; }
+
+
+# Realm
+-keep class io.realm.annotations.RealmModule
+-keep @io.realm.annotations.RealmModule class *
+-keep class io.realm.internal.Keep
+-keep @io.realm.internal.Keep class * { *; }
+-dontwarn javax.**
+-dontwarn io.realm.**
+
+
+# Retrofit
+-keep class retrofit2.** { *; }
+-dontwarn retrofit2.**
+-keepattributes Signature
+-keepattributes Exceptions
+-dontwarn okio.**
+-dontwarn javax.annotation.**
+
+
+# Retrolambda
+-dontwarn java.lang.invoke.*
+
+
+# RxJava RxAndroid
+-dontwarn sun.misc.**
+-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
+    long producerIndex;
+    long consumerIndex;
+}
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
+    rx.internal.util.atomic.LinkedQueueNode producerNode;
+}
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
+    rx.internal.util.atomic.LinkedQueueNode consumerNode;
+}
+-dontnote rx.internal.util.PlatformDependent
+
+
+
+# 微信支付
+-dontwarn com.tencent.mm.**
+-dontwarn com.tencent.wxop.stat.**
+-keep class com.tencent.mm.** {*;}
+-keep class com.tencent.wxop.stat.**{*;}
+
+
+# 信鸽
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep class com.tencent.android.tpush.**  {* ;}
+-keep class com.tencent.mid.**  {* ;}
+-keepattributes *Annotation*
+
+
+# 新浪微博
+-keep class com.sina.weibo.sdk.* { *; }
+-keep class android.support.v4.* { *; }
+-keep class com.tencent.* { *; }
+-keep class com.baidu.* { *; }
+-keep class lombok.ast.ecj.* { *; }
+-dontwarn android.support.v4.**
+-dontwarn com.tencent.**s
+-dontwarn com.baidu.**
+
+
+# XLog
+-keep class com.tencent.mars.** { *; }
+-keepclassmembers class com.tencent.mars.** { *; }
+-dontwarn com.tencent.mars.**
+
+
+# 讯飞语音
+-dontwarn com.iflytek.**
+-keep class com.iflytek.** {*;}
+
+
+# 银联
+-dontwarn com.unionpay.**
+-keep class com.unionpay.** { *; }
+
+
+# 友盟统计分析
+-keepclassmembers class * { public <init>(org.json.JSONObject); }
+-keepclassmembers enum com.umeng.analytics.** {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+
+# 友盟自动更新
+-keepclassmembers class * { public <init>(org.json.JSONObject); }
+-keep public class cn.irains.parking.cloud.pub.R$*{ public static final int *; }
+-keep public class * extends com.umeng.**
+-keep class com.umeng.** { *; }
+
+
+# 支付宝钱包
+-dontwarn com.alipay.**
+-dontwarn HttpUtils.HttpFetcher
+-dontwarn com.ta.utdid2.**
+-dontwarn com.ut.device.**
+-keep class com.alipay.android.app.IAlixPay{*;}
+-keep class com.alipay.android.app.IAlixPay$Stub{*;}
+-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
+-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
+-keep class com.alipay.sdk.app.PayTask{ public *;}
+-keep class com.alipay.sdk.app.AuthTask{ public *;}
+-keep class com.alipay.mobilesecuritysdk.*
+-keep class com.ut.*
+
+
+
+#解决使用Retrofit+rxJava联网时,在6.0系统出现java.lang.InternalError奔溃的问题:http://blog.csdn.net/mp624183768/article/details/79242147
+-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
+    long producerIndex;
+    long consumerIndex;
+}
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
+    rx.internal.util.atomic.LinkedQueueNode producerNode;
+}
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
+    rx.internal.util.atomic.LinkedQueueNode consumerNode;
+}
+
+-keep class com.tencent.mm.opensdk.** {
+*;
+}
+-keep class com.tencent.wxop.** {
+*;
+}
+-keep class com.tencent.mm.sdk.** {
+*;
+}
+-keep class com.umeng.commonsdk.** {*;}
+-keepclassmembers class * {
+   public <init> (org.json.JSONObject);
+}
+-keep public class [com.zongxueguan.naochanle_android].R$*{
+public static final int *;
+}
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+-dontshrink
+ -dontoptimize
+ -dontwarn com.google.android.maps.**
+ -dontwarn android.webkit.WebView
+ -dontwarn com.umeng.**
+ -dontwarn com.tencent.weibo.sdk.**
+ -dontwarn com.facebook.**
+ -keep public class javax.**
+ -keep public class android.webkit.**
+ -dontwarn android.support.v4.**
+ -keep enum com.facebook.**
+ -keepattributes Exceptions,InnerClasses,Signature
+ -keepattributes *Annotation*
+ -keepattributes SourceFile,LineNumberTable
+
+ -keep public interface com.facebook.**
+ -keep public interface com.tencent.**
+ -keep public interface com.umeng.socialize.**
+ -keep public interface com.umeng.socialize.sensor.**
+ -keep public interface com.umeng.scrshot.**
+
+ -keep public class com.umeng.socialize.* {*;}
+
+
+ -keep class com.facebook.**
+ -keep class com.facebook.** { *; }
+ -keep class com.umeng.scrshot.**
+ -keep public class com.tencent.** {*;}
+ -keep class com.umeng.socialize.sensor.**
+ -keep class com.umeng.socialize.handler.**
+ -keep class com.umeng.socialize.handler.*
+ -keep class com.umeng.weixin.handler.**
+ -keep class com.umeng.weixin.handler.*
+ -keep class com.umeng.qq.handler.**
+ -keep class com.umeng.qq.handler.*
+ -keep class UMMoreHandler{*;}
+ -keep class com.tencent.mm.sdk.modelmsg.WXMediaMessage {*;}
+ -keep class com.tencent.mm.sdk.modelmsg.** implements com.tencent.mm.sdk.modelmsg.WXMediaMessage$IMediaObject {*;}
+ -keep class im.yixin.sdk.api.YXMessage {*;}
+ -keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}
+ -keep class com.tencent.mm.sdk.** {
+    *;
+ }
+ -keep class com.tencent.mm.opensdk.** {
+    *;
+ }
+ -keep class com.tencent.wxop.** {
+    *;
+ }
+ -keep class com.tencent.mm.sdk.** {
+    *;
+ }
+ -dontwarn twitter4j.**
+ -keep class twitter4j.** { *; }
+
+ -keep class com.tencent.** {*;}
+ -dontwarn com.tencent.**
+ -keep class com.kakao.** {*;}
+ -dontwarn com.kakao.**
+ -keep public class com.umeng.com.umeng.soexample.R$*{
+     public static final int *;
+ }
+ -keep public class com.linkedin.android.mobilesdk.R$*{
+     public static final int *;
+ }
+ -keepclassmembers enum * {
+     public static **[] values();
+     public static ** valueOf(java.lang.String);
+ }
+
+ -keep class com.tencent.open.TDialog$*
+ -keep class com.tencent.open.TDialog$* {*;}
+ -keep class com.tencent.open.PKDialog
+ -keep class com.tencent.open.PKDialog {*;}
+ -keep class com.tencent.open.PKDialog$*
+ -keep class com.tencent.open.PKDialog$* {*;}
+ -keep class com.umeng.socialize.impl.ImageImpl {*;}
+ -keep class com.sina.** {*;}
+ -dontwarn com.sina.**
+ -keep class  com.alipay.share.sdk.** {
+    *;
+ }
+
+ -keepnames class * implements android.os.Parcelable {
+     public static final ** CREATOR;
+ }
+
+ -keep class com.linkedin.** { *; }
+ -keep class com.android.dingtalk.share.ddsharemodule.** { *; }
+ -keepattributes Signature
+
+-dontwarn okio.**
+-dontwarn javax.annotation.**

BIN
app/release/app-release.apk


+ 1 - 0
app/release/output.json

@@ -0,0 +1 @@
+[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1},"path":"app-release.apk","properties":{"packageId":"com.enation.app.javashop","split":"","minSdkVersion":"17"}}]

+ 26 - 0
app/src/androidTest/java/com/enation/javashop/android/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.enation.javashop.android;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() throws Exception {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+
+        assertEquals("com.enation.javashop.android", appContext.getPackageName());
+    }
+}

+ 87 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:sharedUserId="android.uid.system"
+    package="com.wdkl.app.ncs">
+    <!--android:sharedUserId="android.uid.system"-->
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+
+    <uses-permission android:name ="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
+    <application
+        android:allowBackup="true"
+        android:icon="@mipmap/launcher"
+        android:label="@string/javashop_app_name"
+        android:supportsRtl="true"
+        tools:replace="android:label"
+        tools:remove="android:requestLegacyExternalStorage"
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:name="com.wdkl.app.ncs.application.Application"
+        android:theme="@style/MyAppTheme">
+        <meta-data
+            android:name="com.enation.javashop.imagepluin.cache.MyGlideModule"
+            android:value="GlideModule" />
+        <meta-data
+            android:name="design_width_in_dp"
+            android:value="1024"/>
+        <meta-data
+            android:name="design_height_in_dp"
+            android:value="600"/>
+
+        <activity android:name="com.wdkl.ncs.android.component.welcome.activity.WelcomeActivity"
+            android:screenOrientation="landscape">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="com.wdkl.app.ncs.activity.SchemeActivity">
+
+            <!-- Schame -->
+            <intent-filter>
+                <data
+                    android:host="m.wdkl.com"
+                    android:scheme="wdkl"/>
+
+                <action android:name="android.intent.action.VIEW"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.BROWSABLE"/>
+            </intent-filter>
+
+            <!-- App Links -->
+            <intent-filter android:autoVerify="true">
+                <action android:name="android.intent.action.VIEW"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.BROWSABLE"/>
+
+                <data
+                    android:host="m.wdkl.com"
+                    android:scheme="http"/>
+                <data
+                    android:host="m.wdkl.com"
+                    android:scheme="https"/>
+            </intent-filter>
+        </activity>
+        <meta-data
+            android:name="android.max_aspect"
+            android:value="2.2" />
+    </application>
+
+</manifest>

+ 4 - 0
app/src/main/assets/config.javashop

@@ -0,0 +1,4 @@
+{
+    "TestMode" : false ,
+    "TestPath" : "/order/create"
+}

+ 24 - 0
app/src/main/code/com/wdkl/app/ncs/activity/SchemeActivity.kt

@@ -0,0 +1,24 @@
+package com.wdkl.app.ncs.activity
+
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import com.enation.javashop.android.jrouter.JRouter
+import com.enation.javashop.android.jrouter.logic.datainfo.Postcard
+import com.enation.javashop.android.jrouter.logic.listener.NavListener
+
+/**
+ * Created by LDD on 2017/9/10.
+ */
+class SchemeActivity:AppCompatActivity() {
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val uri = intent.data
+        JRouter.prepare().create(uri).seek(this,object:NavListener(){
+            override fun onArrival(postcard: Postcard?) {
+                finish()
+            }
+        })
+    }
+
+}

+ 109 - 0
app/src/main/code/com/wdkl/app/ncs/application/Application.kt

@@ -0,0 +1,109 @@
+package com.wdkl.app.ncs.application
+
+import android.os.Build
+import com.enation.javashop.android.jrouter.JRouter
+import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.enation.javashop.net.engine.config.NetEngineConfig
+import com.enation.javashop.net.engine.plugin.exception.RestfulExceptionInterceptor
+import com.enation.javashop.utils.base.config.BaseConfig
+import com.wdkl.app.ncs.callingbed2.helper.AnrFcExceptionUtil
+import com.wdkl.app.ncs.callingbed2.helper.NetHelper
+import com.wdkl.app.ncs.callingbed2.helper.SoundPoolManager
+import com.wdkl.app.ncs.callingbed2.helper.Utils
+import com.wdkl.core.socket.SocketManager
+import com.wdkl.core.voip.VoipEvent
+import com.wdkl.net.HttpRequestPresenter
+import com.wdkl.net.urlconn.UrlConnRequest
+import com.wdkl.skywebrtc.SkyEngineKit
+import serialporttest.utils.SerialPortUtil
+
+/**
+ * @author LDD
+ * @Date   2018/1/11 下午12:48
+ * @From   com.enation.javashop.android.application
+ * @Note   应用Application
+ */
+class Application : BaseApplication() {
+
+
+    /**
+     * @author LDD
+     * @From   Application
+     * @Date   2018/1/11 下午12:50
+     * @Note   应用启动时调用
+     */
+    override fun onCreate() {
+        super.onCreate()
+        initRouter()
+        initFrame()
+        initLeaks()
+    }
+
+    /**
+     * @author  LDD
+     * @From    Application
+     * @Date   2018/1/11 下午12:50
+     * @Note   初始化路由
+     * @return rx观察者
+     */
+    private fun initRouter() {
+        JRouter.init(this)
+        JRouter.openDebug()
+        JRouter.openLog()
+        JRouter.prepare().create("/welcome/launch").seek()
+        JRouter.prepare().create("/home/launch").seek()
+        //JRouter.prepare().create("/setting/launch").seek()
+        //JRouter.prepare().create("/shop/launch").seek()
+        JRouter.prepare().create("/extra/launch").seek()
+
+        //JRouter.prepare().create("/hello/launch").seek()
+
+        JRouter.prepare().create("/callingbed2/launch").seek()
+    }
+
+    /**
+     * @author  LDD
+     * @From    Application
+     * @Date   2018/1/11 下午12:50
+     * @Note   初始化内存检测器
+     * @return rx观察者
+     */
+    private fun initLeaks() {
+//        if (JavaShopConfigCenter.INSTANCE.APP_DEV) {
+//            LeakCanary.install(this)
+//        }
+    }
+
+    /**
+     * @author  LDD
+     * @From    Application
+     * @Date   2018/1/11 下午12:50
+     * @Note   初始化内部框架
+     * @return rx观察者
+     */
+    private fun initFrame() {
+
+        NetEngineConfig.init(baseContext)
+                .openLogger()
+                .addNetInterceptor(RestfulExceptionInterceptor())
+
+        //init
+        NetHelper.getInstance().init()
+
+        //open serial port
+        if (Build.MODEL.equals("rk3128")) {
+            SerialPortUtil.getInstance().openSerialPort();
+        }
+
+        // 初始化网络请求
+        HttpRequestPresenter.init(UrlConnRequest())
+        // 初始化信令
+        SkyEngineKit.init(VoipEvent())
+        SocketManager.getInstance().init(applicationContext)
+
+        //anr catcher
+        AnrFcExceptionUtil.getInstance(this).initFCException()
+
+        Utils.checkCameraSupport()
+    }
+}

+ 63 - 0
app/src/main/res/layout/notification_lay.xml

@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:background="#88000000"
+              android:orientation="horizontal"
+              android:paddingLeft="15dp"
+              android:paddingRight="15dp"
+              android:layout_width="match_parent"
+              android:layout_height="64dp">
+    <ImageView
+        android:layout_width="64dp"
+        android:src="@mipmap/launcher"
+        android:padding="5dp"
+        android:layout_gravity="center_vertical"
+        android:layout_height="64dp"/>
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_weight="1.5"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <TextView
+                android:id="@+id/notification_title"
+                android:text="WdklNCS"
+                android:textColor="#fff"
+                android:gravity="bottom"
+                android:textSize="15dp"
+                android:maxLines="1"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"/>
+            <TextView
+                android:id="@+id/notification_time"
+                android:layout_marginLeft="10dp"
+                android:textSize="12dp"
+                android:text="21-01-07"
+                android:maxLines="1"
+                android:textColor="#99ffffff"
+                android:gravity="bottom"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+        </LinearLayout>
+        <LinearLayout
+            android:layout_marginBottom="5dp"
+            android:layout_weight="1"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+            <TextView
+                android:id="@+id/notification_content"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:textSize="12dp"
+                android:textColor="#99ffffff"
+                android:gravity="top"
+                android:maxLines="2"
+                android:ellipsize="end"
+                android:layout_marginBottom="5dp"
+                android:text="@string/app_name"
+                />
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>

+ 17 - 0
app/src/test/java/com/enation/javashop/android/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.enation.javashop.android;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() throws Exception {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 1 - 0
bedlib/.gitignore

@@ -0,0 +1 @@
+/build

+ 23 - 0
bedlib/build.gradle

@@ -0,0 +1,23 @@
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion target_sdk_version
+    buildToolsVersion build_tools_version
+
+
+    buildTypes {
+        sourceSets {
+            main { jni.srcDirs = [] }
+        }
+    }
+
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    compile 'com.android.support.constraint:constraint-layout:1.0.2'
+    testCompile 'junit:junit:4.12'
+}

+ 25 - 0
bedlib/proguard-rules.pro

@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\android_studio\sdk_path/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 26 - 0
bedlib/src/androidTest/java/serialcommunication/wdkl/com/bedlib/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package serialcommunication.wdkl.com.bedlib;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() throws Exception {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getTargetContext();
+
+        assertEquals("serialcommunication.wdkl.com.bedlib.test", appContext.getPackageName());
+    }
+}

+ 10 - 0
bedlib/src/main/AndroidManifest.xml

@@ -0,0 +1,10 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+
+    package="serialcommunication.wdkl.com.bedlib">
+
+    <application android:allowBackup="true" android:label="@string/app_name"
+        android:supportsRtl="true">
+
+    </application>
+
+</manifest>

+ 74 - 0
bedlib/src/main/java/android_serialport_api/SerialPort.java

@@ -0,0 +1,74 @@
+package android_serialport_api;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class SerialPort {
+
+    private static final String TAG = "SerialPort";
+    private FileDescriptor mFd;
+    private FileInputStream mFileInputStream;
+    private FileOutputStream mFileOutputStream;
+
+    public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
+
+        //检查访问权限,如果没有读写权限,进行文件操作,修改文件访问权限
+        if (!device.canRead() || !device.canWrite()) {
+            try {
+                //通过挂载到linux的方式,修改文件的操作权限
+                Process su = Runtime.getRuntime().exec("/system/xbin/su");
+                String cmd = "chmod 777 " + device.getAbsolutePath() + "\n" + "exit\n";
+                su.getOutputStream().write(cmd.getBytes());
+
+                if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) {
+                    throw new SecurityException();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+                throw new SecurityException();
+            }
+        }
+
+        mFd = open(device.getAbsolutePath(), baudrate, flags);
+
+        if (mFd == null) {
+            Log.e(TAG, "native open returns null");
+            throw new IOException();
+        }
+
+        mFileInputStream = new FileInputStream(mFd);
+        mFileOutputStream = new FileOutputStream(mFd);
+    }
+
+    // Getters and setters
+    public InputStream getInputStream() {
+        return mFileInputStream;
+    }
+
+    public OutputStream getOutputStream() {
+        return mFileOutputStream;
+    }
+
+
+    // JNI(调用java本地接口,实现串口的打开和关闭)
+/**串口有五个重要的参数:串口设备名,波特率,检验位,数据位,停止位
+ 其中检验位一般默认位NONE,数据位一般默认为8,停止位默认为1*/
+    /**
+     * @param path     串口设备的据对路径
+     * @param baudrate 波特率
+     * @param flags    校验位
+     */
+    private native static FileDescriptor open(String path, int baudrate, int flags);
+    public native void close();
+
+    static {//加载jni下的C文件库
+        System.loadLibrary("serial_port");
+    }
+}

+ 496 - 0
bedlib/src/main/java/serialporttest/utils/SerialPortUtil.java

@@ -0,0 +1,496 @@
+package serialporttest.utils;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android_serialport_api.SerialPort;
+
+/**
+ * Created by Administrator on 2017/6/6.
+ */
+
+public class SerialPortUtil {
+    private String TAG = "SerialPortUtil";
+
+    public SerialPort serialPort = null;
+    public InputStream inputStream = null;
+    public OutputStream outputStream = null;
+
+    ISerialPortBedOnclickEvent onDataReceiveListener = null;
+    ISerialPortBedOnclickString onDataReceiveStringListener = null;
+    public boolean isOpenSerialPortUtil = false;
+    private static int length = 9;
+    private static byte[] KeyValue = new byte[length];
+
+    int DataIndex = 0;
+    int DataValue = -1;
+
+    public Thread receiveThread = null;
+
+    public static SerialPortUtil instance = null;
+
+    public Timer timer ;
+    public TimerTask timerTask ;
+
+    public SerialPortUtil() {
+    }
+
+    public static SerialPortUtil getInstance() {
+        if (instance == null) {
+            synchronized (SerialPortUtil.class) {
+                if (instance == null) {
+                    instance = new SerialPortUtil();
+                }
+            }
+        }
+        return instance;
+    }
+
+    /**
+     * 打开串口的方法
+     */
+    public void openSerialPort() {
+        Log.i(TAG, "打开串口");
+        try {
+            serialPort = new SerialPort(new File("/dev/" + getSystemPort()), getSystemBaudrate(), 0);
+            //获取打开的串口中的输入输出流,以便于串口数据的收发
+            inputStream = serialPort.getInputStream();
+            outputStream = serialPort.getOutputStream();
+            isOpenSerialPortUtil = true;
+            receiveSerialPort();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 接收串口数据的方法
+     */
+    private byte[] buffer;
+    private String data;
+
+    public byte[] re_buffer = new byte[2048];
+    String data1 = "";
+    public int writeIndex = 0, readIndex = 0;
+    public boolean finshFlag = false;
+
+    /**
+     * 请求设备维一序列号(设备 ID)
+     */
+    public static String KEY_ID = "";
+
+    public void receiveSerialPort() {
+        initKeyValue();
+        Log.i(TAG, "接收串口数据");
+        if (receiveThread != null)
+            return;
+        buffer = new byte[1024];
+        receiveThread = new Thread() {
+            @Override
+            public void run() {
+                while (isOpenSerialPortUtil) {
+                    try {
+                        if (inputStream == null) {
+                            return;
+                        }
+                        int size = inputStream.read(buffer);
+                        if (size > 0 && isOpenSerialPortUtil) {
+                            data = new String(buffer, 0, size);
+                            //Log.d("aaaa", "data==" + data);
+
+                            if (systemVersionIsO()) {//安卓7.0.1 or 8.1.0 版用
+                                //addByData(data);//my do
+                                changeData(size);//吴总改了后的
+                            } else {//安卓4.2.0 版用
+                                sendData(data);
+                            }
+
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        };
+        //启动接收线程
+        receiveThread.start();
+    }
+
+    String addData = "";
+
+    public synchronized void addByData(String data) {
+        addData = addData + data;
+        if (StringUtils.vagueInquiry(data, "$") && StringUtils.vagueInquiry(data, "#")) {
+            sendData(addData);
+            addData = "";
+        }
+    }
+
+    public void changeData(int size) {
+        if (size > 0) {
+            for (int i = 0; i < size; i++) {
+                re_buffer[writeIndex] = buffer[i];
+                writeIndex++;
+                if (writeIndex > 2047) {
+                    writeIndex = 0;
+                }
+            }
+        }
+        if (readIndex != writeIndex) {
+            while (readIndex != writeIndex) {
+                if (re_buffer[readIndex] == '$') {
+                    data1 = "$";
+                    readIndex++;
+                    if (readIndex > 2047) {
+                        readIndex = 0;
+                    }
+                } else if (re_buffer[readIndex] == '#') {
+                    data1 = data1 + "#";
+                    readIndex++;
+                    if (readIndex > 2047) {
+                        readIndex = 0;
+                    }
+                    finshFlag = true;
+                    break;
+                } else if (data1.length() > 0) {
+                    data1 = data1 + new String(re_buffer, readIndex, 1);
+                    readIndex++;
+                    if (readIndex > 2047) {
+                        readIndex = 0;
+                    }
+                } else {
+                    readIndex++;
+                    if (readIndex > 2047) {
+                        readIndex = 0;
+                    }
+                }
+            }
+        }
+        if (finshFlag) {
+            sendData(data1);
+            data1 = "";
+            finshFlag = false;
+        }
+    }
+
+    public void sendData(String data) {
+        //Log.d("bbbb", "data==" + data);
+        if (!StringUtils.notEmpty(data)) return;
+        if (isOpenSerialPortUtil) {
+            //reset data
+            DataIndex = 0;
+            DataValue = -1;
+            if (data.contains("ID")) {
+                String str = data.substring(data.indexOf(",") + 1, data.indexOf("1#"));
+                int i = 0;
+                while (i < 10) {
+                    if (str.length() > 4) {
+                        break;
+                    } else {
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {
+                            e.printStackTrace();
+                        }
+                        getKeyId();
+                        i++;
+                    }
+                }
+                Log.e(TAG, str);
+                KEY_ID = str;
+            } else {
+                if (data.contains("$") && data.contains("#")) {
+
+                    if (null != onDataReceiveStringListener) {//add by waderson 20190708
+                        onDataReceiveStringListener.serialPortBedOnclickString(data);
+                    }
+
+                    String str = data.substring(data.indexOf("$") + 1, data.indexOf("#"));
+                    if (TextUtils.isDigitsOnly(str.substring(3, 4)) && TextUtils.isDigitsOnly(str.substring(5, 6))) {
+                        DataIndex = Integer.parseInt(str.substring(3, 4));
+                        DataValue = Integer.parseInt(str.substring(5, 6));
+                    }
+                    if (DataIndex < length && DataIndex >= 0) {
+                        KeyValue[DataIndex] = (byte) DataValue;
+                    }
+                    if (null != onDataReceiveListener) {
+                        onDataReceiveListener.serialPortBedOnclick(KeyValue);
+                    }
+                    //======================================
+                    for (int i = 0; i < KeyValue.length; i++) {
+                        if (KeyValue[i] > 0) KeyValue[i] = -1;
+                    }
+                    //======================================
+                }
+            }
+
+        }
+    }
+
+    /**
+     * 判断是否是7.1以上的系统版本
+     *
+     * @return
+     */
+    public static boolean systemVersionIsO() {
+        if (android.os.Build.VERSION.SDK_INT >= 24) {//7.1以上的系统
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 关闭串口的方法
+     * 关闭串口中的输入输出流
+     * 然后将flag的值设为flag,终止接收数据线程
+     */
+    public void closeSerialPort() {
+        Log.i(TAG, "关闭串口");
+        try {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+            if (outputStream != null) {
+                outputStream.close();
+            }
+            if (receiveThread != null && receiveThread.isAlive()) {
+                receiveThread.interrupt();
+                receiveThread = null;
+            }
+            isOpenSerialPortUtil = false;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    //初始化 key值
+    public static void initKeyValue() {
+        KeyValue[0] = -1;
+        KeyValue[1] = -1;
+        KeyValue[2] = -1;
+        KeyValue[3] = -1;
+        KeyValue[4] = -1;
+        KeyValue[5] = -1;
+        KeyValue[6] = -1;
+        KeyValue[7] = -1;
+        KeyValue[8] = -1;
+    }
+
+    public void startHeartBeat() {
+        if (timer != null) timer.purge();
+        if (timerTask != null) timerTask.cancel();
+        timer = new Timer();
+        timerTask = new TimerTask() {
+            @Override
+            public void run() {
+                startHeart();
+            }
+        };
+        timer.schedule(timerTask, 0, 5000);
+    }
+
+    /**
+     * 心跳信号
+     */
+    public void startHeart() {
+        send("$HEART,1F#");
+    }
+
+    /**
+     * 关闭心跳<br>
+     * 若MCU在10秒内没有收到信号,将自动重启Android.  随机数为“W”时将关闭心跳<br>
+     */
+    public void closeHeart() {
+        send("$HEART,WE#");
+    }
+
+    /**
+     * 系统重启
+     */
+    public void systemRestart() {
+        send("$SYSRESET,2D#");
+        Log.i(TAG, "系统重启发送");
+    }
+
+    /*
+    * 进入系统ROM升级模式
+    * */
+    public void systemUpDate() {
+        Log.i(TAG, "系统ROM升级模式串口数据");
+        send("$SYSUPDATE,3C#");
+        Log.i(TAG, "系统ROM升级模式发送");
+
+    }
+
+
+    /**
+     * 发送串口数据的方法
+     *
+     * @param command 要发送的数据
+     */
+    private void send(String command) {
+        try {
+            if (isOpenSerialPortUtil) {
+                byte[] sendData = command.getBytes();
+                outputStream.write(sendData);
+                Log.d("serialPort","==command==" + command);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            Log.d("serialPort","==command==" + command);
+        }
+    }
+
+    public void setOnDataReceiveListener(ISerialPortBedOnclickEvent dataReceiveListener) {
+        onDataReceiveListener = dataReceiveListener;
+    }
+    public void setOnDataReceiveStringListener(ISerialPortBedOnclickString dataReceiveStringListener) {
+        onDataReceiveStringListener = dataReceiveStringListener;
+    }
+
+//    private void saveKeyValue() {
+//        if (timer != null) {
+//            timer.purge();
+//        }
+//        timer = new Timer();
+//
+//        if (timerTask != null) {
+//            timerTask.cancel();
+//        }
+//        timerTask = new TimerTask() {
+//            @Override
+//            public void run() {
+//                if (KeyValue[DataIndex] > 0) {
+//                    initKeyValue();
+//                }
+//            }
+//        };
+//        timer.schedule(timerTask, 10, mTimeInterval);
+//    }
+
+    public static int getSystemVersion() {
+        return android.os.Build.VERSION.SDK_INT;
+    }
+
+    public static String getSystemPort() {
+        String port;
+        if (getSystemVersion() >= 26) { //android8.0 = 26
+            port = "ttyS4";// rk3368 android8.1
+        } else if (getSystemVersion() >= 24) { //android7.0 = 24
+            port = "ttyS1";//rk3128 android7.1
+        } else {
+            port = "ttyS2";// a33 android4.4
+        }
+        return port;
+    }
+
+    /**
+     * 得到波特率
+     *
+     * @return String
+     */
+    public static int getSystemBaudrate() {
+        int baudrate;
+        if (getSystemVersion() >= 26) { //android8.0 = 26
+            baudrate = 115200;// rk3368 android8.1
+        } else if (getSystemVersion() >= 24) { //android7.0 = 24
+            baudrate = 115200;//rk3128 android7.1
+        } else {
+            baudrate = 115200;// a20 android4.2
+        }
+        return baudrate;
+    }
+
+
+    public interface ISerialPortBedOnclickEvent {
+        void serialPortBedOnclick(byte[] buffer);
+    }
+
+    public interface ISerialPortBedOnclickString {
+        void serialPortBedOnclickString(String str);
+    }
+
+    //------------------------below things was add by Waderson 20171106  -----------------------------------
+
+    public static final String C_HEARD = "$";//开头符
+    public static final String C_END = "#";//结束符
+    public static final String C_SEPARATE = ",";//分隔符
+
+    /**
+     * 手柄MIC切换<br>
+     */
+    public static final String MIC = "MIC";
+    /**
+     * 床头灯的切换<br> 0 关闭 1打开 2闪烁
+     */
+    public static final String BEDLIGHT = "RELAY";
+    /**
+     * 门灯控制开关
+     */
+    public static final String DOORLIGHT = "DOORLED";
+    /**
+     * 卫生间呼叫灯控制<br>
+     */
+    public static final String ULED = "ULED";
+    /**
+     * 护理灯光控制<br>
+     */
+    public static final String NURSELIGHT = "NLED";
+    /**
+     * 当前 SIP 协议状态<br>
+     */
+    public static final String SIP_STATUS = "SIP";
+    /**
+     * 呼叫状态<br>
+     */
+    public static final String CALL_STATUS = "CALL";
+    /**
+     * 网络状态<br>
+     */
+    public static final String NET_STATUS = "NETRESET";
+
+    /**
+     * 心跳控制<br>
+     * 若MCU在10秒内没有收到信号,将自动重启Android.  随机数为“W”时将关闭心跳<br>
+     * $ HEART ,1 E #  <br>
+     */
+
+    /**
+     * 写入串口<br>
+     * Waderson 20171103
+     * command  命令 <br>
+     * random   随机数<br>
+     * check  校验符<br>
+     */
+    public void sendCommand(String command, String random, String check) {//$NLED3,beComeDoublF#
+        String random_v = "1", check_v = "F";
+        if (null == command || "".equals(command)) {// beComeDoubleStr(rr) + beComeDoubleStr(gg) + beComeDoubleStr(bb), "F"
+            return;
+        }
+        if (null != random && !"".equals(random)) {
+            random_v = random;
+        }
+        if (null != check && !"".equals(check)) {
+            check_v = check;
+        }
+        send(C_HEARD + command + C_SEPARATE + random_v + check_v + C_END);
+    }
+
+    /**
+     * 请求设备维一序列号(设备 ID)
+     */
+    public void getKeyId() {
+        send("$ID,11#");
+    }
+
+
+}

File diff suppressed because it is too large
+ 1081 - 0
bedlib/src/main/java/serialporttest/utils/StringUtils.java


+ 11 - 0
bedlib/src/main/jni/Android.mk

@@ -0,0 +1,11 @@
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+TARGET_PLATFORM := android-3
+LOCAL_MODULE    := serial_port
+LOCAL_SRC_FILES := SerialPort.c
+LOCAL_LDLIBS    := -llog
+
+include $(BUILD_SHARED_LIBRARY)

+ 1 - 0
bedlib/src/main/jni/Application.mk

@@ -0,0 +1 @@
+APP_ABI := armeabi armeabi-v7a x86

+ 167 - 0
bedlib/src/main/jni/SerialPort.c

@@ -0,0 +1,167 @@
+/*
+ * Copyright 2009-2011 Cedric Priscal
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <jni.h>
+
+#include "SerialPort.h"
+
+#include "android/log.h"
+static const char *TAG="serial_port";
+#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
+#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
+#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
+
+static speed_t getBaudrate(jint baudrate)
+{
+	switch(baudrate) {
+	case 0: return B0;
+	case 50: return B50;
+	case 75: return B75;
+	case 110: return B110;
+	case 134: return B134;
+	case 150: return B150;
+	case 200: return B200;
+	case 300: return B300;
+	case 600: return B600;
+	case 1200: return B1200;
+	case 1800: return B1800;
+	case 2400: return B2400;
+	case 4800: return B4800;
+	case 9600: return B9600;
+	case 19200: return B19200;
+	case 38400: return B38400;
+	case 57600: return B57600;
+	case 115200: return B115200;
+	case 230400: return B230400;
+	case 460800: return B460800;
+	case 500000: return B500000;
+	case 576000: return B576000;
+	case 921600: return B921600;
+	case 1000000: return B1000000;
+	case 1152000: return B1152000;
+	case 1500000: return B1500000;
+	case 2000000: return B2000000;
+	case 2500000: return B2500000;
+	case 3000000: return B3000000;
+	case 3500000: return B3500000;
+	case 4000000: return B4000000;
+	default: return -1;
+	}
+}
+
+/*
+ * Class:     android_serialport_SerialPort
+ * Method:    open
+ * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
+ */
+JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
+  (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags)
+{
+	int fd;
+	speed_t speed;
+	jobject mFileDescriptor;
+
+	/* Check arguments */
+	{
+		speed = getBaudrate(baudrate);
+		if (speed == -1) {
+			/* TODO: throw an exception */
+			LOGE("Invalid baudrate");
+			return NULL;
+		}
+	}
+
+	/* Opening device */
+	{
+		jboolean iscopy;
+		const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
+		LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
+		fd = open(path_utf, O_RDWR | flags);
+		LOGD("open() fd = %d", fd);
+		(*env)->ReleaseStringUTFChars(env, path, path_utf);
+		if (fd == -1)
+		{
+			/* Throw an exception */
+			LOGE("Cannot open port");
+			/* TODO: throw an exception */
+			return NULL;
+		}
+	}
+
+	/* Configure device */
+	{
+		struct termios cfg;
+		LOGD("Configuring serial port");
+		if (tcgetattr(fd, &cfg))
+		{
+			LOGE("tcgetattr() failed");
+			close(fd);
+			/* TODO: throw an exception */
+			return NULL;
+		}
+
+		cfmakeraw(&cfg);
+		cfsetispeed(&cfg, speed);
+		cfsetospeed(&cfg, speed);
+
+		if (tcsetattr(fd, TCSANOW, &cfg))
+		{
+			LOGE("tcsetattr() failed");
+			close(fd);
+			/* TODO: throw an exception */
+			return NULL;
+		}
+	}
+
+	/* Create a corresponding file descriptor */
+	{
+		jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
+		jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
+		jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
+		mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
+		(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
+	}
+
+	return mFileDescriptor;
+}
+
+/*
+ * Class:     cedric_serial_SerialPort
+ * Method:    close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
+  (JNIEnv *env, jobject thiz)
+{
+	jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
+	jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
+
+	jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
+	jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
+
+	jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
+	jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
+
+	LOGD("close(fd = %d)", descriptor);
+	close(descriptor);
+}
+

+ 29 - 0
bedlib/src/main/jni/SerialPort.h

@@ -0,0 +1,29 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class android_serialport_api_SerialPort */
+
+#ifndef _Included_android_serialport_api_SerialPort
+#define _Included_android_serialport_api_SerialPort
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     android_serialport_api_SerialPort
+ * Method:    open
+ * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor;
+ */
+JNIEXPORT jobject JNICALL Java_android_1serialport_1api_SerialPort_open
+  (JNIEnv *, jclass, jstring, jint, jint);
+
+/*
+ * Class:     android_serialport_api_SerialPort
+ * Method:    close
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_android_1serialport_1api_SerialPort_close
+  (JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 3 - 0
bedlib/src/main/jni/gen_SerialPort_h.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+javah -o SerialPort.h -jni -classpath ../src android_serialport_api.SerialPort
+

BIN
bedlib/src/main/jniLibs/armeabi-v7a/libserial_port.so


BIN
bedlib/src/main/jniLibs/armeabi/libserial_port.so


BIN
bedlib/src/main/jniLibs/x86/libserial_port.so


+ 3 - 0
bedlib/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">bedlib</string>
+</resources>

+ 17 - 0
bedlib/src/test/java/serialcommunication/wdkl/com/bedlib/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package serialcommunication.wdkl.com.bedlib;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() throws Exception {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 137 - 0
build.gradle

@@ -0,0 +1,137 @@
+buildscript {
+    /**
+     * Kotlin统一版本
+     */
+    ext.kotlin_version = '1.2.40'
+
+    /**
+     * Aop编制版本
+     */
+    ext.aspectj_version = '1.8.9'
+
+    /**
+     * 是否开启单独Module编译
+     */
+    ext.componentTag = false
+
+    /**
+     * 路由注解处理器版本
+     */
+    ext.router_version = '1.0.2'
+
+    /**
+     * kawo组件化框架版本
+     */
+    ext.kawo_version = '1.0.9'
+
+    /**
+     * SDK最小支持版本
+     */
+    ext.min_sdk_version = 24
+
+    /**
+     * SDK目标支持版本
+     */
+    ext.target_sdk_version = 28
+
+    /**
+     * SDK编译版本
+     */
+    ext.build_tools_version = "26.0.2"
+
+    /**
+     * 支持库版本
+     */
+    ext.support_library_version = "26.0.1"
+
+    /**
+     * APP版本码
+     */
+    ext.app_version_code = 6
+
+    /**
+     * APP版本号
+     */
+    ext.app_version = "1.0.6"
+
+    /**
+     * 项目依赖库
+     */
+    dependencies {
+        repositories {
+            /**
+             * 依赖仓储
+             */
+            maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
+            maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
+            maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
+
+            //jcenter()
+            mavenCentral()
+            google()
+        }
+        /**
+         * Gradle插件
+         */
+        classpath 'com.android.tools.build:gradle:3.0.1'
+
+        /**
+         * Kawo组件化插件
+         */
+        classpath "com.enation.geamtear.gradle:kawo:$kawo_version"
+
+        /**
+         * KotlinGradle插件
+         */
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+
+        /**
+         * Kotlin辅助工具
+         */
+        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
+
+        /**
+         * Aop埋点相关
+         */
+        classpath "org.aspectj:aspectjtools:$aspectj_version"
+    }
+
+    repositories {
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
+
+        jcenter()
+        mavenCentral()
+        google()
+
+        maven { url "https://jitpack.io" }
+    }
+}
+/**
+ * 统一依赖仓储
+ */
+allprojects {
+    repositories {
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/google' }
+        maven { url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
+        maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
+
+        jcenter()
+        mavenCentral()
+        google()
+
+        maven { url "https://jitpack.io" }
+    }
+    tasks.withType(Javadoc) { // 新增
+        options.addStringOption('Xdoclint:none', '-quiet')
+        options.addStringOption('encoding', 'UTF-8')
+    }
+}
+
+/**
+ *  清除Build配置时 删除根目录Build文件夹
+ */
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 1 - 0
callingbed2/.gitignore

@@ -0,0 +1 @@
+/build

+ 127 - 0
callingbed2/build.gradle

@@ -0,0 +1,127 @@
+if (componentTag){
+    apply plugin: 'com.android.application'
+    apply plugin: 'com.enation.javashop.aspectjrt'
+}else{
+    apply plugin: 'com.android.library'
+}
+
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+kapt {
+    arguments {
+        arg("moduleName", project.getName())
+    }
+}
+
+android {
+    compileSdkVersion target_sdk_version
+    buildToolsVersion build_tools_version
+
+
+    defaultConfig {
+        if (componentTag){
+            applicationId "com.wdkl.app.ncs.callingbed2"
+            multiDexEnabled true
+        }
+
+        minSdkVersion min_sdk_version
+        targetSdkVersion target_sdk_version
+        versionCode app_version_code
+        versionName app_version
+        dataBinding {
+            enabled = true
+        }
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+}
+
+dependencies {
+    compile fileTree(dir: 'libs', include: ['*.jar'])
+    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+    /**
+     * 单元测试
+     */
+    testCompile 'junit:junit:4.12'
+
+    /**
+     *  Android基础依赖库
+     */
+    compile "com.android.support:design:$support_library_version"
+    compile "com.android.support:support-v4:$support_library_version"
+    compile "com.android.support:cardview-v7:$support_library_version"
+    compile "com.android.support:appcompat-v7:$support_library_version"
+
+    /**
+     * 公共库依赖
+     */
+    compile project(':middleware')
+    //compile project(':sip')
+    //compile project(':bedlib')
+
+    //web rtc
+    compile project(':webrtc')
+    //compile project(':libwebrtc')
+    compile project(':rtc-chat')
+
+    if(componentTag){
+        debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
+        releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+        testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+    }
+
+    /**
+     * Dagger编译依赖
+     */
+    kapt 'com.google.dagger:dagger-compiler:2.7'
+
+    /**
+     * Kotlin依赖
+     */
+    kapt 'com.android.databinding:compiler:2.3.3'
+
+    /**
+     * 路由注解处理器
+     */
+    kapt "com.enation.geamtear:jrouter-compiler:$router_version"
+
+    /**
+     *  constraint-layout布局依赖
+     */
+    compile 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
+
+    compile 'com.github.anrwatchdog:anrwatchdog:1.3.+'
+}
+
+/**
+ * kawo组件化框架配置
+ */
+if(componentTag){
+    kawo {
+        /**
+         * Aop注解排除Jar
+         */
+        aspectExcludeJarFilter 'com.enation.geamtear.pay','AlipaySdk'
+    }
+}

+ 21 - 0
callingbed2/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 24 - 0
callingbed2/src/androidTest/java/com/wdkl/app/ncs/callingbed2/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.wdkl.app.ncs.callingbed2
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+    @Test
+    fun useAppContext() {
+        // Context of the app under test.
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.wdkl.app.ncs.callingbed2", appContext.packageName)
+    }
+}

+ 24 - 0
callingbed2/src/main/AndroidManifest.xml

@@ -0,0 +1,24 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.wdkl.app.ncs.callingbed2">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name"
+        android:supportsRtl="true" >
+
+        <activity android:name=".activity.Callingbed2Activity"
+            android:launchMode="singleInstance"
+            android:screenOrientation="landscape"/>
+
+        <activity android:name=".activity.AppUpdateActivity"
+            android:screenOrientation="landscape"
+            android:launchMode="singleTask"/>
+    </application>
+</manifest>

+ 127 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/activity/AppUpdateActivity.kt

@@ -0,0 +1,127 @@
+package com.wdkl.app.ncs.callingbed2.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.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.databinding.UpdateLayBinding
+import com.wdkl.app.ncs.callingbed2.helper.AppUpdateHelper
+import com.wdkl.app.ncs.callingbed2.helper.HttpHelper
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+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.callingbed2.AppUpdateContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.AppUpdatePresenter
+import kotlinx.android.synthetic.main.update_lay.*
+
+@Router(path = "/callingbed/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() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        if (!TextUtils.isEmpty(Constant.APP_PATH)) {
+            downLoadAPK(urlManager.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() {
+        //
+    }
+
+    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 = {
+
+        })
+    }
+}

+ 784 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/activity/Callingbed2Activity.kt

@@ -0,0 +1,784 @@
+package com.wdkl.app.ncs.callingbed2.activity
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.ConnectivityManager
+import android.os.*
+import android.support.v4.app.Fragment
+import android.support.v7.widget.LinearLayoutManager
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+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.callingbed2.BuildConfig
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.adapter.NurseConfigAdpter
+import com.wdkl.app.ncs.callingbed2.agreement.Callingbed2Agreement
+import com.wdkl.app.ncs.callingbed2.databinding.Callingbed2MainLayBinding
+import com.wdkl.app.ncs.callingbed2.fragment.*
+import com.wdkl.app.ncs.callingbed2.helper.*
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+import com.wdkl.app.ncs.callingbed2.settings.SettingConfig
+import com.wdkl.core.consts.Urls
+import com.wdkl.core.socket.IUserState
+import com.wdkl.core.socket.SocketManager
+import com.wdkl.ncs.android.lib.base.BaseActivity
+import com.wdkl.ncs.android.lib.utils.*
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.lib.widget.MenuDialog
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.common.SipStatus
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed2.Callingbed2ActivityContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.Callingbed2ActivityPresenter
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+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
+import com.wdkl.ncs.android.middleware.model.vo.BedDeviceInfoVO
+import com.wdkl.ncs.android.middleware.model.vo.CallRecordVO
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.VideoUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import kotlinx.android.synthetic.main.callingbed2_main_lay.*
+import kotlinx.android.synthetic.main.view_title_layout.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import serialporttest.utils.SerialPortUtil
+
+
+/**
+ * Callingbed2Activity :BaseActivity<Callingbed2ActivityPresenter, Callingbed2MainLayBinding>
+ *     Callingbed2ActivityPresenter: 位置com.wdkl.ncs.android.middleware.logic.presenter.callingbed2
+ *     Callingbed2MainLayBinding: 位置callingbed2_main_lay.xml
+ *     Callingbed2ActivityContract: 位置com.wdkl.ncs.android.middleware.logic.contract.callingbed2
+ */
+
+@Router(path = "/callingbed2/main")
+class Callingbed2Activity :BaseActivity<Callingbed2ActivityPresenter, Callingbed2MainLayBinding>(), Callingbed2ActivityContract.View, Callingbed2Agreement,
+        /*IEventListener,*/ SerialPortUtil.ISerialPortBedOnclickEvent, SerialPortUtil.ISerialPortBedOnclickString, IUserState {
+
+    private lateinit var receiver: TimeReceiver
+    private lateinit var curFragment: String
+    //首页
+    private val mainFragment = "main_fragment"
+    //语音留言
+    private val costFragment = "cost_fragment"
+    //二维码
+    private val qrFragment = "qr_fragment"
+
+    //通话界面fragment
+    private var skyCallFragment: Fragment? = null
+
+    private lateinit var linearLayout : LinearLayoutManager
+    private lateinit var nurseConfigAdpter : NurseConfigAdpter
+    private var configList = ArrayList<NurseConfigDto>()
+    private var updating :Boolean = false
+
+    private var clickTime : Long = 0
+    private var clickSosTime : Long = 0
+
+    //网络异常计数
+    private var netErrCount : Int = 0
+
+    override fun getLayId(): Int {
+        return R.layout.callingbed2_main_lay;
+    }
+
+    override fun bindDagger() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        //开始ping网络,30秒ping一次
+        //NetHelper.startNetCheck()
+
+        //获取mac地址
+        Constant.LOCAL_MAC = NetHelper.getInstance().macAddress
+
+        presenter.loadTcpServerHost()
+
+        //注册广播
+        regReceiver()
+        RecordHelper.getInstance().init()
+
+        initNurseConfig()
+
+        EventBus.getDefault().register(this)
+
+        // 添加登录回调
+        SocketManager.getInstance().addUserStateCallback(this)
+
+        if (Build.MODEL.equals("rk3128")) {
+            //串口监听
+            setSerialListner()
+            //打开手柄mic
+            SerialPortHelper.setHandsMIC(true)
+            SerialPortHelper.setCallStatus("0")
+            SerialPortHelper.setSosLight("0")
+        }
+
+        initDevice()
+
+        //更新状态图标
+        updateNetState()
+
+        //启动主fragment
+        switchFragment(R.id.callingbed2_main_frame, MainFragment(), mainFragment)
+
+    }
+
+    override fun userLogin() {
+        updateStatus(SipStatus.REGISTERCOM)
+    }
+
+    override fun userLogout() {
+        updateStatus(SipStatus.REGISTERFAIL)
+    }
+
+    fun switchFragment(id: Int, fragment: Fragment, tag: String) {
+        supportFragmentManager.beginTransaction()
+                .replace(id, fragment, tag)
+                .commit()
+        curFragment = tag
+    }
+
+    fun addCallFragment(fragment: Fragment) {
+        skyCallFragment = fragment
+        supportFragmentManager.beginTransaction()
+            .setCustomAnimations(R.anim.slide_down_in, R.anim.slide_up_out)
+            .add(R.id.call_frame, fragment)
+            .commit()
+    }
+
+    fun removeCallFragment() {
+        if (skyCallFragment != null) {
+            supportFragmentManager.beginTransaction()
+                //.setCustomAnimations(R.anim.slide_down_in, R.anim.slide_down_out)
+                .remove(skyCallFragment)
+                .commit()
+            skyCallFragment = null
+        }
+    }
+
+    fun initDevice() {
+        if (!TextUtils.isEmpty(Constant.LOCAL_MAC)) {
+            presenter.loadDeviceInfo(Constant.LOCAL_MAC)
+        }
+    }
+
+    override fun bindEvent() {
+        tv_btn_home.setOnClickListener {
+            //首页
+            if (!mainFragment.equals(curFragment)) {
+                switchFragment(R.id.callingbed2_main_frame, MainFragment(), mainFragment)
+            }
+        }
+
+        tv_btn_more.setOnClickListener {
+            //更多
+            if (!qrFragment.equals(curFragment)) {
+                switchFragment(R.id.callingbed2_main_frame, QrCodeFragment(), qrFragment)
+            }
+        }
+
+        tv_btn_cost.setOnClickListener {
+            //费用
+            if (!costFragment.equals(curFragment)) {
+                switchFragment(R.id.callingbed2_main_frame, CostFragment(), costFragment)
+            }
+        }
+
+        tv_btn_support.setOnClickListener {
+            //增援
+            showMessage("增援请求已发送!")
+        }
+
+        tv_btn_voice_call.setOnClickListener {
+            val time = System.currentTimeMillis()
+            if (time - clickTime > 2000) {
+                //防止重复点击
+                if (!TextUtils.isEmpty(Constant.SIP_ID)) {
+                    //没有摄像头则只能拨打语音
+                    if (Constant.supportCamera) {
+                        MenuDialog.build(this)
+                            .config(voiceCall = {
+                                startCall(Constant.VOICE_CALL)
+                            }, videoCall = {
+                                startCall(Constant.VIDEO_CALL)
+                            }).show()
+                    } else {
+                        startCall(Constant.VOICE_CALL)
+                    }
+                } else {
+                    showMessage("当前设备可能未办理入住,不能呼叫")
+                }
+
+                clickTime = time
+            } else {
+                showMessage("请勿频繁点击...")
+            }
+        }
+    }
+
+    //设置串口监听
+    private fun setSerialListner() {
+        SerialPortUtil.getInstance().setOnDataReceiveListener(this)
+        SerialPortUtil.getInstance().setOnDataReceiveStringListener(this)
+        //开启串口心跳
+        SerialPortUtil.getInstance().startHeartBeat()
+    }
+
+    private fun initNurseConfig() {
+        //初始化默认护理项
+        for (index in 1..5) {
+            var item = NurseConfigDto()
+            item.nurseConfigName = "暂无"
+            item.nurseOptionName = "暂无"
+            item.nurseColorRbg = "E7E7E7"
+            configList.add(item)
+        }
+
+        linearLayout = LinearLayoutManager(activity)
+        nurseConfigAdpter = NurseConfigAdpter(activity, configList)
+        rv_left_list.layoutManager = linearLayout
+        rv_left_list.adapter = nurseConfigAdpter
+    }
+
+    override fun updateNurseConfig(list: List<NurseConfigDto>) {
+        if (list.size > 0) {
+            for ((index, e) in list.withIndex()) {
+                configList.set(index, e)
+            }
+            nurseConfigAdpter.updateData(configList)
+        }
+    }
+
+    override fun checkAppVersion() {
+        Constant.APP_PATH = ""
+        //获取APP版本信息,7寸分机type=204
+        if (Constant.PART_ID != null) {
+            presenter.getAppVersion(Constant.PART_ID, 204)
+        }
+        Log.d("AppUpdate", "checkAppVersion =====>>  Constant.PART_ID: " + Constant.PART_ID)
+    }
+
+
+    override fun destory() {
+        configList.clear()
+        unRegReceiver()
+        EventBus.getDefault().unregister(this)
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        SerialPortUtil.getInstance().closeHeart()
+        SerialPortUtil.getInstance().closeSerialPort()
+    }
+
+    //数据加载错误
+    override fun onError(message: String, type: Int) {
+        errorLog("error",message)
+        showMessage("数据加载错误")
+        //加载失败,重置customid
+        //Constant.CUSTOM_ID = -1
+    }
+
+    //没有网络
+    override fun onNoNet() {
+        showMessage("没有网络")
+        //加载失败,重置customid
+        //Constant.CUSTOM_ID = -1
+    }
+
+    //数据加载完成
+    override fun complete(message: String, type: Int) {
+    }
+
+    //开始获取数据
+    override fun start() {
+    }
+
+    //网络监听
+    override fun networkMonitor(state: NetState) {
+        state.filter(onMobile = {
+
+        },onWifi = {
+
+        },offline = {
+
+        })
+    }
+
+    //显示设备信息
+    override fun showDeviceInfo(deviceInfo: BedDeviceInfoVO) {
+        Constant.SIP_ID = deviceInfo.sipId
+        Constant.DEVICE_ID = deviceInfo.id
+        Constant.PART_ID = deviceInfo.partId
+        Constant.BED_NAME = deviceInfo.fullName
+
+        Constant.DEVICE_TYPE = deviceInfo.deviceType
+        Constant.DEVICE_CODE = deviceInfo.code
+        Constant.DEVICE_MODEL = deviceInfo.model
+        Constant.DEVICE_SOFT_VER = deviceInfo.softVer
+        Constant.DEVICE_HARD_VER = deviceInfo.hardVer
+        Constant.DEVICE_NAME = deviceInfo.name
+
+        view_title_layout_tv_hospital_name.text = deviceInfo.hospitalName + deviceInfo.partName
+        if (deviceInfo.customerId != null) {
+            Constant.CUSTOM_ID = deviceInfo.customerId
+            EventBus.getDefault().post(MessageEvent("updateCustom", Constant.EVENT_UPDATE_CUSTOM))
+        }
+        presenter.loadPartSettings(Constant.PART_ID)
+
+        connectSocket()
+
+        //检查版本
+        //checkAppVersion()
+    }
+
+    private fun connectSocket(){
+        //拿到sip账号之后再注册
+        if (!TextUtils.isEmpty(Constant.SIP_ID)) {
+            // 连接socket登录
+            SocketManager.getInstance().connect(Urls.WS, Constant.SIP_ID, 0)
+        }
+    }
+
+    //显示交互记录
+    override fun showCallRecords(record: CallRecordVO) {
+        //
+    }
+
+    override fun setPartSettings(partSetting: PartSettingDO) {
+        //设置白昼起止时间
+        SettingConfig.setInitialDayTime(this, partSetting.dayStart.substring(0,5))
+        SettingConfig.setEndOfDay(this,partSetting.nightStart.substring(0,5))
+
+        //分机白天夜晚亮度
+        SettingConfig.setExtensionDaytimeBrightness(this, partSetting.dayLight)
+        SettingConfig.setExtensionNightBrightness(this, partSetting.nightLight)
+
+        //分机白天夜晚护理灯 也就是LED灯的亮度
+        SettingConfig.setExtensionDaytimeLEDBrightness(this, partSetting.dayNurseLed)
+        SettingConfig.setExtensionNightLEDBrightness(this, partSetting.nightNurseLed)
+
+        //分机白天夜晚系统音量
+        SettingConfig.setExtensionDaytimeSystemVolume(this, partSetting.dayBedVol)
+        SettingConfig.setExtensionNightSystemVolume(this, partSetting.nightBedVol)
+
+        //分机通话音量  没做白昼区分
+        SettingConfig.setExtensionCallVolume(this, partSetting.dayBedVol)
+
+        SettingConfig.setSipOverTime(this, partSetting.sipOvertime)
+        SettingConfig.setSleepTime(this, partSetting.sleepSecondsBed)
+
+        updateSettings()
+    }
+
+    override fun setTcpServerHost(tcpSeverDTO: TcpSeverDTO) {
+        Constant.TCP_SERVER_URL = tcpSeverDTO.localIp
+        Constant.TCP_PORT = tcpSeverDTO.tcpPort
+        Constant.TCP_HEART_BEAT = tcpSeverDTO.readerIdleTime
+
+        //开启TCP连接
+        startTcp()
+        showMessage("tcp开始连接...host: " + Constant.TCP_SERVER_URL + ", port: " + Constant.TCP_PORT)
+    }
+
+    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 + ", 服务器版本: " + newAppVersion)
+
+        //服务器版本和当前版本不一致才做升级操作
+        if (!BuildConfig.VERSION_NAME.equals(newAppVersion) && !updating) {
+            Constant.APP_PATH = appInfo.appPath
+            updating = true
+            AppTool.Time.delay(200) {
+                push("/callingbed/update")
+            }
+        }
+    }
+
+    private fun startSipService() {
+        //val intent = Intent(BaseApplication.appContext, SipService::class.java)
+        //startService(intent)
+    }
+
+    fun startToActivity(intent: Intent) {
+        AppTool.Setting.startNewActivity(this@Callingbed2Activity, intent)
+    }
+
+    fun startTcp() {
+        if (Constant.TCP_SERVER_URL != null) {
+            Thread(Runnable { TcpClient.getInstance().init(Constant.TCP_SERVER_URL, Constant.TCP_PORT, Constant.TCP_HEART_BEAT) }).start()
+        }
+    }
+
+    private fun regReceiver() {
+        receiver = TimeReceiver()
+        var intentFilter = IntentFilter()
+        intentFilter.addAction(Intent.ACTION_TIME_TICK)
+        intentFilter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
+        intentFilter.addAction(Intent.ACTION_TIME_CHANGED)
+        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
+        registerReceiver(receiver, intentFilter)
+    }
+
+    private fun unRegReceiver() {
+        unregisterReceiver(receiver)
+    }
+
+    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]
+                + ", buffer[7]:" + buffer[7] + ", buffer[8]:" + buffer[8])
+        if (buffer[5].toInt() == 1 || buffer[5].toInt() == 2) {
+            //面板呼叫键
+            runOnUiThread {
+                if (Constant.CALL_STATE == Constant.CALL_STANDBY) {
+                    if (!TextUtils.isEmpty(Constant.SIP_ID)) {
+                        startCall(Constant.VOICE_CALL)
+                    } else {
+                        showMessage("当前设备可能未办理入住,不能呼叫")
+                    }
+                } else if (Constant.CALL_STATE == Constant.CALL_INCOMING) {
+                    //来电接听
+                    EventBus.getDefault().post(MessageEvent("accept", Constant.EVENT_SERIAL_EVENT))
+                }
+            }
+        } else if (buffer[8].toInt() == 1 || buffer[8].toInt() == 2) {
+            //面板挂断键
+            runOnUiThread {
+                if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                    //通话中挂断
+                    EventBus.getDefault().post(MessageEvent("handoff", Constant.EVENT_SERIAL_EVENT))
+                } else if (Constant.CALL_STATE == Constant.CALL_OUTGOING) {
+                    //呼叫取消
+                    EventBus.getDefault().post(MessageEvent("cancel", Constant.EVENT_SERIAL_EVENT))
+                } else if (Constant.CALL_STATE == Constant.CALL_INCOMING) {
+                    //来电拒接
+                    EventBus.getDefault().post(MessageEvent("reject", Constant.EVENT_SERIAL_EVENT))
+                }
+            }
+        } else if (buffer[6].toInt() == 1 || buffer[6].toInt() == 2) {
+            //手柄按键
+            runOnUiThread {
+                if (Constant.CALL_STATE == Constant.CALL_STANDBY) {
+                    if (!TextUtils.isEmpty(Constant.SIP_ID)) {
+                        startCall(Constant.VOICE_CALL)
+                    } else {
+                        showMessage("当前设备可能未办理入住,不能呼叫")
+                    }
+                } else if (Constant.CALL_STATE == Constant.CALL_INCOMING) {
+                    //来电接听
+                    EventBus.getDefault().post(MessageEvent("accept", Constant.EVENT_SERIAL_EVENT))
+                } else if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                    //通话中挂断
+                    EventBus.getDefault().post(MessageEvent("handoff", Constant.EVENT_SERIAL_EVENT))
+                }
+            }
+        } else if (buffer[7].toInt() == 1 || buffer[7].toInt() == 2) {
+            //卫生间紧急按钮: 一定时间内重复多次按无效
+            if (System.currentTimeMillis() - clickSosTime > 10000) {
+                runOnUiThread {
+                    clickSosTime = System.currentTimeMillis()
+                    SOSHelper.sosStart()
+                }
+            }
+        }
+    }
+
+    override fun serialPortBedOnclickString(str: String) {
+        try {
+            val newStr = str.substring(str.indexOf("$") + 1, str.indexOf("#"))
+            Log.d("serialPortBedOnclickString", "newStr==$newStr")
+            if (newStr.startsWith("V")) {
+                Constant.MCU_VERSION_NUMBER = newStr.substring(newStr.indexOf(",") + 1, 16)
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    //开始呼叫
+    fun startCall(type: Int) {
+        //通话之前先判断webrtc socket和tcp是否连接正常,否则不能建立通话
+        if (SocketManager.getInstance().socketOpen() && Constant.TCP_CONNECTED) {
+            //去电界面
+            Constant.CALL_TYPE = type
+            Constant.CALL_STATE = Constant.CALL_OUTGOING
+            var fragment = SkyCallFragment()
+            var bundle = Bundle()
+            bundle.putInt("call_state", 0)
+            fragment.arguments = bundle
+            addCallFragment(fragment)
+        } else {
+            showMessage("通话服务或网络未连接,请检查网络稍后再试")
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        //代码同步
+        synchronized(Unit) {
+            handleTcpModel(messageEvent)
+        }
+    }
+
+    fun handleTcpModel(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+            //退出通话界面
+            Constant.EVENT_REMOVE_CALL_FRAGMENT -> {
+                if (skyCallFragment != null) {
+                    Constant.CALL_STATE = Constant.CALL_STANDBY
+                    SerialPortHelper.setCallStatus("0")
+                    removeCallFragment()
+                }
+            }
+
+            //Sip注册状态
+            Constant.EVENT_SIP_REGISTER_STATUS -> {
+                if (messageEvent.message is String) run {
+                    val status = messageEvent.message as String
+                    Log.d("sip", "sip regist status: " + status)
+                    updateStatus(status)
+                }
+            }
+
+            //网络ping状态
+            /*Constant.EVENT_INTERNETPING -> {
+                if (NetHelper.NetConn && NetHelper.getInstance().getNetworkType() == ConnectivityManager.TYPE_ETHERNET) {
+                    view_title_layout_iv_ethernet.setImageResource(R.mipmap.ic_etherneted)
+                } else {
+                    view_title_layout_iv_ethernet.setImageResource(R.mipmap.ic_no_ethernet)
+                }
+                view_title_layout_iv_wifi.setImageResource(R.mipmap.ic_wifi_nor)
+            }*/
+
+            //TCP消息处理
+            Constant.EVENT_TCP_MSG -> {
+                if (messageEvent.message is TcpModel) {
+                    val tcpModel = messageEvent.message as TcpModel
+                    Log.d("TCP", "received tcp action: " + tcpModel.action + ", type: " + tcpModel.type)
+                    if (tcpModel.getType() == TcpType.VOICE) {
+                        val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                        if (tcpModel.action == TcpAction.VoiceAction.CALL) {
+                            Constant.fromId = tcpModel.fromId
+                            Constant.interactionId = interactionVO.id
+                            Constant.CALL_STATE = Constant.CALL_INCOMING
+                            //通话之前先判断webrtc socket是否连接上,否则不能建立通话
+                            if (SocketManager.getInstance().socketOpen()) {
+                                //来电界面
+                                var fragment = SkyCallFragment()
+                                var bundle = Bundle()
+                                bundle.putInt("call_state", 1)
+                                bundle.putSerializable("tcp_model", tcpModel)
+                                fragment.arguments = bundle
+                                addCallFragment(fragment)
+                            } else {
+                                showMessage("通话服务还未建立连接,请稍后")
+                                Constant.CALL_STATE = Constant.CALL_STANDBY
+                                VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
+                            }
+                        }
+                    } else if (tcpModel.type == TcpType.SOS) {
+                        if (tcpModel.action == TcpAction.SOSAction.CANCEL) {
+                            //紧急呼叫已处理
+                            SOSHelper.sosStop()
+                        }
+                    } else if (tcpModel.type == TcpType.DEVICE) {
+                        //检查APP版本
+                        if (tcpModel.getAction() == TcpAction.DeviceAction.APP_UPDATE) {
+                            checkAppVersion()
+                        }
+                    } else if (tcpModel.action == TcpAction.DataAction.REFRESH) {
+                        //重新加载数据,比如出院,入院等
+                        initDevice()
+                    }
+
+                    //探视
+                    if (tcpModel.type == TcpType.VIDEO) {
+                        val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                        if (tcpModel.action == TcpAction.VideoAction.VIDEO_INVITE_JOIN) {
+                            if (Constant.CALL_STATE != Constant.CALL_STANDBY) {
+                                VideoUtil.videoInCalling(Constant.DEVICE_ID, tcpModel.fromId, interactionVO.id)
+                            } else {
+                                //收到探视请求,向探视机发起视频通话
+                                Constant.interactionId = interactionVO.id
+                                Constant.fromId = tcpModel.fromId
+                                Constant.CALL_STATE = Constant.CALL_OUTGOING
+
+                                //通话之前先判断webrtc socket是否连接上,否则不能建立通话
+                                if (SocketManager.getInstance().socketOpen()) {
+                                    //探视界面
+                                    var fragment = SkyCallFragment()
+                                    var bundle = Bundle()
+                                    bundle.putInt("call_state", 2)
+                                    bundle.putSerializable("tcp_model", tcpModel)
+                                    fragment.arguments = bundle
+                                    addCallFragment(fragment)
+                                } else {
+                                    showMessage("通话服务还未建立连接,请稍后")
+                                    Constant.CALL_STATE = Constant.CALL_STANDBY
+                                    VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, Constant.fromId, Constant.interactionId)
+                                }
+                            }
+                        } else if (tcpModel.action == TcpAction.VideoAction.HANDOFF) {
+                            Constant.CALL_STATE = Constant.CALL_STANDBY
+                            SerialPortHelper.setCallStatus("0")
+                        }
+                    }
+                }
+            }
+
+            //TCP连接状态
+            Constant.EVENT_TCP_STATE -> {
+                updateTcpState()
+            }
+        }
+    }
+
+    private fun updateStatus(state: String) {
+        this.runOnUiThread {
+            when (state) {
+                SipStatus.REGISTERING -> {
+                    view_title_layout_tv_point.setBackgroundResource(R.color.yellow_color)
+                }
+                SipStatus.REGISTERFAIL -> {
+                    view_title_layout_tv_point.setBackgroundResource(R.color.red_color)
+                }
+                SipStatus.REGISTERCOM -> {
+                    view_title_layout_tv_point.setBackgroundResource(R.color.green)
+                }
+            }
+        }
+    }
+
+    private fun updateNetState() {
+        if (NetHelper.getInstance().networkType == ConnectivityManager.TYPE_WIFI) {
+            view_title_layout_iv_wifi.visibility = View.VISIBLE
+            view_title_layout_iv_wifi.setImageResource(R.mipmap.ic_wifi_success)
+            view_title_layout_iv_ethernet.visibility = View.GONE
+        } else if (NetHelper.getInstance().networkType == ConnectivityManager.TYPE_ETHERNET) {
+            view_title_layout_iv_ethernet.visibility = View.VISIBLE
+            view_title_layout_iv_ethernet.setImageResource(R.mipmap.ic_ethernet_success)
+            view_title_layout_iv_wifi.visibility = View.GONE
+        }
+
+        /*if (NetHelper.isBTConnected()) {
+            view_title_layout_iv_bt.visibility = View.VISIBLE
+            view_title_layout_iv_bt.setImageResource(R.mipmap.ic_bt_success)
+        } else {
+            view_title_layout_iv_bt.visibility = View.GONE
+        }*/
+
+        /*
+        * 检查网络情况,若tcp断开连接多次且IP也是空的则网络异常,重启设备
+        * 仅对3128设备有效
+         */
+        if (Build.MODEL.equals("rk3128")) {
+            var count = SettingConfig.getNetErrResetCount(this)
+            if (!Constant.TCP_CONNECTED && TextUtils.isEmpty(NetHelper.getInstance().localIP)) {
+                netErrCount++
+            } else {
+                netErrCount = 0
+                if (count > 0) {
+                    count = 0
+                    SettingConfig.setNetErrResetCount(this, count)
+                }
+            }
+
+            if (netErrCount >= 5) {
+                //如果重启次数超过8次还是无网则不再重启
+                if (count < 8) {
+                    count++
+                    SettingConfig.setNetErrResetCount(this, count)
+                    Handler().postDelayed({
+                        AppUpdateHelper.reboot(this)
+                    }, 5000)
+                } else {
+                    WarningDialogHelper.showDialog(this@Callingbed2Activity)
+                }
+            }
+        }
+    }
+
+    private fun updateTcpState() {
+        if (Constant.TCP_CONNECTED) {
+            view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_success)
+            if (SocketManager.getInstance().userState == 0) {
+                //如果socket断开了则重连
+                connectSocket()
+            }
+        } else {
+            view_title_layout_iv_tcp.setImageResource(R.mipmap.ic_tcp_fail)
+        }
+    }
+
+    private fun updateSettings() {
+        val  currentTimestamp = System.currentTimeMillis()
+
+        //白天起始时间戳
+        var date = TimeHandle.getDateTime("yyyy-MM-dd")
+        var dayStartTimeStamp = TimeHandle.dateToStamp(date+" "+SettingConfig.getInitialDayTime(this)+":00", "yyyy-MM-dd HH:mm:ss")
+        //白天结束时间戳
+        var endOfDayTimeStamp = TimeHandle.dateToStamp(date+" "+SettingConfig.getEndOfDay(this)+":00", "yyyy-MM-dd HH:mm:ss")
+
+        //白天
+        if (dayStartTimeStamp < currentTimestamp && currentTimestamp < endOfDayTimeStamp) {
+            //设置白天亮度
+            ScreenManagerUtil().setScreenBrightness(this, Math.ceil(2.54 * SettingConfig.getExtensionDaytimeBrightness(this)).toInt())
+            //设置白天系统音量和响铃音
+            VoiceManagerUtil.setSystemVoice(this, SettingConfig.getExtensionDaytimeSystemVolume(this))
+            VoiceManagerUtil.setMusicVoice(this, SettingConfig.getExtensionDaytimeSystemVolume(this))
+            view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_daylight)
+            Constant.inday = true
+        } else { //夜晚
+            //设置晚上亮度
+            ScreenManagerUtil().setScreenBrightness(this, Math.ceil(2.54 * SettingConfig.getExtensionNightBrightness(this)).toInt())
+            //设置晚上系统音量和响铃音
+            VoiceManagerUtil.setSystemVoice(this, SettingConfig.getExtensionNightSystemVolume(this))
+            VoiceManagerUtil.setMusicVoice(this, SettingConfig.getExtensionNightSystemVolume(this))
+            view_title_layout_iv_day_night.setImageResource(R.mipmap.ic_night)
+            Constant.inday = false
+        }
+    }
+
+    inner class TimeReceiver: BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            if (intent.action == Intent.ACTION_TIME_TICK
+                    || intent.action == Intent.ACTION_TIME_CHANGED
+                    || intent.action == Intent.ACTION_TIMEZONE_CHANGED) {
+                updateNetState()
+                updateSettings()
+                updateTcpState()
+
+                if (SocketManager.getInstance().userState == 1) {
+                    view_title_layout_tv_point.setBackgroundResource(R.color.green)
+                } else {
+                    view_title_layout_tv_point.setBackgroundResource(R.color.red_color)
+                }
+            } else if (intent.action == ConnectivityManager.CONNECTIVITY_ACTION) {
+                updateNetState()
+                if (NetHelper.getInstance().networkAvailable()) {
+                    if (!Constant.TCP_CONNECTED) {
+                        TcpClient.getInstance().doConnect()
+                    }
+                    if (SocketManager.getInstance().userState == 0) {
+                        //如果socket断开了则重连
+                        connectSocket()
+                    }
+                }
+            }
+        }
+    }
+}

+ 77 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/ActionAdapter.kt

@@ -0,0 +1,77 @@
+package com.wdkl.app.ncs.callingbed2.adapter
+
+import android.content.Context
+import android.support.v7.widget.RecyclerView
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.ncs.android.middleware.api.UrlManager
+import com.wdkl.ncs.android.middleware.model.ActionItem
+
+class ActionAdapter : RecyclerView.Adapter<ActionAdapter.ViewHolder>{
+    private var context: Context
+    private var data: ArrayList<ActionItem>
+    private lateinit var onItemClickListener: OnItemClickListener
+
+    private val urlManager = UrlManager.build()
+
+    constructor(context: Context, data: ArrayList<ActionItem>): super() {
+        this.context = context
+        this.data = data
+    }
+
+    fun setOnItemClickListener(listener: OnItemClickListener) {
+        this.onItemClickListener = listener
+    }
+
+    fun updateData(data: ArrayList<ActionItem>) {
+        this.data = data
+        notifyDataSetChanged()
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_action_view, parent, false)
+        val viewHolder = ViewHolder(view)
+
+        return viewHolder
+    }
+
+    override fun getItemCount(): Int {
+        return data?.size
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        holder.textView.text = data?.get(position).name
+        Glide.with(holder.imageView.context)
+                .load(urlManager.base + data?.get(position).iconSrc)
+                .diskCacheStrategy(DiskCacheStrategy.ALL)
+                .into(holder.imageView)
+
+        holder.actionView.setOnClickListener {
+            onItemClickListener?.onItemClick(holder.itemView, data?.get(position).id)
+        }
+    }
+
+
+    class ViewHolder: RecyclerView.ViewHolder {
+        var actionView : View
+        var textView : TextView
+        var imageView : ImageView
+
+        constructor(itemView: View): super(itemView) {
+            actionView = itemView.findViewById(R.id.ll_action_view)
+            textView = itemView.findViewById(R.id.tv_action)
+            imageView = itemView.findViewById(R.id.iv_action)
+        }
+    }
+
+    interface OnItemClickListener {
+        fun onItemClick(view: View, keyId: Int)
+    }
+}

+ 248 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/CallRecordsItemAdapter.kt

@@ -0,0 +1,248 @@
+package com.wdkl.ncs.android.component.nursehome.adapter
+
+import android.content.Intent
+import android.support.v4.content.ContextCompat.startActivity
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.layout.LinearLayoutHelper
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.databinding.AdapterCallRecordsItemBinding
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.TimeHandle
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.logic.contract.shop.ShopCategoryActivityContract
+import com.wdkl.ncs.android.middleware.model.dos.CallingHistoryDO
+import com.wdkl.ncs.android.middleware.model.dos.InteractionDO
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.EventUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.ImUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+
+/**
+ * 呼叫记录适配器
+ */
+class CallRecordsItemAdapter(var data: ArrayList<InteractionVO>) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterCallRecordsItemBinding>, InteractionVO>() {
+    var TAG = CallRecordsItemAdapter::class.java.getSimpleName()
+
+    /**
+     * 数据提供者
+     */
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    /**
+     * Item坐标
+     */
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    /**
+     * 获取Item总数
+     */
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    //刷新数据
+    fun updateCallRecords(data: ArrayList<InteractionVO>) {
+        this.data = data
+        notifyDataSetChanged()
+    }
+
+    /**
+     * 创建LayoutHelper
+     */
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        return LinearLayoutHelper(0, data.size)
+    }
+
+    /**
+     * 创建ViewHolder
+     */
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseRecyclerViewHolder<AdapterCallRecordsItemBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_call_records_item)
+    }
+
+    /**
+     * 绑定数据
+     */
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterCallRecordsItemBinding>?, position: Int) {
+        holder?.bind { binding ->
+            val itemData = getItem(position)
+            if (itemData.createDate != null) {
+                binding.callTimeTv.text = TimeHandle.getDateTime(itemData.createDate * 1000, "MM-dd HH:mm")
+            }
+            if (itemData.actionAccept != null) {
+                binding.processingTimeTv.text = TimeHandle.getDateTime(itemData.actionAccept * 1000, "MM-dd HH:mm")
+            }
+
+            //是否已播放 已响应
+            if (itemData.actionEnd != null) {
+                /*Log.e(TAG, "" + itemData.actionEnd+" "+position)
+                Log.e(TAG, "" + itemData.actionType+" "+position)
+                Log.e(TAG, "" + itemData.data+" "+position)
+                Log.e(TAG, "" + itemData.actionType+" "+position)
+                Log.e(TAG, "" + TcpType.IM.name+" "+position)*/
+
+                binding.treatmentStateImagev.setImageResource(R.mipmap.ic_event_done)
+//                binding.callStatusImagev.setImageResource(R.drawable.yi_jie)
+                binding.treatmentStateImagev.visibility = View.VISIBLE
+                binding.processingTimeTv.visibility = View.VISIBLE
+
+                //判断是呼入还是呼出 1 分机到主机 2主机到分机
+                if(itemData.actionDirectionType == 1){
+                    binding.sickbedTv.text = itemData.toFrameFullName
+                    binding.nameTv.text = itemData.toMemberName
+                    //TODO 图标显示不正常 暂时没放完整图标
+                    binding.callStatusImagev.setImageResource(R.mipmap.ic_call_done)
+                }else if(itemData.actionDirectionType == 2 || itemData.actionDirectionType == 3){
+                    binding.sickbedTv.text = itemData.fromFrameFullName
+                    binding.nameTv.text = itemData.fromMemberName
+                    binding.callStatusImagev.setImageResource(R.mipmap.ic_call_done)
+                }
+
+                if(itemData.actionType == TcpType.EVENT.name){//事件已经响应 相应的数据
+                    binding.playTv.visibility = View.GONE
+                    binding.projectTv.visibility = View.VISIBLE
+                    binding.conductorNameTv.visibility = View.VISIBLE
+                    binding.projectTv.text = itemData.data
+                    binding.conductorNameTv.text = itemData.toMemberName
+                    binding.processingTimeTv.text = TimeHandle.getDateTime(itemData.actionEnd*1000, "MM-dd HH:mm")
+                    binding.projectTv.setBackgroundResource(R.mipmap.ic_event_handled)
+                }else if (itemData.actionType == TcpType.IM.name){//语音已播放
+
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.VISIBLE
+                    binding.conductorNameTv.visibility = View.GONE
+                    binding.playTv.setBackgroundResource(R.mipmap.ic_play_done)
+                    binding.processingTimeTv.text = "已播放"
+
+                }else if(itemData.actionType == TcpType.VOICE.name){ //语音呼叫已接听
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.VISIBLE
+                    binding.treatmentStateImagev.visibility = View.GONE
+                    binding.processingTimeTv.visibility = View.GONE
+
+                }else if(itemData.actionType == TcpType.VIDEO.name){ //视频呼叫已接听
+
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.VISIBLE
+                    binding.treatmentStateImagev.visibility = View.GONE
+                    binding.processingTimeTv.visibility = View.GONE
+                }
+
+            } else {
+                /*Log.e(TAG, "" + itemData.actionEnd+" "+position)
+                Log.e(TAG, "" + itemData.actionType+" "+position)
+                Log.e(TAG, "" + itemData.data+" "+position)
+                Log.e(TAG, "" + itemData.actionDirectionType+" "+position)*/
+
+                //判断是呼入还是呼出 1 分机到主机 2主机到分机
+                if(itemData.actionDirectionType == 1){
+                    Log.e(TAG, "进入。。。。" )
+                    binding.sickbedTv.text = itemData.toFrameFullName
+                    binding.nameTv.text = itemData.toMemberName
+                    // 图标显示不正常 暂时没放完整图标
+                    binding.callStatusImagev.setImageResource(R.mipmap.ic_call_undo)
+                }else if(itemData.actionDirectionType == 2 || itemData.actionDirectionType == 3){
+                    binding.sickbedTv.text = itemData.fromFrameFullName
+                    binding.nameTv.text = itemData.fromMemberName
+                    binding.callStatusImagev.setImageResource(R.mipmap.ic_call_undo)
+                }
+
+                binding.treatmentStateImagev.setImageResource(R.mipmap.ic_event_undo)
+//                binding.callStatusImagev.setImageResource(R.drawable.wei_jie)
+                binding.conductorNameTv.visibility = View.GONE
+                binding.treatmentStateImagev.visibility = View.VISIBLE
+                binding.processingTimeTv.visibility = View.VISIBLE
+                if(itemData.actionType == TcpType.EVENT.name){//事件未处理
+                    binding.playTv.visibility = View.GONE
+                    binding.projectTv.visibility = View.VISIBLE
+                    binding.projectTv.text = itemData.data
+                    binding.projectTv.setBackgroundResource(R.mipmap.ic_event_unhandle)
+                    binding.processingTimeTv.text = "未处理"
+                }else if (itemData.actionType == TcpType.IM.name){//语音未播放
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.VISIBLE
+                    binding.playTv.setBackgroundResource(R.mipmap.ic_play)
+                    binding.processingTimeTv.text = "未播放"
+                }else if(itemData.actionType == TcpType.VOICE.name){ //语音呼叫未接听
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.GONE
+                    binding.treatmentStateImagev.visibility = View.GONE
+                    binding.processingTimeTv.visibility = View.GONE
+                }else if(itemData.actionType == TcpType.VIDEO.name){ //视频呼叫未接听
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.GONE
+                    binding.treatmentStateImagev.visibility = View.GONE
+                    binding.processingTimeTv.visibility = View.GONE
+
+                }
+            }
+
+
+/*            binding.projectTv.setOnClickListener {
+                //事件处理监听
+//                    点击之后发送通知
+//                    showMessage("点击了事件按钮")
+                if (itemData.actionEnd != null)return@setOnClickListener
+                Log.e(TAG, "点击了事件按钮")
+
+                Thread(Runnable {
+                    run {
+                        Log.e(TAG, "aaassss "+Constants.ids +"kong")
+
+                        if (!Constants.ids.equals("")) {
+                            var eventTcpModel = EventUtil.eventResponse(Constants.ids.toInt(), itemData.fromDeviceId, itemData.id)
+//                        val map = HashMap<String,Int>()
+//                        map.put("id",1)
+//                        eventTcpModel.setData(map)
+                            TcpClient.getInstance().sendMsg(eventTcpModel.toJson())
+                        } else {
+                            showMessage("获取不到当给前设备id")
+                        }
+                    }
+                }).start()
+
+                //处理完之后要刷新数据
+            }*/
+
+/*            binding.playTv.setOnClickListener {
+                if (itemData.actionEnd != null)return@setOnClickListener
+                //点击之后播放语音留言
+                showMessage("点击了播放留言")
+                Thread(Runnable {
+                    run {
+                        if (!Constants.ids.equals("")) {
+                            var imUtilTcpModel = ImUtil.imRead(Constants.ids.toInt(), itemData.fromDeviceId, itemData.id)
+
+//                val map = HashMap<String,Int>()
+//                map.put("id",1)
+//                imUtilTcpModel.setData(map)
+                            TcpClient.getInstance().sendMsg(imUtilTcpModel.toJson())
+                            var mediaPlayer = MediaPlayer()
+                            mediaPlayer.startMediaPlayer("http://192.168.1.112:8006"+itemData.data)
+                            mediaPlayer.setVolume(0.3f)
+
+                        } else {
+                            showMessage("获取不到当给前设备id")
+                        }
+                    }
+                }).start()
+
+                //处理完之后要刷新数据
+            }*/
+
+        }
+    }
+
+}

+ 95 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/CostItemAdapter.kt

@@ -0,0 +1,95 @@
+package com.wdkl.app.ncs.callingbed2.adapter
+
+import android.content.Context
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.ncs.android.middleware.model.vo.CustomerFeeConfigByGroupNameVO
+import com.wdkl.ncs.android.middleware.model.vo.CustomerFeeConfigVO
+
+class CostItemAdapter : RecyclerView.Adapter<CostItemAdapter.ParentViewHolder> {
+
+    private var context: Context
+    private var mainData: ArrayList<CustomerFeeConfigByGroupNameVO>
+
+    constructor(context: Context, data: ArrayList<CustomerFeeConfigByGroupNameVO>) {
+        this.context = context
+        this.mainData = data
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ParentViewHolder {
+        val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_cost_main_view, parent, false)
+        val viewHolder = ParentViewHolder(view)
+
+        return viewHolder
+    }
+
+    override fun onBindViewHolder(holder: ParentViewHolder?, position: Int) {
+        holder?.costGroupName?.text = mainData.get(position).feeGroupName
+        holder?.costGroupTotal?.text = "小计: ¥" + mainData.get(position).subtotal
+
+        val layoutManager = LinearLayoutManager(context)
+        holder?.costDetail?.layoutManager = layoutManager
+        holder?.costDetail?.adapter = CostDetailAdapter(mainData.get(position).customerFeeConfigList)
+    }
+
+    override fun getItemCount(): Int {
+        return mainData.size
+    }
+
+    class ParentViewHolder : RecyclerView.ViewHolder {
+        var costGroupName : TextView
+        var costGroupTotal : TextView
+        var costDetail : RecyclerView
+
+        constructor(itemView: View): super(itemView) {
+            costGroupName = itemView.findViewById(R.id.tv_cost_group_name)
+            costGroupTotal = itemView.findViewById(R.id.tv_cost_group_total)
+            costDetail = itemView.findViewById(R.id.rv_cost_detail)
+        }
+    }
+
+    class CostDetailAdapter : RecyclerView.Adapter<CostDetailAdapter.ChildViewHolder> {
+        private var costData: List<CustomerFeeConfigVO>
+
+        constructor(data: List<CustomerFeeConfigVO>) {
+            costData = data
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ChildViewHolder {
+            val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_cost_detail, parent, false)
+            val viewHolder = ChildViewHolder(view)
+
+            return viewHolder
+        }
+
+        override fun onBindViewHolder(holder: ChildViewHolder?, position: Int) {
+            holder?.costName?.text = costData.get(position).feeName
+            holder?.costValue?.text = "¥" + costData.get(position).feeValue
+            holder?.costId?.text = costData.get(position).keyCode
+            holder?.costUnit?.text = costData.get(position).unit
+        }
+
+        override fun getItemCount(): Int {
+            return costData.size
+        }
+
+        class ChildViewHolder : RecyclerView.ViewHolder {
+            var costName : TextView
+            var costValue : TextView
+            var costId : TextView
+            var costUnit : TextView
+
+            constructor(item : View) : super(item) {
+                costName = item.findViewById(R.id.tv_item_cost_name)
+                costValue = item.findViewById(R.id.tv_item_cost_value)
+                costId = item.findViewById(R.id.tv_item_cost_id)
+                costUnit = item.findViewById(R.id.tv_item_cost_unit)
+            }
+        }
+    }
+}

+ 67 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/EventHistoryAdapter.kt

@@ -0,0 +1,67 @@
+package com.wdkl.app.ncs.callingbed2.adapter
+
+import android.content.Context
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.ncs.android.middleware.model.EventHistoryItem
+
+class EventHistoryAdapter : RecyclerView.Adapter<EventHistoryAdapter.ViewHolder>{
+    private var context: Context
+    private var data: ArrayList<EventHistoryItem>
+
+    constructor(context: Context, data: ArrayList<EventHistoryItem>): super() {
+        this.context = context
+        this.data = data
+    }
+
+    fun updateData(data: ArrayList<EventHistoryItem>) {
+        this.data = data
+        notifyDataSetChanged()
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_event_history, parent, false)
+        val viewHolder = ViewHolder(view)
+
+        return viewHolder
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        holder.roomName.text = data.get(position).room
+        holder.customName.text = data.get(position).customer
+        holder.actionTime.text = data.get(position).endTime
+        holder.actionName.text = data.get(position).actionData
+        holder.actionState.text = data.get(position).actionState
+    }
+
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+
+
+    class ViewHolder: RecyclerView.ViewHolder {
+        var customImg : ImageView
+        var callStateImg : ImageView
+        var roomName : TextView
+        var customName : TextView
+        var actionTime : TextView
+        var actionName : TextView
+        var actionState : TextView
+
+        constructor(itemView: View): super(itemView) {
+            customImg = itemView.findViewById(R.id.iv_custom)
+            callStateImg = itemView.findViewById(R.id.iv_call_state)
+            roomName = itemView.findViewById(R.id.tv_room_name)
+            customName = itemView.findViewById(R.id.tv_customer_name)
+            actionTime = itemView.findViewById(R.id.tv_action_time)
+            actionName = itemView.findViewById(R.id.tv_action_name)
+            actionState = itemView.findViewById(R.id.tv_action_state)
+        }
+    }
+}

+ 60 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/adapter/NurseConfigAdpter.kt

@@ -0,0 +1,60 @@
+package com.wdkl.app.ncs.callingbed2.adapter
+
+import android.content.Context
+import android.graphics.Color
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.ncs.android.middleware.model.dto.NurseConfigDto
+import java.lang.Exception
+
+class NurseConfigAdpter : RecyclerView.Adapter<NurseConfigAdpter.ViewHolder> {
+    private var context: Context
+    private var data: ArrayList<NurseConfigDto>
+
+    constructor(context: Context, data: ArrayList<NurseConfigDto>) {
+        this.context = context
+        this.data = data
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
+        val view = LayoutInflater.from(parent?.context).inflate(R.layout.item_nurse_config, parent, false)
+        val viewHolder = ViewHolder(view)
+
+        return viewHolder
+    }
+
+    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+        try {
+            holder.nurseColor.setBackgroundColor(Color.parseColor("#"+data.get(position).nurseColorRbg))
+            holder.nurseName.setText(data.get(position).nurseConfigName)
+            holder.nurseValue.setText(data.get(position).nurseOptionName)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    fun updateData(data: ArrayList<NurseConfigDto>) {
+        this.data = data
+        notifyDataSetChanged()
+    }
+
+    class ViewHolder: RecyclerView.ViewHolder {
+        var nurseColor : TextView
+        var nurseName : TextView
+        var nurseValue : TextView
+
+        constructor(itemView: View): super(itemView) {
+            nurseColor = itemView.findViewById(R.id.tv_nurse_color)
+            nurseName = itemView.findViewById(R.id.tv_nurse_config_name)
+            nurseValue = itemView.findViewById(R.id.tv_nurse_config_value)
+        }
+    }
+}

+ 12 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/agreement/Callingbed2Agreement.kt

@@ -0,0 +1,12 @@
+package com.wdkl.app.ncs.callingbed2.agreement
+
+import com.wdkl.ncs.android.middleware.model.dto.NurseConfigDto
+
+interface Callingbed2Agreement {
+
+    //更新护理数据
+    fun updateNurseConfig(list: List<NurseConfigDto>)
+
+    //检查APP版本
+    fun checkAppVersion()
+}

+ 24 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/di/Callingbed2Component.kt

@@ -0,0 +1,24 @@
+package com.wdkl.app.ncs.callingbed2.di
+
+import com.wdkl.app.ncs.callingbed2.activity.AppUpdateActivity
+import com.wdkl.app.ncs.callingbed2.activity.Callingbed2Activity
+import com.wdkl.app.ncs.callingbed2.fragment.*
+import com.wdkl.ncs.android.middleware.di.ApplicationComponent
+import dagger.Component
+
+@Component(dependencies = arrayOf(ApplicationComponent::class))
+interface Callingbed2Component {
+    fun inject(activity: Callingbed2Activity)
+
+    fun inject(activity: AppUpdateActivity)
+
+    fun inject(activity: MainFragment)
+
+    fun inject(activity: CallFragment)
+
+    fun inject(activity: VoiceMsgFragment)
+
+    fun inject(activity: QrCodeFragment)
+
+    fun inject(activity: CostFragment)
+}

+ 148 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/BaseCallFragment.kt

@@ -0,0 +1,148 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.os.Bundle
+import android.os.CountDownTimer
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.enation.javashop.utils.base.tool.BaseToolActivity
+import com.wdkl.app.ncs.callingbed2.helper.RingPlayHelper
+import com.wdkl.app.ncs.callingbed2.helper.SerialPortHelper
+import com.wdkl.app.ncs.callingbed2.settings.SettingConfig
+import com.wdkl.core.voip.VoipEvent
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.skywebrtc.SkyEngineKit
+import com.wdkl.skywebrtc.except.NotInitializedException
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+abstract class BaseCallFragment: Fragment() {
+
+    private var layout: View? = null
+
+    protected lateinit var baseActivity: BaseToolActivity
+
+    //通话状态:0-去电, 1-来电, 2-探视
+    protected var callState : Int = 0
+    protected var tcpModel: TcpModel? = null
+
+    //计时器
+    lateinit var countDownTimer: CountDownTimer
+
+    protected var gEngineKit: SkyEngineKit? = null
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        retainInstance = true
+        callState = arguments.getInt("call_state")
+        if (arguments.getSerializable("tcp_model") != null) {
+            tcpModel = arguments.getSerializable("tcp_model") as TcpModel
+        }
+    }
+
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
+        if (layout == null) {
+            layout = inflater.inflate(getLayId(), null)
+        }
+
+        /**初始化宿主Activity*/
+        baseActivity = getActivity() as BaseToolActivity
+
+        return layout
+    }
+
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        try {
+            SkyEngineKit.init(VoipEvent())
+            gEngineKit = SkyEngineKit.Instance()
+        } catch (e: NotInitializedException) {
+            SkyEngineKit.init(VoipEvent())
+            try {
+                gEngineKit = SkyEngineKit.Instance()
+            } catch (ex: NotInitializedException) {
+                ex.printStackTrace()
+                baseActivity.finish()
+            }
+        }
+
+        init()
+        bindEvent()
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        destroy()
+    }
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+        super.onStop()
+    }
+
+    protected abstract fun getLayId(): Int
+
+    protected abstract fun init()
+
+    protected abstract fun bindEvent()
+
+    protected abstract fun destroy()
+
+    //初始化计时器
+    protected fun initCountDownTimer(view: TextView) {
+        val overTime = SettingConfig.getSipOverTime(baseActivity) * 1000L
+        countDownTimer = object: CountDownTimer(overTime, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+                if (view != null) {
+                    val time = millisUntilFinished/1000
+                    view.setText("倒计时: " + time + "秒")
+                }
+            }
+
+            override fun onFinish() {
+                //呼叫超时,返回到主界面
+                RingPlayHelper.stopRingTone()
+                showMessage("无人应答...")
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                SerialPortHelper.setCallStatus("0")
+                backToMain()
+            }
+        }
+    }
+
+    //开始计时
+    protected fun startTimer() {
+        if (countDownTimer != null) {
+            countDownTimer.start()
+        }
+    }
+
+    //取消计时器
+    protected fun cancelTimer() {
+        if (countDownTimer != null) {
+            countDownTimer.cancel()
+        }
+    }
+
+    //返回主界面
+    protected fun backToMain() {
+        EventBus.getDefault().post(MessageEvent("BackCall", Constant.EVENT_REMOVE_CALL_FRAGMENT))
+    }
+
+}

+ 362 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/CallFragment.kt

@@ -0,0 +1,362 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.os.CountDownTimer
+import android.os.SystemClock
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import com.enation.javashop.net.engine.model.NetState
+import com.google.gson.Gson
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.activity.Callingbed2Activity
+import com.wdkl.app.ncs.callingbed2.agreement.Callingbed2Agreement
+import com.wdkl.app.ncs.callingbed2.databinding.VoiceCallLayBinding
+import com.wdkl.app.ncs.callingbed2.helper.MediaPlayHelper
+import com.wdkl.app.ncs.callingbed2.helper.SoundPoolManager
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+import com.wdkl.app.ncs.callingbed2.settings.SettingConfig
+//import com.wdkl.app.ncs.sip.event.AEvent
+//import com.wdkl.app.ncs.sip.event.IEventListener
+//import com.wdkl.app.ncs.sip.helper.StarRtcHelper
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.debugLog
+import com.wdkl.ncs.android.lib.utils.errorLog
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed2.CallFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.MainFragmentPresenter
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import kotlinx.android.synthetic.main.voice_call_lay.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class CallFragment : BaseFragment<MainFragmentPresenter, VoiceCallLayBinding>(), CallFragmentContract.View/*, IEventListener*/ {
+    val TAG = "CallFragment"
+
+    //通话状态:0-去电, 1-来电, 2-接通, 3-挂断
+    var callState : Int = 0
+    //呼叫倒计时
+    lateinit var countDownTimer: CountDownTimer
+    //来电设备id
+    var fromId: Int = -1
+    //目的设备id
+    var toId: Int = -1
+    //Sip通话目标设备id
+    var sipTargetId: String = ""
+    //Interaction ID
+    var interactionId: Int = -1
+    //去电铃声id
+    //var outCallStreamId: Int = -1
+    //来电铃声id
+    //var inCallStreamId: Int = -1
+
+
+    override fun getLayId(): Int {
+        return R.layout.voice_call_lay
+    }
+
+    override fun bindDagger() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        initCountDownTimer()
+        //StarRtcHelper.getInstance().addAudioCallListeners(this)
+
+        when (callState) {
+            0 -> {
+                //去电
+                sendCall()
+                VoiceUtil.startAudioCall(Constant.DEVICE_ID)
+                //播放铃音
+                MediaPlayHelper.getInstance().playResMusic(R.raw.outgoing_call, 0.6f, true)
+            }
+
+            1 -> {
+                //来电
+                incomingCall()
+                //播放铃声
+                MediaPlayHelper.getInstance().playResMusic(R.raw.incoming_call, 1.0f, true)
+            }
+
+            2 -> {
+                //接通
+                //acceptCall()
+            }
+
+            3 -> {
+                //挂断
+                //rejectCall()
+            }
+        }
+    }
+
+    override fun bindEvent() {
+        //去电或正在通话界面挂断按钮
+        voice_call_hangup.setOnClickListener {
+            //挂断通话,返回首页
+            if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                //StarRtcHelper.getInstance().hangupAudioCall()
+                VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, interactionId)
+                backToMain()
+            } else {
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                cancelCall()
+            }
+        }
+        //来电界面挂断按钮
+        voice_call_ring_hangoff.setOnClickListener {
+            //挂断通话,返回首页
+            MediaPlayHelper.getInstance().stopMusic()
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, fromId, interactionId)
+            backToMain()
+        }
+        //来电界面接听按钮
+        voice_call_ring_pickup_audio.setOnClickListener {
+            //接收通话,向主叫方发出已接听的TCP消息,主叫方接收该消息后向我发起sip通话请求,我自动接听sip通话
+            Log.d("wzlll", "pickup call state: " + Constant.CALL_STATE + ", my id: " + Constant.DEVICE_ID + ", from id: " + fromId + ", interaction id: " + interactionId + ", target sip: " + sipTargetId)
+            MediaPlayHelper.getInstance().stopMusic()
+            //if (!TextUtils.isEmpty(sipTargetId)) {
+                Constant.CALL_STATE = Constant.CALL_INCOMING
+                VoiceUtil.acceptAudioCall(Constant.DEVICE_ID, fromId, interactionId)
+            //}
+        }
+    }
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+        super.onStop()
+    }
+
+    override fun destory() {
+        countDownTimer.cancel()
+        if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+            //StarRtcHelper.getInstance().hangupAudioCall()
+            VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, interactionId)
+        }
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        //StarRtcHelper.getInstance().removeAudioCallListeners(this)
+        MediaPlayHelper.getInstance().stopMusic()
+    }
+
+    override fun onError(message: String, type: Int) {
+        getUtils().dismissDialog()
+        errorLog("error",message)
+        showMessage(message)
+    }
+
+    override fun complete(message: String, type: Int) {
+        getUtils().dismissDialog()
+    }
+
+    override fun start() {
+        getUtils().showDialog()
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+
+/*    override fun dispatchEvent(aEventID: String, success: Boolean, eventObj: Any) {
+        Log.d("wzlll", "call state: " + Constant.CALL_STATE)
+        Log.d("wzlll", "received sip event: " + aEventID + ", success: " + success + ", event obj: " + eventObj.toString())
+        when (aEventID) {
+            AEvent.AEVENT_VOIP_REV_CALLING_AUDIO -> {
+                //分机呼叫主机,主机接听,同时发送sip通话请求,分机自动接听
+                if (Constant.CALL_STATE != Constant.CALL_CALLING) {
+                    MediaPlayHelper.getInstance().stopMusic()
+                    StarRtcHelper.getInstance().pickupAudioCall(activity, eventObj.toString())
+                    activity.runOnUiThread {
+                        acceptCall()
+                    }
+                }
+            }
+            AEvent.AEVENT_VOIP_REV_CALLING -> {
+                //语音通话请求
+                //if (Constant.CALL_STATE != Constant.CALL_CALLING) {
+                //    StarRtcHelper.getInstance().pickupAudioCall(activity, eventObj.toString())
+                //    activity.runOnUiThread {
+                //                        acceptCall()
+                //                    }
+                //}
+            }
+            AEvent.AEVENT_VOIP_REV_BUSY -> {
+                //对方线路忙
+                activity.runOnUiThread {
+                    //rejectCall()
+                    showMessage("线路忙!")
+                }
+            }
+
+            AEvent.AEVENT_VOIP_REV_REFUSED -> {
+                //对方拒绝通话
+                activity.runOnUiThread {
+                    //rejectCall()
+                }
+            }
+
+            AEvent.AEVENT_VOIP_REV_HANGUP -> {
+                //对方已挂断
+                activity.runOnUiThread {
+                    if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                        rejectCall()
+                    }
+                }
+            }
+
+            AEvent.AEVENT_VOIP_REV_CONNECT -> {
+                //对方接受通话
+                activity.runOnUiThread {
+                    //showMessage("通话成功!")
+                    //acceptCall()
+                }
+            }
+
+            AEvent.AEVENT_VOIP_REV_ERROR -> {
+                //通话错误
+                activity.runOnUiThread {
+                    //showMessage("通话错误!")
+                    rejectCall()
+                }
+            }
+        }
+    }*/
+
+    fun initCountDownTimer() {
+        val overTime = SettingConfig.getSipOverTime(getActivity()) * 1000L
+        countDownTimer = object: CountDownTimer(overTime, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+                val time = millisUntilFinished/1000
+                voice_call_timeout?.setText("呼叫倒计时: " + time + " 秒")
+            }
+
+            override fun onFinish() {
+                //呼叫超时,返回到主界面
+                showMessage("无人应答...")
+                MediaPlayHelper.getInstance().stopMusic()
+                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                backToMain()
+            }
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+            Constant.EVENT_TCP_MSG -> {
+                val tcpModel = messageEvent.message as TcpModel
+                if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) { //我方呼出,对方接受
+                    //todo: 通话中界面更新;建立数据通话
+                    val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                    interactionId = interactionVO.id
+                    fromId = tcpModel.fromId
+                    //acceptCall()
+                } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) { //我方呼出,对方拒绝
+                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方拒绝并停留3秒,结束至正常界面;更新左侧
+                    showMessage("对方已拒绝!")
+                    rejectCall()
+                } else if (tcpModel.getAction() == TcpAction.VoiceAction.CALLING) { //我方呼出,对方通话中
+                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方占线并停留3秒,结束至正常界面;更新左侧
+                    //对于分机来说,对方处于通话中应当还是可以正常呼叫,而不应该直接挂断
+                    //showMessage("对方处于通话中!")
+                    //rejectCall()
+                } else if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) { //呼叫成功
+                    val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                    interactionId = interactionVO.id
+                    voice_call_calling_text.setText("呼叫成功,等待接听中...")
+                } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) { //我方呼出,对方不在线,设备离线或其它错误
+                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方离线并停留3秒,结束至正常界面;更新左侧
+                    showMessage("呼叫失败,对方可能不在线!")
+                    MediaPlayHelper.getInstance().stopMusic()
+                    rejectCall()
+                } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) { //对方挂断,不论我方呼出或呼入
+                    //todo: 清掉呼出的TcpModel,通话中界面更新 --- 显示对方已挂断并停留3秒,结束至正常界面;更新左侧
+                    //showMessage("通话结束!")
+                    MediaPlayHelper.getInstance().stopMusic()
+                    rejectCall()
+                } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+                    //对方呼叫时取消
+                    MediaPlayHelper.getInstance().stopMusic()
+                    cancelCall()
+                }
+            }
+        }
+    }
+
+    //呼叫
+    fun sendCall() {
+        Constant.CALL_STATE = Constant.CALL_OUTGOING
+        voice_call_calling_text.setText("正在呼叫...")
+        voice_call_hangup_view.visibility = View.VISIBLE
+        voice_call_ring_view.visibility = View.GONE
+        voice_call_timer.visibility = View.GONE
+        voice_call_timeout.visibility = View.VISIBLE
+        countDownTimer.start()
+    }
+
+    //来电
+    fun incomingCall() {
+        Constant.CALL_STATE = Constant.CALL_INCOMING
+        countDownTimer.cancel()
+        voice_call_calling_text.setText("有新的来电...")
+        voice_call_hangup_view.visibility = View.GONE
+        voice_call_ring_view.visibility = View.VISIBLE
+        voice_call_timer.visibility = View.GONE
+        voice_call_timeout.visibility = View.GONE
+    }
+
+    //接通
+    fun acceptCall() {
+        Constant.CALL_STATE = Constant.CALL_CALLING
+        countDownTimer.cancel()
+        voice_call_calling_text.setText("通话中...")
+        voice_call_hangup_view.visibility = View.VISIBLE
+        voice_call_ring_view.visibility = View.GONE
+        voice_call_timer.visibility = View.VISIBLE
+        voice_call_timeout.visibility = View.GONE
+        voice_call_timer.base = SystemClock.elapsedRealtime()
+        voice_call_timer.start()
+    }
+
+    //挂断通话
+    fun rejectCall() {
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        countDownTimer.cancel()
+        voice_call_timer.base = SystemClock.elapsedRealtime()
+        voice_call_timer.stop()
+        backToMain()
+    }
+
+    //呼叫取消
+    fun cancelCall() {
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        countDownTimer.cancel()
+        voice_call_timer.base = SystemClock.elapsedRealtime()
+        voice_call_timer.stop()
+        backToMain()
+    }
+
+    private fun backToMain() {
+        EventBus.getDefault().post(MessageEvent("BackToMain", Constant.EVENT_BACK_MAIN))
+    }
+}

+ 75 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/CostFragment.kt

@@ -0,0 +1,75 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.support.v7.widget.LinearLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.adapter.CostItemAdapter
+import com.wdkl.app.ncs.callingbed2.databinding.CostViewLayBinding
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.errorLog
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed2.CostFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.CostFragmentPresenter
+import com.wdkl.ncs.android.middleware.model.vo.CustomerFeeConfigByGroupNameVO
+import kotlinx.android.synthetic.main.cost_view_lay.*
+
+class CostFragment : BaseFragment<CostFragmentPresenter, CostViewLayBinding>(), CostFragmentContract.View {
+
+    override fun getLayId(): Int {
+        return R.layout.cost_view_lay
+    }
+
+    override fun bindDagger() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        if (Constant.CUSTOM_ID != -1) {
+            presenter.loadCustomCost(Constant.CUSTOM_ID)
+        } else {
+            showMessage("没有custom id")
+        }
+    }
+
+    override fun bindEvent() {
+        //
+    }
+
+    override fun destory() {
+        //
+    }
+
+    override fun showCustomCost(cost: ArrayList<CustomerFeeConfigByGroupNameVO>) {
+        val layoutManager = LinearLayoutManager(activity)
+        rv_cost_main_view.layoutManager = layoutManager
+        rv_cost_main_view.adapter = CostItemAdapter(activity, cost)
+    }
+
+    override fun onError(message: String, type: Int) {
+        errorLog("error",message)
+        showMessage(message)
+    }
+
+    override fun onNoNet() {
+        showMessage("没有网络")
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+}

+ 142 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/MainFragment.kt

@@ -0,0 +1,142 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.text.TextUtils
+import android.text.method.ScrollingMovementMethod
+import android.view.View
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.activity.Callingbed2Activity
+import com.wdkl.app.ncs.callingbed2.databinding.MainView2LayoutBinding
+import com.wdkl.app.ncs.callingbed2.helper.HttpHelper
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.TimeHandle
+import com.wdkl.ncs.android.lib.utils.debugLog
+import com.wdkl.ncs.android.lib.utils.errorLog
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed2.MainFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.MainFragmentPresenter
+import com.wdkl.ncs.android.middleware.model.dos.EventDO
+import com.wdkl.ncs.android.middleware.model.vo.CustomerInfoVO
+import kotlinx.android.synthetic.main.main_view2_layout.*
+import kotlinx.android.synthetic.main.view_bed_name.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class MainFragment: BaseFragment<MainFragmentPresenter, MainView2LayoutBinding>(), MainFragmentContract.View {
+    val TAG = "MainFragment"
+
+
+    override fun getLayId(): Int {
+        return R.layout.main_view2_layout
+    }
+
+    override fun bindDagger() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        //医嘱内容可以滚动显示
+        tv_order_content.movementMethod = ScrollingMovementMethod.getInstance()
+
+        updateInfo()
+    }
+
+    override fun bindEvent() {
+    }
+
+    override fun destory() {
+    }
+
+    override fun showCustomInfo(customInfo: CustomerInfoVO) {
+        //隐藏空页面并展示入住信息
+        ll_empty.visibility = View.GONE
+        ll_custom_view.visibility = View.VISIBLE
+        //床位名称
+        tv_bed_name.text = Constant.BED_NAME
+
+        //显示入住信息
+        tv_custom_name.text = customInfo.named
+        if (customInfo.sex == 0) {
+            tv_custom_gender.setBackgroundResource(R.mipmap.ic_gender_female)
+        } else if (customInfo.sex == 1) {
+            tv_custom_gender.setBackgroundResource(R.mipmap.ic_gender_male)
+        }
+        tv_custom_age.text = "" + customInfo.age + customInfo.ageUnit
+        if (customInfo.inDate != null) {
+            tv_custom_indate.text = "入院日期: " + TimeHandle.getDateTime(customInfo.inDate * 1000, "yyyy.MM.dd")
+        }
+        if (TextUtils.isEmpty(customInfo.advice)) {
+            tv_order_content.text = "暂无"
+        } else {
+            tv_order_content.text = customInfo.advice
+        }
+
+        tv_doctor_name.text = customInfo.doctorName
+        tv_nurse_name.text = customInfo.nurseName
+
+        //更新护理项
+        if (customInfo.list != null) {
+            (activity as Callingbed2Activity).updateNurseConfig(customInfo.list)
+        }
+    }
+
+    override fun onError(message: String, type: Int) {
+        errorLog("error",message)
+        showMessage(message)
+    }
+
+    override fun onNoNet() {
+        showMessage("没有网络")
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+
+    override fun onStart() {
+        EventBus.getDefault().register(this)
+        super.onStart()
+    }
+
+    override fun onStop() {
+        EventBus.getDefault().unregister(this)
+        super.onStop()
+    }
+
+    fun updateInfo() {
+        if (!TextUtils.isEmpty(Constant.BED_NAME)) {
+            tv_bed_name.text = Constant.BED_NAME
+        }
+
+        if (Constant.CUSTOM_ID != -1) {
+            ll_empty.visibility = View.GONE
+            presenter.loadCustomInfo(Constant.CUSTOM_ID)
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.getType()) {
+            Constant.EVENT_UPDATE_CUSTOM -> {
+                updateInfo()
+            }
+        }
+    }
+}

+ 108 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/QrCodeFragment.kt

@@ -0,0 +1,108 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.graphics.BitmapFactory
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.app.ncs.callingbed2.BuildConfig
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.activity.Callingbed2Activity
+import com.wdkl.app.ncs.callingbed2.databinding.QrCodeLayBinding
+import com.wdkl.app.ncs.callingbed2.helper.AppUpdateHelper
+import com.wdkl.app.ncs.callingbed2.helper.NetHelper
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.*
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.logic.contract.callingbed2.QrCodeFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.QrCodeFragmentPresenter
+import kotlinx.android.synthetic.main.qr_code_lay.*
+import kotlinx.android.synthetic.main.view_bed_name.*
+import java.lang.StringBuilder
+
+class QrCodeFragment : BaseFragment<QrCodeFragmentPresenter, QrCodeLayBinding>(), QrCodeFragmentContract.View {
+    val TAG = "QrCodeFragment"
+
+    val QR_CODE_PATH = "http://m.wdklian.com/care/apk/care.user?type=NCS_DEVICE"
+
+    override fun getLayId(): Int {
+        return R.layout.qr_code_lay
+    }
+
+    override fun bindDagger() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        tv_bed_name.text = Constant.BED_NAME
+
+        Thread{
+            //val logoBitmap = BitmapFactory.decodeResource(resources, R.mipmap.erlogo)
+            var builder = StringBuilder()
+            builder.append(QR_CODE_PATH)
+            builder.append("&code=")
+            builder.append(Constant.DEVICE_CODE)
+            builder.append("&mac=")
+            builder.append(Constant.LOCAL_MAC)
+            builder.append("&model=")
+            builder.append(Constant.DEVICE_MODEL)
+            builder.append("&hard_ver=")
+            builder.append(Constant.DEVICE_HARD_VER)
+            builder.append("&soft_ver=")
+            builder.append(Constant.DEVICE_SOFT_VER)
+            builder.append("&device_type=")
+            builder.append(Constant.DEVICE_TYPE)
+            builder.append("&device_name=")
+            builder.append(Constant.DEVICE_NAME)
+            val code = EcodeHelper().createQRImage(builder.toString(),200, null)
+            activity.runOnUiThread {
+                view_qr_code?.setImageBitmap(code)
+            }
+        }.start()
+        val macAddr = NetHelper.getInstance().macAddress
+        val ipAddr = NetHelper.getInstance().localIP
+        tv_device_id.text = "设备ID: " + Constant.DEVICE_ID
+        tv_local_ip.text = "本机IP: " + ipAddr
+        tv_local_mac.text = "本机MAC: " + macAddr
+        tv_app_version.text = "版本信息: V" + BuildConfig.VERSION_NAME
+        tv_mcu_version.text = "MCU版本: " + Constant.MCU_VERSION_NUMBER
+    }
+
+    override fun bindEvent() {
+        tv_btn_update_app.setOnClickListener {
+            (activity as Callingbed2Activity).checkAppVersion()
+        }
+
+        tv_btn_reboot.setOnClickListener {
+            AppUpdateHelper.reboot(context)
+        }
+    }
+
+    override fun destory() {
+    }
+
+    override fun onError(message: String, type: Int) {
+        getUtils().dismissDialog()
+        errorLog("error",message)
+        showMessage(message)
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+
+    override fun setUrlString(url: String) {
+        //
+    }
+}

+ 519 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/SkyCallFragment.kt

@@ -0,0 +1,519 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.os.Handler
+import android.os.Looper
+import android.os.SystemClock
+import android.util.Log
+import android.view.View
+import android.view.ViewGroup
+import com.google.gson.Gson
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.helper.RingPlayHelper
+import com.wdkl.app.ncs.callingbed2.helper.SerialPortHelper
+import com.wdkl.core.socket.SocketManager
+import com.wdkl.ncs.android.lib.utils.AppTool
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constant
+import com.wdkl.ncs.android.middleware.common.MessageEvent
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.channel.VideoUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.skywebrtc.CallSession
+import com.wdkl.skywebrtc.EnumType
+import kotlinx.android.synthetic.main.sky_voice_call_layout.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import org.webrtc.SurfaceViewRenderer
+import java.util.*
+
+class SkyCallFragment: BaseCallFragment(), CallSession.CallSessionCallback {
+
+    //来电设备id
+    var fromId: Int = -1
+
+    private var interactionVO: InteractionVO? = null
+
+    private var localSurfaceView: SurfaceViewRenderer? = null
+    private var remoteSurfaceView: SurfaceViewRenderer? = null
+
+    private val handler = Handler(Looper.getMainLooper())
+
+    private var visiting: Boolean = false
+
+    private var audioCall: Boolean = false
+
+    override fun getLayId(): Int {
+        return R.layout.sky_voice_call_layout
+    }
+
+    override fun init() {
+        //初始化计时器
+        initCountDownTimer(sky_voice_call_timeout)
+        //tcp参数
+        if (tcpModel != null) {
+            fromId = tcpModel!!.fromId
+            interactionVO = Gson().fromJson(tcpModel!!.data.toString(), InteractionVO::class.java)
+        }
+
+        when (callState) {
+            0 -> {
+                //去电
+                startOutgoing()
+                RingPlayHelper.playRingTone(baseActivity, R.raw.ring_back2, true)
+            }
+
+            1 -> {
+                //来电
+                showIncomingCall()
+                RingPlayHelper.playRingTone(baseActivity, R.raw.incoming_call, true)
+            }
+
+            2 -> {
+                //探视
+                visiting = true
+                acceptCall()
+                //因为探视请求是由探视机发起的,所以在每一条的探视交互记录中的from为探视机
+                startVisiting(interactionVO!!.fromSipId)
+            }
+        }
+    }
+
+    override fun bindEvent() {
+        //去电取消或通话挂断
+        sky_voice_call_hangup.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            if (Constant.CALL_STATE == Constant.CALL_CALLING) {
+                //结束sip通话
+                val session = gEngineKit?.getCurrentSession()
+                if (session != null) {
+                    session.leave()
+                }
+
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                sky_voice_call_timer.stop()
+                backToMain()
+            } else {
+                Constant.CALL_STATE = Constant.CALL_STANDBY
+                VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                cancelCall()
+            }
+        }
+
+        //来电拒绝
+        sky_voice_call_ring_reject.setOnClickListener {
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+            backToMain()
+        }
+
+        //来电接听
+        sky_voice_call_ring_pickup_audio.setOnClickListener {
+            acceptCall()
+            RingPlayHelper.stopRingTone()
+            Constant.CALL_STATE = Constant.CALL_INCOMING
+            VoiceUtil.acceptAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+        }
+    }
+
+    override fun destroy() {
+        cancelTimer()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        RingPlayHelper.stopRingTone()
+        SerialPortHelper.setCallStatus("0")
+    }
+
+    private fun startOutgoing() {
+        VoiceUtil.startAudioCall(Constant.DEVICE_ID)
+        Constant.CALL_STATE = Constant.CALL_OUTGOING
+        sky_voice_call_timeout.visibility = View.VISIBLE
+        sky_voice_call_timer.visibility = View.GONE
+        startTimer()
+    }
+
+    //去电界面
+    private fun showOutgoingCall() {
+        Constant.CALL_STATE = Constant.CALL_OUTGOING
+        sky_voice_call_calling_text.text = "呼叫成功,等待接听..."
+        sky_voice_call_outgoing.visibility = View.VISIBLE
+        sky_voice_call_incoming.visibility = View.GONE
+        sky_voice_call_timeout.visibility = View.VISIBLE
+        sky_voice_call_timer.visibility = View.GONE
+        startTimer()
+        SerialPortHelper.setCallStatus("1")
+    }
+
+    //来电界面
+    private fun showIncomingCall() {
+        Constant.CALL_STATE = Constant.CALL_INCOMING
+        sky_voice_call_calling_text.text = "有新来电..."
+        sky_voice_call_outgoing.visibility = View.GONE
+        sky_voice_call_incoming.visibility = View.VISIBLE
+        sky_voice_call_timeout.visibility = View.GONE
+        sky_voice_call_timer.visibility = View.GONE
+        cancelTimer()
+        SerialPortHelper.setCallStatus("1")
+    }
+
+    //开始接听
+    private fun acceptCall() {
+        sky_voice_call_calling_text.text = "连接中..."
+        sky_voice_call_outgoing.visibility = View.VISIBLE
+        sky_voice_call_incoming.visibility = View.GONE
+        sky_voice_call_timeout.visibility = View.GONE
+        sky_voice_call_timer.visibility = View.GONE
+        cancelTimer()
+    }
+
+    //呼叫取消
+    private fun cancelCall() {
+        cancelTimer()
+        Constant.CALL_STATE = Constant.CALL_STANDBY
+        sky_voice_call_timer.stop()
+        SerialPortHelper.setCallStatus("0")
+        backToMain()
+    }
+
+    //语音接通
+    private fun joinAudioCall() {
+        val session = gEngineKit?.getCurrentSession()
+        if (session != null) {
+            Log.e("dds", "audio call session state: " + session.state)
+            session.setSessionCallback(this)
+        }
+        if (session != null && session.state == EnumType.CallState.Incoming) {
+            session.joinHome(session.roomId)
+            session.toggleSpeaker(true)
+        }
+    }
+
+    //视频接通
+    private fun joinVideoCall() {
+        val session = gEngineKit?.getCurrentSession()
+        if (session != null) {
+            Log.e("dds", "video call session state: " + session.state)
+            session.setSessionCallback(this)
+        }
+        if (session != null && session.state == EnumType.CallState.Incoming) {
+            val surfaceView = gEngineKit!!.currentSession.setupLocalVideo(false)
+            if (surfaceView != null) {
+                localSurfaceView = surfaceView as SurfaceViewRenderer
+                localSurfaceView!!.setZOrderMediaOverlay(false)
+                fullscreen_video_frame.addView(localSurfaceView)
+            }
+
+            session.joinHome(session.roomId)
+            session.toggleSpeaker(true)
+        }
+    }
+
+    private fun showCalling(audioOnly: Boolean) {
+        if (audioOnly) {
+            //移除视频画面
+            fullscreen_video_frame.visibility = View.GONE
+            pip_video_frame.visibility = View.GONE
+            ll_voice_call.visibility = View.VISIBLE
+        } else {
+            //显示视频画面
+            fullscreen_video_frame.visibility = View.VISIBLE
+            pip_video_frame.visibility = View.VISIBLE
+            ll_voice_call.visibility = View.GONE
+        }
+
+        Constant.CALL_STATE = Constant.CALL_CALLING
+        sky_voice_call_calling_text.text = "通话中..."
+        sky_voice_call_timeout.visibility = View.GONE
+        cancelTimer()
+        sky_voice_call_timer.visibility = View.VISIBLE
+        sky_voice_call_timer.base = SystemClock.elapsedRealtime()
+        sky_voice_call_timer.start()
+        SerialPortHelper.setCallStatus("2")
+    }
+
+    //创建会话
+    private fun startCall(targetId: String, audioOnly: Boolean): Boolean {
+        audioCall = audioOnly
+        val room = UUID.randomUUID().toString() + System.currentTimeMillis()
+        val b = gEngineKit!!.startOutCall(baseActivity, room, targetId, audioOnly)
+        if (b) {
+            val session = gEngineKit!!.currentSession
+            if (session == null) {
+                return false
+            } else {
+                session.setSessionCallback(this)
+                session.toggleSpeaker(true)
+            }
+        }
+        return b
+    }
+
+    //通话结束
+    private fun callEnd() {
+        Log.e("dds", "call end !!!!!!!!!!!!!!!!!!")
+        if (visiting) {
+            VideoUtil.handoffVideoCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+        } else {
+            VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+        }
+
+        if (sky_voice_call_timer != null) {
+            sky_voice_call_timer.stop()
+        }
+        if (gEngineKit != null && gEngineKit!!.currentSession != null && gEngineKit!!.currentSession.state != EnumType.CallState.Idle) {
+            gEngineKit!!.endCall()
+        }
+        backToMain()
+    }
+
+    //探视
+    private fun startVisiting(targetId: String) {
+        if (!startCall(targetId, false)) {
+            //通话失败,重置并返回主界面
+            Constant.CALL_STATE = Constant.CALL_STANDBY
+            VideoUtil.rejectVideoCall(Constant.DEVICE_ID, fromId, interactionVO!!.id)
+            if (sky_voice_call_timer != null) {
+                sky_voice_call_timer.stop()
+            }
+            backToMain()
+        }
+    }
+
+
+    /********************************************************
+     ********************* webrtc通话回调 ********************
+     * 注意: 如涉及到UI更新的需要在主线程处理,务必注意
+     *******************************************************/
+    override fun didChangeState(state: EnumType.CallState?) {
+        Log.e("dds", "didChangeState: " + state)
+        handler.post {
+            if (state == EnumType.CallState.Connected) {
+                //更新界面显示
+                showCalling(audioCall)
+            }
+        }
+    }
+
+    override fun didDisconnected(userId: String?) {
+        handler.post {
+            callEnd()
+        }
+    }
+
+    override fun didError(error: String?) {
+        handler.post {
+            callEnd()
+        }
+    }
+
+    //处理本地视频画面
+    override fun didCreateLocalVideoTrack() {
+        Log.e("dds", "didCreateLocalVideoTrack")
+        handler.post {
+            if (localSurfaceView == null) {
+                val surfaceView = gEngineKit!!.currentSession.setupLocalVideo(true)
+                Log.e("dds", "didCreateLocalVideoTrack surfaceView: " + surfaceView)
+                if (surfaceView != null) {
+                    localSurfaceView = surfaceView as SurfaceViewRenderer
+                }
+            }
+        }
+    }
+
+    //处理远端视频画面
+    override fun didReceiveRemoteVideoTrack(userId: String?) {
+        Log.e("dds", "didReceiveRemoteVideoTrack  userId: " + userId)
+        handler.post {
+            //本地画面
+            if (localSurfaceView != null) {
+                localSurfaceView!!.setZOrderMediaOverlay(true)
+                if (localSurfaceView!!.parent != null) {
+                    (localSurfaceView!!.parent as ViewGroup).removeView(localSurfaceView)
+                }
+                pip_video_frame!!.addView(localSurfaceView)
+            }
+
+            //远端画面
+            val surfaceView = gEngineKit!!.currentSession.setupRemoteVideo(userId, false)
+            Log.e("dds", "didReceiveRemoteVideoTrack,surfaceView = $surfaceView")
+            if (surfaceView != null) {
+                remoteSurfaceView = surfaceView as SurfaceViewRenderer
+                fullscreen_video_frame.removeAllViews()
+                if (remoteSurfaceView!!.parent != null) {
+                    (remoteSurfaceView!!.parent as ViewGroup).removeView(remoteSurfaceView)
+                }
+                fullscreen_video_frame.addView(remoteSurfaceView)
+            }
+        }
+    }
+
+    override fun didCallEndWithReason(callEndReason: EnumType.CallEndReason?) {
+        handler.post {
+            when (callEndReason) {
+                EnumType.CallEndReason.Busy -> {
+                    showMessage("对方忙线中")
+                }
+                EnumType.CallEndReason.AcceptByOtherClient -> {
+                    showMessage("通话中")
+                }
+                EnumType.CallEndReason.Hangup -> {
+                    //showMessage("挂断")
+                }
+                EnumType.CallEndReason.MediaError -> {
+                    showMessage("媒体错误")
+                }
+                EnumType.CallEndReason.OpenCameraFailure -> {
+                    showMessage("打开摄像头错误")
+                }
+                EnumType.CallEndReason.RemoteHangup -> {
+                    showMessage("对方挂断")
+                }
+                EnumType.CallEndReason.RemoteSignalError -> {
+                    showMessage("对方网络断开")
+                }
+                EnumType.CallEndReason.SignalError -> {
+                    showMessage("连接断开")
+                }
+                EnumType.CallEndReason.Timeout -> {
+                    showMessage("对方未接听")
+                }
+            }
+
+            callEnd()
+        }
+    }
+
+    override fun didChangeMode(isAudioOnly: Boolean) {
+        handler.post {
+            //
+        }
+    }
+
+    override fun didUserLeave(userId: String?) {
+        handler.post {
+            callEnd()
+        }
+    }
+
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        when (messageEvent.type) {
+            Constant.EVENT_SIP_CALL_STATUS -> {
+                //收到sip通话邀请,加入通话
+                Log.e("dds", "EVENT_SIP_CALL_STATUS: " + messageEvent.message)
+                if (messageEvent.message is String) {
+                    val call = messageEvent.message as String
+                    if (call.equals("video_call")) {
+                        audioCall = false
+                        Handler().postDelayed({
+                            joinVideoCall()
+                        }, 2000)
+                    } else {
+                        audioCall = true
+                        Handler().postDelayed({
+                            joinAudioCall()
+                        }, 1500)
+                    }
+                }
+            }
+
+            Constant.EVENT_TCP_MSG -> {
+                if (messageEvent.message is TcpModel) {
+                    val curTcpModel = messageEvent.message as TcpModel
+                    if (curTcpModel.getType() == TcpType.VOICE) {
+                        val curInteractionVO = Gson().fromJson(curTcpModel.data.toString(), InteractionVO::class.java)
+                        if (curTcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                            //我方呼出,对方接受
+                            RingPlayHelper.stopRingTone()
+                            Constant.interactionId = curInteractionVO.id
+                            fromId = curTcpModel.fromId
+                            acceptCall()
+                            if (!startCall(curInteractionVO.toSipId, Constant.CALL_TYPE == Constant.VOICE_CALL)) {
+                                //通话失败,重置并返回主界面
+                                Constant.CALL_STATE = Constant.CALL_STANDBY
+                                VoiceUtil.handoffAudioCall(Constant.DEVICE_ID, fromId, Constant.interactionId)
+                                if (sky_voice_call_timer != null) {
+                                    sky_voice_call_timer.stop()
+                                }
+                                backToMain()
+                            }
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
+                            //我方呼出,对方拒绝
+                            showMessage("对方已拒绝!")
+                            RingPlayHelper.stopRingTone()
+                            cancelCall()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
+                            //我方呼出,对方通话中
+                            showMessage("对方正在忙线中,暂时无法接听!")
+                            AppTool.Time.delay(2000) {
+                                RingPlayHelper.stopRingTone()
+                                cancelCall()
+                            }
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.SUCCESS) {
+                            //呼叫成功
+                            //本机呼叫的时候tcpmodel为空,只有呼叫成功的时候才能获得对应tcp相关数据
+                            interactionVO = curInteractionVO
+                            Constant.interactionId = curInteractionVO.id
+                            showOutgoingCall()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
+                            //我方呼出,对方不在线,设备离线或其它错误
+                            showMessage("呼叫失败,找不到设备或对方不在线!")
+                            RingPlayHelper.stopRingTone()
+                            cancelCall()
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) {
+                            //对方挂断,不论我方呼出或呼入
+                            if (Constant.interactionId == curInteractionVO.id) {
+                                RingPlayHelper.stopRingTone()
+                                cancelCall()
+                            }
+                        } else if (curTcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+                            //对方呼叫时取消
+                            RingPlayHelper.stopRingTone()
+                            cancelCall()
+                        }
+                    }
+                }
+            }
+
+            //外部呼叫按键
+            Constant.EVENT_SERIAL_EVENT -> {
+                if (messageEvent.message is String) {
+                    val serialAction = messageEvent.message as String
+                    if (serialAction.equals("cancel")) {
+                        RingPlayHelper.stopRingTone()
+                        Constant.CALL_STATE = Constant.CALL_STANDBY
+                        VoiceUtil.cancelAudioCall(Constant.DEVICE_ID)
+                        cancelCall()
+                    } else if (serialAction.equals("accept")) {
+                        RingPlayHelper.stopRingTone()
+                        Constant.CALL_STATE = Constant.CALL_INCOMING
+                        VoiceUtil.acceptAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+                        acceptCall()
+                    } else if (serialAction.equals("handoff")) {
+                        val session = gEngineKit?.getCurrentSession()
+                        if (session != null) {
+                            session.leave()
+                        }
+
+                        Constant.CALL_STATE = Constant.CALL_STANDBY
+                        sky_voice_call_timer.stop()
+                        backToMain()
+                    } else if (serialAction.equals("reject")) {
+                        RingPlayHelper.stopRingTone()
+                        Constant.CALL_STATE = Constant.CALL_STANDBY
+                        VoiceUtil.rejectAudioCall(Constant.DEVICE_ID, fromId, interactionVO?.id)
+                        backToMain()
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 138 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/fragment/VoiceMsgFragment.kt

@@ -0,0 +1,138 @@
+package com.wdkl.app.ncs.callingbed2.fragment
+
+import android.os.SystemClock
+import android.view.MotionEvent
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.app.ncs.callingbed2.R
+import com.wdkl.app.ncs.callingbed2.databinding.VoiceMsgLayBinding
+import com.wdkl.app.ncs.callingbed2.helper.HttpHelper
+import com.wdkl.app.ncs.callingbed2.helper.MediaPlayHelper
+import com.wdkl.app.ncs.callingbed2.helper.RecordHelper
+import com.wdkl.app.ncs.callingbed2.launch.Callingbed2Launch
+import com.wdkl.ncs.android.lib.base.BaseFragment
+import com.wdkl.ncs.android.lib.utils.debugLog
+import com.wdkl.ncs.android.lib.utils.errorLog
+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.callingbed2.VoiceFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.callingbed2.VoiceFragmentPresenter
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.ImUtil
+import kotlinx.android.synthetic.main.voice_msg_lay.*
+import java.io.File
+import javax.inject.Inject
+
+class VoiceMsgFragment @Inject constructor() : BaseFragment<VoiceFragmentPresenter, VoiceMsgLayBinding>(), VoiceFragmentContract.View {
+    val TAG = "VoiceMsgFragment"
+
+    var downTime : Long = 0
+    var upTime : Long = 0
+    var downY : Float = 0f
+    var cancel : Boolean = false
+    lateinit var voiceFile : String
+
+    private val urlManager = UrlManager.build()
+
+    override fun getLayId(): Int {
+        return R.layout.voice_msg_lay
+    }
+
+    override fun bindDagger() {
+        Callingbed2Launch.component.inject(this)
+    }
+
+    override fun init() {
+        //
+    }
+
+    override fun bindEvent() {
+        //debugLog(TAG,"bindEvent")
+        tv_voice_button.setOnTouchListener { v, event ->
+            when(event.action) {
+                MotionEvent.ACTION_DOWN -> {
+                    cancel = false
+                    downTime = System.currentTimeMillis()
+                    downY = event.getY()
+                    RecordHelper.getInstance().startRecord()
+                    tv_voice_text.text = "松开留言,上滑取消"
+                    voice_call_timer.base = SystemClock.elapsedRealtime()
+                    voice_call_timer.start()
+                    voiceFile = RecordHelper.getInstance().audiofilePath
+                }
+
+                MotionEvent.ACTION_MOVE -> {
+                    val moveY = event.getY()
+                    if (downY - moveY > 100) {
+                        cancel = true
+                    }
+                }
+
+                MotionEvent.ACTION_UP -> {
+                    upTime = System.currentTimeMillis()
+                    RecordHelper.getInstance().stopRecord()
+                    tv_voice_text.text = "按住说话"
+                    voice_call_timer.base = SystemClock.elapsedRealtime()
+                    voice_call_timer.stop()
+
+                    if (Math.abs(upTime - downTime) <= 1000) {
+                        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+                        showMessage("说话时间太短")
+                    } else if(cancel) {
+                        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+                        showMessage("语音留言已取消")
+                    } else {
+                        //上传语音留言
+                        val callback = object : HttpHelper.UploadCallback {
+                            override fun onFail() {
+                                activity.runOnUiThread {
+                                    showMessage("留言发送失败!")
+                                }
+                            }
+
+                            override fun onSuccess(data: String) {
+                                activity.runOnUiThread {
+                                    val tcpModel = ImUtil.imMsg(Constant.DEVICE_ID, data)
+                                    TcpClient.getInstance().sendMsg(tcpModel.toJson())
+                                    showMessage("留言发送成功!")
+                                }
+                            }
+                        }
+                        HttpHelper.upload(urlManager.base + Constant.UPLOAD_VOICE_MSG_URL, File(voiceFile), callback)
+                    }
+                }
+            }
+
+            return@setOnTouchListener false
+        }
+    }
+
+    override fun destory() {
+        debugLog(TAG,"destory")
+    }
+
+    override fun onError(message: String, type: Int) {
+        getUtils().dismissDialog()
+        errorLog("error",message)
+        showMessage(message)
+    }
+
+    override fun complete(message: String, type: Int) {
+        getUtils().dismissDialog()
+    }
+
+    override fun start() {
+        getUtils().showDialog()
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+}

+ 174 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/AnrFcExceptionUtil.java

@@ -0,0 +1,174 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.app.Application;
+import android.os.Environment;
+import android.util.Log;
+
+import com.github.anrwatchdog.ANRError;
+import com.github.anrwatchdog.ANRWatchDog;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Created by dengzhe on 2018/4/2.
+ * //=========================FC&ANR异常处理类=========================//
+ */
+
+public class AnrFcExceptionUtil implements Thread.UncaughtExceptionHandler {
+
+    private static ANRWatchDog mANRWatchDog;
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+    public static final String TAG = "MyApplication";
+    private static Application application;
+
+    private static AnrFcExceptionUtil mAnrFcExceptionUtil;
+
+    /**
+     * 存储异常和参数信息
+     */
+    private Map<String, String> paramsMap = new HashMap<>();
+    /**
+     * 格式化时间
+     */
+    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+
+
+    public static AnrFcExceptionUtil getInstance(Application application) {
+        if (mAnrFcExceptionUtil == null) {
+            mAnrFcExceptionUtil = new AnrFcExceptionUtil(application);
+        }
+        return mAnrFcExceptionUtil;
+    }
+
+    private AnrFcExceptionUtil(Application application) {
+        //获取系统默认的UncaughtException处理器
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        this.application = application;
+    }
+
+    @Override
+    public void uncaughtException(Thread thread, Throwable ex) {
+        if (!handleException(ex) && mDefaultHandler != null) {
+            //如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, ex);
+        } else {
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "error : ", e);
+            }
+
+            android.os.Process.killProcess(android.os.Process.myPid());
+            System.exit(0);
+        }
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回false.
+     */
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+        //使用Toast来显示异常信息
+        /*new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+//                Toast.makeText(application.getApplicationContext(), "很抱歉,程序出现异常,即将重新启动.",
+//                        Toast.LENGTH_SHORT).show();
+                Looper.loop();
+            }
+        }.start();*/
+        saveCrashInfo2File(ex);
+        return true;
+    }
+
+    /**
+     * 保存错误信息到文件中
+     *
+     * @param ex
+     * @return 返回文件名称
+     */
+    private String saveCrashInfo2File(Throwable ex) {
+        StringBuffer sb = new StringBuffer();
+        for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            sb.append(key + "=" + value + "\n");
+        }
+
+        Writer writer = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(writer);
+        ex.printStackTrace(printWriter);
+        Throwable cause = ex.getCause();
+        while (cause != null) {
+            cause.printStackTrace(printWriter);
+            cause = cause.getCause();
+        }
+        printWriter.close();
+        String result = writer.toString();
+        sb.append(result);
+
+        try {
+            long timestamp = System.currentTimeMillis();
+            String time = format.format(new Date());
+            String fileName = "crash-" + time + "-" + timestamp + ".txt";
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/crash/";
+                File dir = new File(path);
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+                FileOutputStream fos = new FileOutputStream(path + fileName);
+                fos.write(sb.toString().getBytes());
+                Log.i(TAG, "saveCrashInfo2File: "+sb.toString());
+                fos.close();
+            }
+            return fileName;
+        } catch (Exception e) {
+            Log.e(TAG, "an error occured while writing file...", e);
+        }
+        return null;
+    }
+
+    /**
+     * ===================================================崩溃异常处理===================================================
+     */
+    public void initFCException() {
+        //设置该CrashHandler为程序的默认处理器
+        AnrFcExceptionUtil catchExcep = AnrFcExceptionUtil.getInstance(application);
+        Thread.setDefaultUncaughtExceptionHandler(catchExcep);
+        mANRWatchDog = new ANRWatchDog(8000);
+        mANRWatchDog.setInterruptionListener(new ANRWatchDog.InterruptionListener() {
+            @Override
+            public void onInterrupted(InterruptedException exception) {
+            }
+        }).setIgnoreDebugger(true).setANRListener(new ANRWatchDog.ANRListener() {
+            @Override
+            public void onAppNotResponding(ANRError error) {
+                /*Intent mStartActivity = new Intent(application.getApplicationContext(), Constants.ANR_FC);
+                int mPendingIntentId = 123456;
+                PendingIntent mPendingIntent = PendingIntent.getActivity(application.getApplicationContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+                AlarmManager mgr = (AlarmManager) application.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
+                mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1500, mPendingIntent);
+                android.os.Process.killProcess(android.os.Process.myPid());*/
+
+                Log.d("anr", "Anr restart app...");
+                AppUpdateHelper.reboot(application);
+            }
+        }).start();
+
+    }
+}

+ 213 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/AppUpdateHelper.java

@@ -0,0 +1,213 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+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.util.Log;
+
+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();
+            }
+        }*/
+    }
+
+    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) {
+        if (Build.MODEL.equals("rk3128")) {
+            try {
+                Intent intent = new Intent(Intent.ACTION_REBOOT);
+                intent.putExtra("nowait", 1);
+                intent.putExtra("interval", 1);
+                intent.putExtra("window", 0);
+                context.sendBroadcast(intent);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            SerialPortHelper.resetDevice();
+        } else if (Build.MODEL.equals("rk3288")) {
+            Process process;
+            PrintWriter printWriter;
+            try {
+                process = Runtime.getRuntime().exec("su");
+                printWriter = new PrintWriter(process.getOutputStream());
+                printWriter.println("reboot");
+                printWriter.flush();
+                printWriter.close();
+                process.waitFor();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public interface UpdateCallBack {
+        void onFailed();
+        void onSuccess();
+    }
+}

+ 88 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/CallDialogHelper.java

@@ -0,0 +1,88 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.media.AudioManager;
+import android.view.Display;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.wdkl.app.ncs.callingbed2.R;
+import com.wdkl.core.voip.AsyncPlayer;
+
+public class CallDialogHelper {
+
+    private static AlertDialog callDialog;
+
+    private static AsyncPlayer ringPlayer;
+
+    public static void showCallDialog(Activity activity, int callType, View.OnClickListener hangupCall, View.OnClickListener acceptCall, View.OnClickListener rejectCall) {
+        if (ringPlayer == null) {
+            ringPlayer = new AsyncPlayer(null);
+        }
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.call_dialog_lay, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+
+        LinearLayout outCall = contentView.findViewById(R.id.ll_call_outgoing);
+        ImageView hangup = contentView.findViewById(R.id.iv_hangup_call);
+
+        RelativeLayout inCall = contentView.findViewById(R.id.rl_call_incoming);
+        ImageView accept = contentView.findViewById(R.id.iv_accept_call);
+        ImageView reject = contentView.findViewById(R.id.iv_reject_call);
+
+        if (callType == 0) {
+            //去电
+            outCall.setVisibility(View.VISIBLE);
+            inCall.setVisibility(View.GONE);
+            ringPlayer.play(activity, R.raw.wr_ringback, true, AudioManager.STREAM_MUSIC);
+        } else {
+            //来电
+            outCall.setVisibility(View.GONE);
+            inCall.setVisibility(View.VISIBLE);
+            ringPlayer.play(activity, R.raw.incoming_call, true, AudioManager.STREAM_MUSIC);
+        }
+
+        hangup.setOnClickListener(hangupCall);
+        accept.setOnClickListener(acceptCall);
+        reject.setOnClickListener(rejectCall);
+
+        callDialog = builder.create();
+        callDialog.setCanceledOnTouchOutside(false);
+        callDialog.setCancelable(false);
+        callDialog.show();
+
+        //设置dialog宽高及位置
+        try {
+            Window window = callDialog.getWindow();
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = 800;
+            lp.height = 400;
+            lp.gravity = Gravity.CENTER;
+            //lp.alpha = 0.4f;
+            //window.setBackgroundDrawableResource(R.color.transparent_dialog);
+            window.setAttributes(lp);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void dismissCallDialog() {
+        if (callDialog != null && callDialog.isShowing()) {
+            callDialog.dismiss();
+        }
+        if (ringPlayer != null) {
+            ringPlayer.stop();
+        }
+    }
+}

+ 161 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/HttpHelper.java

@@ -0,0 +1,161 @@
+package com.wdkl.app.ncs.callingbed2.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 okhttp3.ResponseBody;
+
+import static com.wdkl.app.ncs.callingbed2.helper.AppUpdateHelper.FILE_APK_NAME;
+import static com.wdkl.app.ncs.callingbed2.helper.AppUpdateHelper.FILE_APK_PATH;
+
+public class HttpHelper {
+
+    /**
+     * @param url   服务器地址
+     * @param file  所要上传的文件
+     */
+    public static void upload(String url, File file, 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();
+    }
+}

+ 219 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/MediaPlayHelper.java

@@ -0,0 +1,219 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+
+public class MediaPlayHelper {
+
+    private static MediaPlayHelper sInstance = null;
+    private MediaPlayer mediaPlayer;
+    private HandlerThread playHandlerThread;
+    private Handler playHandler;
+
+    private int mResId;
+    private String mUrl;
+    private float mVolume;
+    private boolean mLoop;
+
+    /**
+     * 播放res资源
+     */
+    public static final int PLAY_RES = 100;
+    /**
+     * 播放url资源
+     */
+    public static final int PLAY_URL = 101;
+    /**
+     * 停止
+     */
+    public static final int STOP = 102;
+    /**
+     * 释放
+     */
+    public static final int RELEASE = 103;
+
+
+    private MediaPlayHelper() {
+        createHandlerThread();
+    }
+
+    public static MediaPlayHelper getInstance() {
+        if (sInstance == null) {
+            synchronized (MediaPlayHelper.class) {
+                if (sInstance == null) {
+                    sInstance = new MediaPlayHelper();
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    private void createHandlerThread() {
+        if (playHandlerThread == null) {
+            playHandlerThread = new HandlerThread("playHandlerThread");
+            playHandlerThread.start();
+        }
+
+        if (playHandler == null) {
+            playHandler = new Handler(playHandlerThread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case PLAY_RES:
+                            playResMusicNow();
+                            break;
+                        case PLAY_URL:
+                            playUrlMusicNow();
+                            break;
+                        case STOP:
+                            stopMusicNow();
+                            break;
+                        case RELEASE:
+                            releaseMediaNow();
+                            break;
+                        default:
+                            break;
+                    }
+                }
+            };
+        }
+    }
+
+    public void playResMusic(int id, float vol, boolean loop) {
+        mResId = id;
+        mVolume = vol;
+        mLoop = loop;
+        playHandler.sendEmptyMessage(PLAY_RES);
+    }
+
+    public void playUrlMusic(String url, float vol, boolean loop) {
+        mUrl = url;
+        mVolume = vol;
+        mLoop = loop;
+        playHandler.sendEmptyMessage(PLAY_URL);
+    }
+
+    public void stopMusic() {
+        playHandler.sendEmptyMessage(STOP);
+    }
+
+    public void releaseMusic() {
+        playHandler.sendEmptyMessage(RELEASE);
+    }
+
+    //播放本地res音频资源
+    private void playResMusicNow() {
+        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
+            mediaPlayer.stop();
+        }
+        mediaPlayer = MediaPlayer.create(BaseApplication.appContext, mResId);
+        try {
+            mediaPlayer.setLooping(mLoop);
+            mediaPlayer.setVolume(mVolume, mVolume);
+            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mediaPlayer.start();
+            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer player) {
+                    //playMusicComplete();
+                    //stopMusicNow();
+                }
+            });
+            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer player, int what, int extra) {
+                    //playMusicError();
+                    stopMusicNow();
+                    return false;
+                }
+            });
+        } catch (Exception e) {
+            //playMusicError();
+            e.printStackTrace();
+        }
+    }
+
+    //播放远程或本地存储音频资源
+    private void playUrlMusicNow() {
+        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
+            mediaPlayer.stop();
+        }
+        mediaPlayer = new MediaPlayer();
+        try {
+            mediaPlayer.reset();
+            mediaPlayer.setDataSource(mUrl);
+            mediaPlayer.setLooping(mLoop);
+            mediaPlayer.setVolume(mVolume, mVolume);
+            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+                @Override
+                public void onPrepared(MediaPlayer player) {
+                    if (mediaPlayer != null) {
+                        mediaPlayer.start();
+                    }
+                }
+            });
+            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer player) {
+                    //playMusicComplete();
+                    //stopMusicNow();
+                }
+            });
+            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer player, int what, int extra) {
+                    //playMusicError();
+                    stopMusicNow();
+                    return false;
+                }
+            });
+            mediaPlayer.prepareAsync();
+        } catch (Exception e) {
+            //playMusicError();
+            e.printStackTrace();
+        }
+    }
+
+    private void stopMusicNow() {
+        if (mediaPlayer != null) {
+            mediaPlayer.setOnPreparedListener(null);
+            mediaPlayer.setOnCompletionListener(null);
+            try {
+                //if (mediaPlayer.isPlaying()) {
+                    mediaPlayer.stop();
+                    mediaPlayer.release();
+                //}
+            } catch (IllegalStateException e) {
+                e.printStackTrace();
+            }
+        }
+        mediaPlayer = null;
+    }
+
+    private void releaseMediaNow() {
+        if (mediaPlayer != null) {
+            mediaPlayer.setOnPreparedListener(null);
+            mediaPlayer.setOnCompletionListener(null);
+            try {
+                mediaPlayer.stop();
+                mediaPlayer.release();
+            } catch (IllegalStateException e) {
+                e.printStackTrace();
+            }
+        }
+        mediaPlayer = null;
+    }
+
+    public boolean isMediaPlaying() {
+        if (mediaPlayer != null) {
+            return mediaPlayer.isPlaying();
+        }
+        return false;
+    }
+}

+ 497 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/NetHelper.java

@@ -0,0 +1,497 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+
+
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.middleware.common.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static com.wdkl.ncs.android.middleware.common.Constant.EVENT_INTERNETPING;
+
+public class NetHelper {
+    private WifiManager wifiManager;
+    private ConnectivityManager connManager;
+    private static NetHelper sInstance = null;
+
+    private static Timer timerNetStatus = null;
+    private static final int SCHEDULE_TIME = 30000;
+    public static boolean NetConn = false;
+
+    /**
+     * 以太网是否ping成功
+     */
+    public static final String ETHERNETSTATUS = "ethernetStatus";
+
+    public NetHelper() {
+    }
+
+    public static NetHelper getInstance() {
+        if (sInstance == null) {
+            synchronized (NetHelper.class) {
+                if (sInstance == null) {
+                    sInstance = new NetHelper();
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    public void init() {
+        wifiManager = (WifiManager) BaseApplication.appContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+        connManager = (ConnectivityManager) BaseApplication.appContext.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    public static void startNetCheck() {
+        if (timerNetStatus != null) {
+            timerNetStatus.purge();
+        }
+        timerNetStatus = new Timer();
+        timerNetStatus.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                NetConn = ping(getLocalElement(3), 2, null);
+
+                EventBus.getDefault().post(new MessageEvent(ETHERNETSTATUS, EVENT_INTERNETPING));//循环检测SIP,以太网ping状态
+            }
+        }, 10, SCHEDULE_TIME);
+    }
+
+    /**
+     * ping 网络
+     *
+     * @param host
+     * @param pingCount
+     * @param stringBuffer
+     * @return
+     */
+    public static boolean ping(String host, int pingCount, StringBuffer stringBuffer) {
+        String line = null;
+        Process process = null;
+        BufferedReader successReader = null;
+        String command = "ping -c " + pingCount + " " + host;
+        boolean isSuccess = false;
+        try {
+            process = Runtime.getRuntime().exec(command);
+            if (process == null) {
+                append(stringBuffer, "ping fail:process is null.");
+                return false;
+            }
+            successReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            while ((line = successReader.readLine()) != null) {
+                append(stringBuffer, line);
+            }
+            int status = process.waitFor();
+            if (status == 0) {
+                append(stringBuffer, "exec cmd success:" + command);
+                isSuccess = true;
+            } else {
+                append(stringBuffer, "exec cmd fail.");
+                isSuccess = false;
+            }
+            append(stringBuffer, "exec finished.");
+        } catch (IOException e) {
+        } catch (InterruptedException e) {
+        } finally {
+            if (process != null) {
+                process.destroy();
+            }
+            if (successReader != null) {
+                try {
+                    successReader.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+
+        return isSuccess;
+    }
+
+    private static void append(StringBuffer stringBuffer, String text) {
+        if (stringBuffer != null) {
+            stringBuffer.append(text + "\n");
+        }
+    }
+
+    /**
+     * 获取网关  Waderson
+     * <p>
+     * 1 WIFI情况下获取网关 2 有线网络下的DHCP模式连接 3 有线网络其他连接方式:比如静态ip、pppoe拨号、ipoe拨号等
+     */
+    public static String getLocalElement(int type) {
+        String e = "";
+        if (1 == type) {
+            //e = getLocalElementByWifi();
+        } else if (2 == type) {
+            e = getLocalElementByDhcp();
+        } else if (3 == type) {
+            e = getLocalElementByIp();
+        }
+        if (!TextUtils.isEmpty(e) && e.length() >= 11 && e.length() <= 15) {
+            return e;
+        } else {
+            return "192.168.101.1";
+        }
+    }
+
+
+    /**
+     * 有线网络下的DHCP模式连接
+     */
+    public static String getLocalElementByDhcp() {
+        BufferedReader bufferedReader = null;
+        String str2 = "";
+        String str3 = "getprop dhcp.eth0.gateway";
+        Process exec;
+        BufferedReader bufferedReader2 = null;
+        try {
+            exec = Runtime.getRuntime().exec(str3);
+            try {
+                bufferedReader2 = new BufferedReader(new InputStreamReader(exec.getInputStream()));
+            } catch (Throwable th3) {
+                if (bufferedReader != null) {
+                    bufferedReader.close();
+                }
+                if (exec != null) {
+                    exec.exitValue();
+                }
+            }
+            try {
+                str3 = bufferedReader2.readLine();
+                if (str3 != null) {
+                    TextUtils.isEmpty(str3);
+                }
+                try {
+                    bufferedReader2.close();
+                } catch (IOException iOException222) {
+                    iOException222.printStackTrace();
+                }
+                if (exec != null) {
+                    try {
+                        exec.exitValue();
+                    } catch (Exception e5) {
+                    }
+                }
+            } catch (IOException e6) {
+                str3 = str2;
+                if (bufferedReader2 != null) {
+                    bufferedReader2.close();
+                }
+                if (exec != null) {
+                    exec.exitValue();
+                }
+                return str3;
+            }
+        } catch (IOException e62) {
+            bufferedReader2 = null;
+            exec = null;
+            str3 = str2;
+            if (bufferedReader2 != null) {
+                try {
+                    bufferedReader2.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (exec != null) {
+                exec.exitValue();
+            }
+            return str3;
+        } catch (Throwable th4) {
+            exec = null;
+            if (bufferedReader != null) {
+                try {
+                    bufferedReader.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (exec != null) {
+                exec.exitValue();
+            }
+        }
+        return str3;
+    }
+
+    /**
+     * 有线网络其他连接方式:比如静态ip、pppoe拨号、ipoe拨号等
+     */
+    public static String getLocalElementByIp() {
+        BufferedReader bufferedReader = null;
+        String result = "";
+        String str2 = "";
+        String str3 = "ip route list table 0";
+        Process exec;
+        BufferedReader bufferedReader2 = null;
+        try {
+            exec = Runtime.getRuntime().exec(str3);
+            try {
+                bufferedReader2 = new BufferedReader(new InputStreamReader(exec.getInputStream()));
+            } catch (Throwable th3) {
+                if (bufferedReader != null) {
+                    bufferedReader.close();
+                }
+                if (exec != null) {
+                    exec.exitValue();
+                }
+            }
+            try {
+                str2 = bufferedReader2.readLine();
+                if (str2 != null) {
+                    str2 = str2.trim();
+                    String[] strings = str2.split("\\s+");
+                    if (strings.length > 3) {
+                        result = strings[2];
+                    }
+                }
+                try {
+                    bufferedReader2.close();
+                } catch (IOException iOException222) {
+                    iOException222.printStackTrace();
+                }
+                if (exec != null) {
+                    try {
+                        exec.exitValue();
+                    } catch (Exception e5) {
+                    }
+                }
+            } catch (IOException e6) {
+                if (bufferedReader2 != null) {
+                    bufferedReader2.close();
+                }
+                if (exec != null) {
+                    exec.exitValue();
+                }
+                return result;
+            }
+        } catch (IOException e62) {
+            bufferedReader2 = null;
+            exec = null;
+            if (bufferedReader2 != null) {
+                try {
+                    bufferedReader2.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (exec != null) {
+                exec.exitValue();
+            }
+            return result;
+        } catch (Throwable th4) {
+            exec = null;
+            if (bufferedReader != null) {
+                try {
+                    bufferedReader.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+            if (exec != null) {
+                exec.exitValue();
+            }
+        }
+        return result;
+    }
+
+    //获取本地ip地址
+    public String getLocalIP() {
+        try {
+            for (Enumeration<NetworkInterface> enNetI = NetworkInterface.getNetworkInterfaces(); enNetI.hasMoreElements(); ) {
+                NetworkInterface netI = enNetI.nextElement();
+                for (Enumeration<InetAddress> enumIpAddr = netI.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+                    InetAddress inetAddress = enumIpAddr.nextElement();
+                    if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
+                        return inetAddress.getHostAddress();
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 得到MAC
+     *
+     * @return String
+     */
+    public String getMacAddress() {
+        String mac = "";
+        try {
+            mac = getLocalMacAddressFromIp();
+            if (TextUtils.isEmpty(mac)) {
+                mac = getNetworkMac();
+            }
+            if (TextUtils.isEmpty(mac)) {
+                mac = tryGetWifiMac();
+            }
+        } catch (Exception e) {
+        }
+        return mac;
+    }
+
+    /**
+     * 通过WiFiManager获取mac地址
+     * 这个方法Android 7.0是获取不到的,返回的是null,其实是返回“02:00:00:00:00:00”
+     *
+     * @return
+     */
+    public String tryGetWifiMac() {
+        String mac;
+        WifiInfo wi = wifiManager.getConnectionInfo();
+        if (wi == null || wi.getMacAddress() == null) {
+            mac = null;
+        }
+        if ("02:00:00:00:00:00".equals(wi.getMacAddress().trim())) {
+            mac = null;
+        } else {
+            mac = wi.getMacAddress().trim();
+        }
+        return mac;
+    }
+
+    /**
+     * 通过网络接口获取
+     *
+     * @return
+     */
+    public static String getNetworkMac() {
+        try {
+            List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
+            for (NetworkInterface nif : all) {
+                // if (!nif.getName().equalsIgnoreCase("wlan0") && !nif.getName().equalsIgnoreCase("eth0") && !nif.getName().equalsIgnoreCase("eth1"))
+                if (!nif.getName().equalsIgnoreCase("eth0") && !nif.getName().equalsIgnoreCase("eth1") && !nif.getName().equalsIgnoreCase("eth2"))
+                    continue;
+                byte[] macBytes = nif.getHardwareAddress();
+                if (macBytes == null) {
+                    return null;
+                }
+                StringBuilder res1 = new StringBuilder();
+                for (byte b : macBytes) {
+                    res1.append(String.format("%02X:", b));
+                }
+                if (res1.length() > 0) {
+                    res1.deleteCharAt(res1.length() - 1);
+                }
+                return res1.toString();
+            }
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        }
+        return null;
+    }
+
+
+    /**
+     * 根据IP地址获取MAC地址
+     *
+     * @return
+     */
+    @SuppressLint("NewApi")
+    public static String getLocalMacAddressFromIp() {
+        String strMacAddr = null;
+        try {
+            // 获得IpD地址
+            InetAddress ip = getLocalInetAddress();
+            byte[] b = NetworkInterface.getByInetAddress(ip)
+                    .getHardwareAddress();
+            StringBuffer buffer = new StringBuffer();
+            for (int i = 0; i < b.length; i++) {
+                if (i != 0) {
+                    buffer.append(':');
+                }
+                String str = Integer.toHexString(b[i] & 0xFF);
+                buffer.append(str.length() == 1 ? 0 + str : str);
+            }
+            strMacAddr = buffer.toString().toUpperCase();
+
+        } catch (Exception e) {
+
+        }
+
+        return strMacAddr;
+    }
+
+    /**
+     * 获取移动设备本地IP
+     *
+     * @return
+     */
+    public static InetAddress getLocalInetAddress() {
+        InetAddress ip = null;
+        try {
+            // 列举
+            Enumeration<NetworkInterface> en_netInterface = NetworkInterface
+                    .getNetworkInterfaces();
+            while (en_netInterface.hasMoreElements()) {// 是否还有元素
+                NetworkInterface ni = (NetworkInterface) en_netInterface
+                        .nextElement();// 得到下一个元素
+                Enumeration<InetAddress> en_ip = ni.getInetAddresses();// 得到一个ip地址的列举
+                while (en_ip.hasMoreElements()) {
+                    ip = en_ip.nextElement();
+                    if (!ip.isLoopbackAddress()
+                            && ip.getHostAddress().indexOf(":") == -1)
+                        break;
+                    else
+                        ip = null;
+                }
+
+                if (ip != null) {
+                    break;
+                }
+            }
+        } catch (SocketException e) {
+
+            e.printStackTrace();
+        }
+        return ip;
+    }
+
+    public static boolean isBTConnected() {
+        BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (btAdapter != null) {
+            return btAdapter.getState() == BluetoothAdapter.STATE_ON;
+        }
+
+        return false;
+    }
+
+    public int getNetworkType() {
+        if (connManager != null && connManager.getActiveNetworkInfo() != null) {
+            return connManager.getActiveNetworkInfo().getType();
+        }
+
+        return -1;
+    }
+
+    public boolean networkAvailable() {
+        if (connManager != null && connManager.getActiveNetworkInfo() != null) {
+            return connManager.getActiveNetworkInfo().isConnected();
+        }
+
+        return false;
+    }
+}

+ 90 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/RecordHelper.java

@@ -0,0 +1,90 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.media.MediaRecorder;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+public class RecordHelper {
+    private static final String VOICE_MSG_FILE_PATH = Environment.getExternalStorageDirectory().getPath() + "/VoiceMsg";
+    private String audiofilePath;
+    private MediaRecorder mediaRecorder;
+    private static RecordHelper sInstance = null;
+    private boolean recording = false;
+
+    private final static Object lock = new Object();
+
+    public RecordHelper() {
+    }
+
+    public static RecordHelper getInstance() {
+        if (sInstance == null) {
+            synchronized (RecordHelper.class) {
+                if (sInstance == null) {
+                    sInstance = new RecordHelper();
+                }
+            }
+        }
+        return sInstance;
+    }
+
+    public void init() {
+        //创建音频文件路径
+        File file = new File(VOICE_MSG_FILE_PATH);
+        if (!file.exists()) {
+            file.mkdir();
+        }
+    }
+
+    public void startRecord() {
+        audiofilePath = VOICE_MSG_FILE_PATH + "/" + System.currentTimeMillis() + "_voice.mp3";
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (lock) {
+                    try {
+                        mediaRecorder = new MediaRecorder();
+                        mediaRecorder.setOutputFile(audiofilePath);
+                        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);// 设置MediaRecorder的音频源为麦克风
+                        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);// 设置MediaRecorder录制的音频格式
+                        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);// 设置MediaRecorder录制音频的编码为amr
+                        mediaRecorder.prepare();
+                        mediaRecorder.start();
+                        recording = true;
+                    } catch (IOException e) {
+                        Log.i("error", "call startAmr(File mRecAudioFile) failed!" + e.getMessage());
+                    }
+                }
+            }
+        }).start();
+    }
+
+    public void stopRecord() {
+        synchronized (lock) {
+            try {
+                mediaRecorder.stop();
+                mediaRecorder.release();
+                mediaRecorder = null;
+            } catch (Exception e) {
+                e.printStackTrace();
+            } finally {
+                recording = false;
+            }
+        }
+    }
+
+    public void deleteAudioFile(String path) {
+        synchronized (lock) {
+            File file = new File(path);
+            if (file.exists()) {
+                file.delete();
+            }
+        }
+    }
+
+    public String getAudiofilePath() {
+        return audiofilePath;
+    }
+}

+ 24 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/RingPlayHelper.java

@@ -0,0 +1,24 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.content.Context;
+import android.media.AudioManager;
+
+import com.wdkl.core.voip.AsyncPlayer;
+
+public class RingPlayHelper {
+
+    private static AsyncPlayer ringPlayer;
+
+    public static void playRingTone(Context context, int res, boolean loop) {
+        if (ringPlayer == null) {
+            ringPlayer = new AsyncPlayer(null);
+        }
+        ringPlayer.play(context, res, loop, AudioManager.STREAM_MUSIC);
+    }
+
+    public static void stopRingTone() {
+        if (ringPlayer != null) {
+            ringPlayer.stop();
+        }
+    }
+}

+ 35 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/SOSHelper.java

@@ -0,0 +1,35 @@
+package com.wdkl.app.ncs.callingbed2.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 static Handler handler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(Message msg) {
+            sosStop();
+        }
+    };
+
+    public static void sosStart() {
+        SerialPortHelper.setSosLight("2");
+        OtherUtil.sendSosCall(Constant.DEVICE_ID);
+
+        //60s之后紧急按钮灯自动复位
+        handler.sendEmptyMessageDelayed(110, 60000);
+    }
+
+    public static void sosStop() {
+        handler.removeCallbacksAndMessages(null);
+        SerialPortHelper.setSosLight("0");
+    }
+}

+ 81 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/ScreenManagerUtil.kt

@@ -0,0 +1,81 @@
+package com.wdkl.app.ncs.callingbed2.helper
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Build
+import android.provider.Settings
+import android.util.Log
+
+class ScreenManagerUtil {
+
+
+    /**
+     * @return 0--255
+     */
+    fun getScreenBrightness(context: Context): Int {
+        var screenBrightness = 150
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if (!Settings.System.canWrite(context)) {
+                val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
+                intent.data = Uri.parse("package:" + context.packageName)
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                context.startActivity(intent)
+            } else {
+                //有了权限,具体的动作
+                try {
+                    screenBrightness = Settings.System.getInt(context.contentResolver,
+                            Settings.System.SCREEN_BRIGHTNESS)
+                } catch (e: Settings.SettingNotFoundException) {
+                    e.printStackTrace()
+                }
+
+            }
+        } else {
+            try {
+                screenBrightness = Settings.System.getInt(context.contentResolver,
+                        Settings.System.SCREEN_BRIGHTNESS)
+            } catch (e: Settings.SettingNotFoundException) {
+                e.printStackTrace()
+            }
+
+        }
+
+        return screenBrightness
+    }
+
+
+    /**
+     * 保存当前的屏幕亮度值,并使之生效
+     *
+     * @param paramInt 0-255
+     */
+    fun setScreenBrightness(context: Context, paramInt: Int) {
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if (!Settings.System.canWrite(context)) {
+                val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS)
+                intent.data = Uri.parse("package:" + context.packageName)
+                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                context.startActivity(intent)
+            } else {
+                //有了权限,具体的动作
+                Settings.System.putInt(context.contentResolver,
+                        Settings.System.SCREEN_BRIGHTNESS, paramInt)
+                val uri = Settings.System
+                        .getUriFor("screen_brightness")
+                Log.w("当前亮度", "当前亮度======" + getScreenBrightness(context))
+                context.contentResolver.notifyChange(uri, null)
+            }
+        } else {
+            Settings.System.putInt(context.contentResolver,
+                    Settings.System.SCREEN_BRIGHTNESS, paramInt)
+            val uri = Settings.System
+                    .getUriFor("screen_brightness")
+            Log.w("当前亮度", "当前亮度======" + getScreenBrightness(context))
+            context.contentResolver.notifyChange(uri, null)
+        }
+
+    }
+}

+ 37 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/SerialPortHelper.java

@@ -0,0 +1,37 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import serialporttest.utils.SerialPortUtil;
+
+public class SerialPortHelper {
+
+    //MIC设置
+    public static void setHandsMIC(boolean isHandsMIC) {
+        if (isHandsMIC) {
+            //打开手柄mic
+            SerialPortUtil.getInstance().sendCommand(SerialPortUtil.MIC, "0", "F");
+        } else {
+            //打开面板mic
+            SerialPortUtil.getInstance().sendCommand(SerialPortUtil.MIC, "1", "F");
+        }
+    }
+
+    /*
+     * 呼叫和通话状态
+     * data: 0 -- 正常,  1 -- 呼叫中, 2 -- 通话中
+     */
+    public static void setCallStatus(String data) {
+        SerialPortUtil.getInstance().sendCommand(SerialPortUtil.CALL_STATUS , data, "F");
+    }
+
+    /**
+     * 设置卫生间紧急按钮灯状态: 0关闭1打开2闪烁
+     */
+    public static void setSosLight(String state) {
+        SerialPortUtil.getInstance().sendCommand(SerialPortUtil.ULED, state, "F");
+    }
+
+    //重置设备
+    public static void resetDevice() {
+        SerialPortUtil.getInstance().sendCommand(SerialPortUtil.NET_STATUS , "1", "F");
+    }
+}

+ 82 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/SoundPoolManager.java

@@ -0,0 +1,82 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.os.Build;
+
+import com.wdkl.app.ncs.callingbed2.R;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+
+import java.util.HashMap;
+
+public class SoundPoolManager {
+
+    private static SoundPoolManager instance;
+    private SoundPool soundPool;
+    private HashMap<Integer, Integer> soundID = new HashMap<>();
+    private boolean isLoaded = false;
+    private boolean inited = false;
+
+    public static SoundPoolManager getInstance() {
+        if (instance == null) {
+            instance = new SoundPoolManager();
+        }
+        return instance;
+    }
+
+    public void init() {
+        if (inited) {
+            return;
+        }
+
+        if(Build.VERSION.SDK_INT > 21){
+            SoundPool.Builder builder = new SoundPool.Builder();
+            //传入音频数量
+            builder.setMaxStreams(3);
+            //AudioAttributes是一个封装音频各种属性的方法
+            AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
+            //设置音频流的合适的属性
+            attrBuilder.setLegacyStreamType(AudioManager.STREAM_MUSIC);//STREAM_MUSIC
+            //加载一个AudioAttributes
+            builder.setAudioAttributes(attrBuilder.build());
+            soundPool = builder.build();
+        }else{
+            soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
+        }
+
+        //来电
+        soundID.put(1, soundPool.load(BaseApplication.appContext, R.raw.incoming_call, 1));
+        //去电
+        soundID.put(2, soundPool.load(BaseApplication.appContext, R.raw.outgoing_call, 1));
+        //测试
+        soundID.put(3, soundPool.load(BaseApplication.appContext, R.raw.speaker_test, 1));
+        soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
+            @Override
+            public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
+                isLoaded = true;
+                inited = true;
+            }
+        });
+    }
+
+    public int playSound(int index, float leftVol, float rightVol, int loop) {
+        try {
+            if (isLoaded) {
+                return soundPool.play(soundID.get(index), leftVol, rightVol, 1, loop, 1);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return -1;
+    }
+
+    public void stopPlaySound(int streamId) {
+        soundPool.stop(streamId);
+    }
+
+    public void release() {
+        soundPool.release();
+    }
+}

+ 17 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/Utils.java

@@ -0,0 +1,17 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.hardware.Camera;
+
+import com.wdkl.ncs.android.middleware.common.Constant;
+
+public class Utils {
+
+    public static void checkCameraSupport() {
+        int num = Camera.getNumberOfCameras();
+        if (num > 0) {
+            Constant.supportCamera = true;
+        } else {
+            Constant.supportCamera = false;
+        }
+    }
+}

+ 184 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/VoiceManagerUtil.java

@@ -0,0 +1,184 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.content.Context;
+import android.media.AudioManager;
+
+/**
+ * 类名称:VoiceManagerUtil <br>
+ * 类描述:声音控制工具类 <br>
+ * 创建人:Waderson Shll (TEL:15675117662)<br>
+ * 创建时间:2018-03-15 <br>
+ * 特别提醒:如有需要该类可任意创建与调用;在未通知本人的情况下该类禁止任何修改!<br>
+ */
+public class VoiceManagerUtil {
+    /**
+     * 获取提示音音量最大值
+     *
+     * @param context
+     */
+    public static int getAlarmMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM);
+    }
+
+    /**
+     * 获取提示音音量当前值
+     *
+     * @param context
+     */
+    public static int getAlarmNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
+    }
+
+    /**
+     * 获取多媒体音量最大值
+     *
+     * @param context
+     */
+    public static int getMusicMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+    }
+
+    /**
+     * 获取多媒体音量当前值
+     *
+     * @param context
+     */
+    public static int getMusicNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+    }
+
+    /**
+     * 获取铃声音量最大值
+     *
+     * @param context
+     */
+    public static int getRingMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_RING);
+    }
+
+    /**
+     * 获取铃声音量当前值
+     *
+     * @param context
+     */
+    public static int getRingNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
+    }
+
+    /**
+     * 获取系统音量最大值
+     *
+     * @param context
+     */
+    public static int getSystemMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
+    }
+
+    /**
+     * 获取系统音量当前值
+     *
+     * @param context
+     */
+    public static int getSystemNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_SYSTEM);
+    }
+
+    /**
+     * 获取通话音量最大值
+     *
+     * @param context
+     */
+    public static int getCallMax(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
+    }
+
+    /**
+     * 获取通话音量当前值
+     *
+     * @param context
+     */
+    public static int getCallNow(Context context) {
+        AudioManager mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        return mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
+    }
+
+    /**
+     * 设置提示音音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setAlarmVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_ALARM, (int) (getAlarmMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置多媒体音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setMusicVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, (int) (getMusicMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置铃声音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setRingVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_RING, (int) (getRingMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置系统音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setSystemVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_SYSTEM, (int) (getSystemMax(context) * vPercent), 0);
+    }
+
+    /**
+     * 设置通话音量
+     *
+     * @param context
+     * @param percent (百分比;只能0--100之间)
+     */
+    public static void setCallVoice(Context context, int percent) {
+        float vPercent=((float)percent)/100f;
+        vPercent = vPercent < 0 ? 0 : vPercent;
+        vPercent = vPercent > 1 ? 1 : vPercent;
+        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, (int) (getCallMax(context) * vPercent), 0);
+    }
+
+}

+ 50 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/helper/WarningDialogHelper.java

@@ -0,0 +1,50 @@
+package com.wdkl.app.ncs.callingbed2.helper;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import com.wdkl.app.ncs.callingbed2.R;
+
+public class WarningDialogHelper {
+    private static AlertDialog dialog;
+
+    public static void showDialog(Activity activity) {
+        if (dialog != null && dialog.isShowing()) {
+            return;
+        }
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.warning_dialog_lay, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+        Button button = contentView.findViewById(R.id.cancel_button);
+        button.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                AppUpdateHelper.reboot(activity);
+            }
+        });
+
+        dialog = builder.create();
+        //dialog.setCanceledOnTouchOutside(false);
+        //dialog.setCancelable(false);
+        dialog.show();
+
+        //设置dialog宽高及位置
+        try {
+            Window window = dialog.getWindow();
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = 600;
+            lp.height = 240;
+            lp.gravity = Gravity.CENTER;
+            window.setAttributes(lp);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 20 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/launch/Callingbed2Launch.kt

@@ -0,0 +1,20 @@
+package com.wdkl.app.ncs.callingbed2.launch
+
+import com.enation.javashop.android.jrouter.external.annotation.Router
+import com.wdkl.app.ncs.callingbed2.di.Callingbed2Component
+import com.wdkl.app.ncs.callingbed2.di.DaggerCallingbed2Component
+import com.wdkl.ncs.android.lib.base.BaseLaunch
+import com.wdkl.ncs.android.middleware.di.DaggerManager
+
+@Router(path = "/callingbed2/launch")
+class Callingbed2Launch :BaseLaunch() {
+    companion object {
+        lateinit var component:Callingbed2Component
+    }
+
+    override fun moduleInit() {
+        component = DaggerCallingbed2Component.builder()
+                .applicationComponent(DaggerManager.APPLICATION_COMPONENT)
+                .build()
+    }
+}

+ 354 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/settings/SettingConfig.java

@@ -0,0 +1,354 @@
+package com.wdkl.app.ncs.callingbed2.settings;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+
+public class SettingConfig {
+
+
+    private static final String SP_NAME = "SP_BED_FUNCTION";
+
+    //白昼设置白天的初始时间设置
+    private static final String KEY_SP_INITIAL_DAY_TIME = "KEY_SP_INITIAL_DAY_TIME";
+    private static final String initial_day_time = "07:00";
+    private static final String KEY_SP_INITIAL_DAY_TIME_LOCATION = "KEY_SP_INITIAL_DAY_TIME_LOCATION";
+    private static final String initial_day_time_location = "12";
+
+
+    //白昼设置白天的结束时间设置
+    private static final String KEY_SP_END_OF_DAY = "KEY_SP_END_OF_DAY";
+    private static final String end_of_day = "19:00";
+    //白昼设置白天的结束时间设置
+    private static final String KEY_SP_END_OF_DAY_LOCATION = "KEY_SP_END_OF_DAY_LOCATION";
+    private static final String end_of_day_location = "38";
+
+
+    //分机白天亮度
+    private static final String KEY_SP_EXTENSION_DAYTIME_BRIGHTNESS = "KEY_SP_EXTENSION_DAYTIME_BRIGHTNESS";
+    private static final int extension_daytime_brightness = 80;
+    //分机晚上亮度
+    private static final String KEY_SP_EXTENSION_NIGHT_BRIGHTNESS = "KEY_SP_EXTENSION_NIGHT_BRIGHTNESS";
+    private static final int extension_night_brightness = 50;
+
+    //分机LED白天亮度
+    private static final String KEY_SP_EXTENSION_DAYTIME_LED_BRIGHTNESS = "KEY_SP_EXTENSION_DAYTIME_LED_BRIGHTNESS";
+    private static final int extension_daytime_led_brightness = 80;
+    //分机LED晚上亮度
+    private static final String KEY_SP_EXTENSION_NIGHT_LED_BRIGHTNESS = "KEY_SP_EXTENSION_NIGHT_LED_BRIGHTNESS";
+    private static final int extension_night_led_brightness = 50;
+
+    //分机白天系统音量
+    private static final String KEY_SP_EXTENSION_DAYTIME_SYSTEM_VOLUME = "KEY_SP_EXTENSION_DAYTIME_SYSTEM_VOLUME";
+    private static final int extension_daytime_system_volume = 100;
+    //分机晚上系统音量
+    private static final String KEY_SP_EXTENSION_NIGHT_SYSTEM_VOLUME = "KEY_SP_EXTENSION_NIGHT_SYSTEM_VOLUME";
+    private static final int extension_night_system_volume = 70;
+
+    //分机手柄录入音量
+    private static final String KEY_SP_THE_EXTENSION_HANDLR_RECORDS_THE_VOLUME = "KEY_SP_THE_EXTENSION_HANDLR_RECORDS_THE_VOLUME";
+    private static final int the_extension_handle_records_the_volume = 50;
+
+    //分机通话音量
+    private static final String KEY_SP_EXTENSION_CALL_VOLUME = "KEY_SP_EXTENSION_CALL_VOLUME";
+    private static final int extension_call_volume = 100;
+
+    //呼叫超时时间
+    private static final String KEY_SP_SIP_OVERTIME = "KEY_SP_SIP_OVERTIME";
+    private static final int sip_over_time = 30;
+
+    //息屏超时时间
+    private static final String KEY_SP_SLEEP_TIME = "KEY_SP_SLEEP_TIME";
+    private static final int sleep_time = 30;
+
+    //网络异常重启次数
+    private static final String KEY_SP_NET_ERR_RESET_COUNT = "KEY_SP_NET_ERR_RESET_COUNT";
+
+    /**
+     * 获取分机白天亮度
+     *
+     * @return
+     */
+    public static int getExtensionDaytimeBrightness(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_DAYTIME_BRIGHTNESS, extension_daytime_brightness);
+    }
+
+    /**
+     * 设置分机白天亮度
+     *
+     * @param value
+     */
+    public static void setExtensionDaytimeBrightness(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_DAYTIME_BRIGHTNESS, value).apply();
+    }
+
+    /**
+     * 获取分机晚上亮度
+     *
+     * @return
+     */
+    public static int getExtensionNightBrightness(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_NIGHT_BRIGHTNESS, extension_night_brightness);
+    }
+
+    /**
+     * 设置分机晚上亮度
+     *
+     * @param value
+     */
+    public static void setExtensionNightBrightness(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_NIGHT_BRIGHTNESS, value).apply();
+    }
+
+    /**
+     * 获取分机LED白天亮度
+     *
+     * @return
+     */
+    public static int getExtensionDaytimeLEDBrightness(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_DAYTIME_LED_BRIGHTNESS, extension_daytime_led_brightness);
+    }
+
+    /**
+     * 设置分机LED白天亮度
+     *
+     * @param value
+     */
+    public static void setExtensionDaytimeLEDBrightness(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_DAYTIME_LED_BRIGHTNESS, value).apply();
+    }
+
+    /**
+     * 获取分机LED晚上亮度
+     *
+     * @return
+     */
+    public static int getExtensionNightLEDBrightness(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_NIGHT_LED_BRIGHTNESS, extension_night_led_brightness);
+    }
+
+    /**
+     * 设置分机LED晚上亮度
+     *
+     * @param value
+     */
+    public static void setExtensionNightLEDBrightness(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_NIGHT_LED_BRIGHTNESS, value).apply();
+    }
+
+
+    /**
+     * 获取分机白天系统音量
+     *
+     * @return
+     */
+    public static int getExtensionDaytimeSystemVolume(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_DAYTIME_SYSTEM_VOLUME, extension_daytime_system_volume);
+    }
+
+    /**
+     * 设置分机白天系统音量
+     *
+     * @param value
+     */
+    public static void setExtensionDaytimeSystemVolume(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_DAYTIME_SYSTEM_VOLUME, value).apply();
+    }
+
+    /**
+     * 获取分机晚上系统音量
+     *
+     * @return
+     */
+    public static int getExtensionNightSystemVolume(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_NIGHT_SYSTEM_VOLUME, extension_night_system_volume);
+    }
+
+    /**
+     * 设置分机晚上系统音量
+     *
+     * @param value
+     */
+    public static void setExtensionNightSystemVolume(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_NIGHT_SYSTEM_VOLUME, value).apply();
+    }
+
+
+
+    /**
+     * 获取分机手柄录入音量
+     *
+     * @return
+     */
+    public static int getTheExtensionHandleRecordsTheVolume(Context context) {
+        return getSP(context).getInt(KEY_SP_THE_EXTENSION_HANDLR_RECORDS_THE_VOLUME, the_extension_handle_records_the_volume);
+    }
+
+    /**
+     * 设置分机手柄录入音量
+     *
+     * @param value
+     */
+    public static void setTheExtensionHandleRecordsTheVolume(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_THE_EXTENSION_HANDLR_RECORDS_THE_VOLUME, value).apply();
+    }
+
+
+    /**
+     * 获取分机通话音量
+     *
+     * @return
+     */
+    public static int getExtensionCallVolume(Context context) {
+        return getSP(context).getInt(KEY_SP_EXTENSION_CALL_VOLUME, extension_call_volume);
+    }
+
+    /**
+     * 设置分机通话音量
+     *
+     * @param value
+     */
+    public static void setExtensionCallVolume(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_EXTENSION_CALL_VOLUME, value).apply();
+    }
+
+
+    /**
+     * 获取白昼设置白天的初始时间
+     *
+     * @return
+     */
+    public static String getInitialDayTime(Context context) {
+        return getSP(context).getString(KEY_SP_INITIAL_DAY_TIME, initial_day_time);
+    }
+
+    /**
+     * 设置白昼白天的初始时间
+     *
+     * @param value
+     */
+    public static void setInitialDayTime(Context context, String value) {
+        getEditor(context).putString(KEY_SP_INITIAL_DAY_TIME, value).apply();
+    }
+
+    /**
+     * 获取白昼设置白天的初始时间位置
+     *
+     * @return
+     */
+    public static String getInitialDayTimeLocation(Context context) {
+        return getSP(context).getString(KEY_SP_INITIAL_DAY_TIME_LOCATION, initial_day_time_location);
+    }
+
+    /**
+     * 设置白昼白天的初始时间位置
+     *
+     * @param value
+     */
+    public static void setInitialDayTimeLocation(Context context, String value) {
+        getEditor(context).putString(KEY_SP_INITIAL_DAY_TIME_LOCATION, value).apply();
+    }
+
+
+
+    /**
+     * 设置白昼白天的结束时间
+     *
+     * @param value
+     */
+    public static void setEndOfDay(Context context, String value) {
+        getEditor(context).putString(KEY_SP_END_OF_DAY, value).apply();
+    }
+
+    /**
+     * 获取白昼设置白天的结束时间
+     *
+     * @return
+     */
+    public static String getEndOfDay(Context context) {
+        return getSP(context).getString(KEY_SP_END_OF_DAY, end_of_day);
+    }
+
+    /**
+     * 设置白昼白天的结束时间位置
+     *
+     * @param value
+     */
+    public static void setEndOfDayLocation(Context context, String value) {
+        getEditor(context).putString(KEY_SP_END_OF_DAY_LOCATION, value).apply();
+    }
+
+    /**
+     * 获取白昼设置白天的结束时间位置
+     *
+     * @return
+     */
+    public static String getEndOfDayLocation(Context context) {
+        return getSP(context).getString(KEY_SP_END_OF_DAY_LOCATION, end_of_day_location);
+    }
+
+    /**
+     * 设置呼叫超时时间
+     *
+     * @param value
+     */
+    public static void setSipOverTime(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_SIP_OVERTIME, value).apply();
+    }
+
+    /**
+     * 获取呼叫超时时间
+     *
+     * @return
+     */
+    public static int getSipOverTime(Context context) {
+        return getSP(context).getInt(KEY_SP_SLEEP_TIME, sip_over_time);
+    }
+
+    /**
+     * 设置息屏时间
+     *
+     * @param value
+     */
+    public static void setSleepTime(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_SLEEP_TIME, value).apply();
+    }
+
+    /**
+     * 获取息屏时间
+     *
+     * @return
+     */
+    public static int getSleepTime(Context context) {
+        return getSP(context).getInt(KEY_SP_SIP_OVERTIME, sleep_time);
+    }
+
+    /**
+     * 获取网络异常重启次数
+     *
+     * @return
+     */
+    public static int getNetErrResetCount(Context context) {
+        return getSP(context).getInt(KEY_SP_NET_ERR_RESET_COUNT, 0);
+    }
+
+    /**
+     * 设置网络异常重启次数
+     *
+     * @param value
+     */
+    public static void setNetErrResetCount(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_NET_ERR_RESET_COUNT, value).apply();
+    }
+
+
+    private static SharedPreferences getSP(Context context) {
+        return context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+    }
+
+    private static SharedPreferences.Editor getEditor(Context context) {
+        return getSP(context).edit();
+    }
+
+
+}

+ 579 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/sip/SipHelper.java.bak

@@ -0,0 +1,579 @@
+package com.wdkl.app.ncs.callingbed2.sip;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.IVvsipServiceListener;
+import com.vvsip.ansip.VvsipCall;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipServiceBinder;
+import com.vvsip.ansip.VvsipTask;
+import com.wdkl.app.ncs.callingbed2.common.MessageEvent;
+import com.wdkl.app.ncs.callingbed2.helper.NetHelper;
+
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.wdkl.app.ncs.callingbed2.common.Constant.EVENT_SIP_REGISTER_STATUS;
+import static com.wdkl.app.ncs.callingbed2.sip.SipStatus.REGISTERCOM;
+import static com.wdkl.app.ncs.callingbed2.sip.SipStatus.REGISTERFAIL;
+import static com.wdkl.app.ncs.callingbed2.sip.SipStatus.REGISTERING;
+import static com.vvsip.ansip.VvsipTask.EXOSIP_CALL_CLOSED;
+
+
+public class SipHelper {
+
+    private final static String SIP_IP_END = ":5060";
+
+    private static String sipIP = "";
+    private static String sipID = "";
+    private static String sipPWD = "";
+
+    /**
+     * Sip启动注册.
+     */
+    protected int mSipRegisterTime = 5000;
+    private Handler sipRegisterHandler = null;
+    private Runnable sipRegisterRunnable = null;
+
+    /**
+     * SIP信息
+     */
+    public static final String SipInfoTag = "SipInfo";
+    /**
+     * 电话呼叫对象
+     */
+    private List<VvsipCall> mVvsipCalls = null;
+
+
+    private static SipHelper sipHelper;
+
+    public Handler getSipRegisterHandler() {
+        return sipRegisterHandler;
+    }
+
+    public Runnable getSipRegisterRunnable() {
+        return sipRegisterRunnable;
+    }
+
+    public List<VvsipCall> getmVvsipCalls() {
+        return mVvsipCalls;
+    }
+
+    private IVvsipServiceListener sipListner;
+
+
+    private Context mContext;
+
+    public static SipHelper getInstance() {
+        if (sipHelper == null) {
+            synchronized (SipHelper.class) {
+                if (sipHelper == null) {
+                    sipHelper = new SipHelper();
+                }
+            }
+        }
+        return sipHelper;
+    }
+
+    /**
+     * Instantiates a new Sip register util.
+     */
+    private SipHelper() {
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+
+        // Runnable exiting the splash screen and launching the menu
+        sipRegisterRunnable = new Runnable() {
+            public void run() {
+                isSuccessRegisterSip();
+            }
+        };
+
+        // Run the exitRunnable in in mSipRegisterTime ms
+        sipRegisterHandler = new Handler();
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (sipservice != null) {
+            sipRegisterHandler.postDelayed(sipRegisterRunnable, 3000);
+            return;
+        }
+        sipRegisterHandler.postDelayed(sipRegisterRunnable, mSipRegisterTime);
+    }
+
+    public void initSip(Context context, String ip, String id, String pwd) {
+        mContext = context;
+        sipIP = ip;
+        sipID = id;
+        sipPWD = pwd;
+    }
+
+    public void setSipListner(IVvsipServiceListener listner) {
+        sipListner = listner;
+    }
+
+    /*public void addSipListner(IVvsipServiceListener listner) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null && listner != null) {
+            sipService.addListener(listner);
+            Log.d("sip", "add sip listner");
+        }
+    }
+
+    public void removeSipListner(IVvsipServiceListener listner) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null && listner != null) {
+            sipService.removeListener(listner);
+            Log.d("sip", "remove sip listner");
+        }
+    }*/
+
+    /**
+     * 检测Sip服务是否注册成功
+     */
+    public void isSuccessRegisterSip() {
+        VvsipTask vvsipTask = VvsipTask.getVvsipTask();
+        if (vvsipTask != null && VvsipTask.global_failure != 0) {
+            /**
+             * ==================================sip服务启动失败 ================================
+             */
+        }
+    }
+
+    /**
+     * 注销Sip服务
+     */
+    public void unRegisterSip() {
+        //LogUtil.i(SipInfoTag, "lifecycle // onDestroy");
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (sipservice != null && sipListner != null) {
+            sipservice.removeListener(sipListner);
+        }
+
+        getSipServiceStartHandler().removeCallbacks(getSipServiceStartRunnable());
+        sipRegisterHandler.removeCallbacks(sipRegisterRunnable);
+        if (getSipServiceConnection() != null && isRegister) {
+            try {
+                mContext.unbindService(getSipServiceConnection());
+                setSipServiceConnection(null);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        if (mVvsipCalls != null) {
+            mVvsipCalls.clear();
+            mVvsipCalls = null;
+        }
+
+        //Log.i(SipInfoTag, "lifecycle // onDestroy");
+    }
+
+    public static String sipStatus = "";
+
+    /**
+     * Sip信息获取
+     */
+    public void obtainSipInfo() {
+        if (sipStatus.equals(REGISTERCOM) && NetHelper.NetConn) {//sip注册成功并且以太网连上
+            return;
+        }
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null && sipListner != null) {
+            //sipService.addListener(sipListner);
+            sipService.setMessageHandler(messageHandler);
+        } else {
+            //LogUtil.i(SipInfoTag, "lifecycle // _service==null");
+        }
+        sipRegister();
+        failUiRefreshSip();
+    }
+
+    private void failUiRefreshSip() {
+        if (!NetHelper.NetConn) {
+            sipStatus = REGISTERFAIL;
+            EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+            if (mSipThread != null) {
+                mSipThread.interrupt();
+                mSipThread = null;
+            }
+            //LogUtil.e(SipInfoTag, "以太网断开,SIP UI状态刷新为失败");
+        }
+    }
+
+
+    /**
+     * Sip信息
+     */
+    private String sipinfo = "";
+    private static int handleCount = 0;
+    //Sip註冊次數
+    private CountDownTimer mCountDownAutoTimer;
+    @SuppressLint("HandlerLeak")
+    private Handler messageHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            //LogUtil.i("QASE", "handleMessage==" + " msg.obj==" + msg.obj.toString() + " msg.what==" + msg.what);
+            //LogUtil.i(SipInfoTag, "#" + msg.obj);
+            sipinfo = "" + msg.obj + sipinfo;
+            //LogUtil.i(SipInfoTag, "Sip信息" + sipinfo);
+
+            if (msg.what == 22) {//释放资源
+                //EventBus.getDefault().post(new MessageEvent(msg.what, EVENT_SIP_REGISTER_STATUS));
+            }
+
+            if (sipinfo.contains("200 OK")) {//注册成功
+                sipStatus = REGISTERCOM;
+                EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_REGISTER_STATUS));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                if (msg.obj.toString().contains("408")) {//超时
+                    sipStatus = REGISTERFAIL;
+                    EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+                    sipRegister();
+                }
+            } else {//注册失败
+                sipStatus = REGISTERFAIL;
+                EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                sipRegister();
+            }
+            failUiRefreshSip();
+
+            if (msg.obj.toString().contains("autocall")) {
+                VvsipCall pCall = null;
+                //LogUtil.e(SipInfoTag, "onClick1");
+                for (VvsipCall _pCall : mVvsipCalls) {
+                    if (_pCall.cid > 0)
+                        //LogUtil.e(SipInfoTag, "state#" + _pCall.mState);
+                        if (_pCall.cid > 0 && _pCall.mState <= 2) {
+                            pCall = _pCall;
+                            break;
+                        }
+                }
+                //LogUtil.e(SipInfoTag, "onClick2");
+                if (pCall == null)
+                    return;
+                //LogUtil.e(SipInfoTag, "onClick3#" + pCall.mState);
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                pCall.stop();
+                _service.setSpeakerModeOff();
+            }
+        }
+    };
+
+    /**
+     * ====================Sip注册======================
+     */
+    private Thread mSipThread;
+
+    private static class SipThread extends Thread {
+        WeakReference<Activity> mThreadCallingBedActivity;
+
+        private SipThread(Activity activity) {
+            mThreadCallingBedActivity = new WeakReference<Activity>(activity);
+        }
+
+        @Override
+        public void run() {
+            super.run();
+            if (mThreadCallingBedActivity == null)
+                return;
+            if (mThreadCallingBedActivity.get() != null) {
+                IVvsipService sipService = VvsipService.getService();
+                try {
+                    if (sipService != null && !SipHelper.getInstance().sipinfo.contains("200 OK") && NetHelper.NetConn) {
+                        sipStatus = REGISTERING;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERING, EVENT_SIP_REGISTER_STATUS));
+                        sipService.register(sipIP + SIP_IP_END, sipID, sipPWD);
+                        handleCount++;
+                        Log.e(SipInfoTag, "以太网连接,SIP UI状态刷新为注册中");
+                    } else if (sipService != null && SipHelper.getInstance().sipinfo.contains("200 OK")) {
+                        sipStatus = REGISTERCOM;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_REGISTER_STATUS));
+                    }
+                } catch (NullPointerException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void sipRegister() {
+        synchronized (this) {
+            mSipThread = new SipThread((Activity) mContext);
+            if (handleCount < 3) {
+                if (mCountDownAutoTimer == null) {
+                    mCountDownAutoTimer = new CountDownTimer(10000, 1000) {
+                        @Override
+                        public void onTick(long l) {
+                        }
+
+                        @Override
+                        public void onFinish() {
+                            handleCount = 0;
+                            if (mSipThread != null) {
+                                mSipThread.start();
+                            }
+                            if (mCountDownAutoTimer != null) {
+                                mCountDownAutoTimer.cancel();
+                                mCountDownAutoTimer = null;
+                            }
+                        }
+                    };
+                    mCountDownAutoTimer.start();
+                }
+                return;
+            } else {
+                if (mCountDownAutoTimer != null) {
+                    mCountDownAutoTimer.cancel();
+                    mCountDownAutoTimer = null;
+                }
+            }
+            if (handleCount == 0) {
+                mSipThread.start();
+            }
+        }
+    }
+
+
+    public void setmSipThread(Thread mSipThread) {
+        this.mSipThread = mSipThread;
+    }
+
+    public Thread getmSipThread() {
+        return mSipThread;
+    }
+
+    /**
+     * 开始通话
+     */
+    public void startCall(String sipUseName) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService == null) return;
+        //----------------------------------------------携带呼叫列表转接床头机的Mac地址--------------------------------------------------//
+        sipService.initiateOutgoingCall(sipUseName, "");
+    }
+
+    /**
+     * 结束通话
+     */
+    public void endCall() {
+        VvsipCall call = null;
+        for (VvsipCall pCall : mVvsipCalls) {
+            if (pCall.cid > 0 && pCall.mState <= 2) {
+                call = pCall;
+                break;
+            }
+        }
+        if (call == null) return;
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService == null) return;
+        VvsipTask sipTask = sipService.getVvsipTask();
+        if (sipTask == null) return;
+        VvsipService.getService().mainEndCall(EXOSIP_CALL_CLOSED);
+        call.stop();
+        sipService.setSpeakerModeOff();
+        sipService.stopPlayer();
+        sipService.setAudioNormalMode();
+    }
+
+    /**
+     * 添加一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void addCallObject(final VvsipCall call) {
+        try {
+            if (call == null) {
+                return;
+            }
+
+            if (mVvsipCalls == null)
+                return;
+            mVvsipCalls.add(call);
+        } catch (Exception e) {
+            //LogUtil.e(SipInfoTag, "onNewVvsipCallEvent: " + e);
+        }
+    }
+
+    /**
+     * 移除一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void removeCallObject(final VvsipCall call) {
+        try {
+            if (call == null) {
+                return;
+            }
+
+            // 4 crash detected here for 4.0.9 with mVvsipCalls=NULL
+            if (mVvsipCalls == null)
+                return;
+            mVvsipCalls.remove(call);
+        } catch (Exception e) {
+            //Log.e(SipInfoTag, "onRemoveVvsipCallEvent: " + e);
+        }
+    }
+
+    /**
+     * 自动接电话
+     */
+    public void autoTalking() {
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+        for (VvsipCall _pCall : mVvsipCalls) {
+            if (_pCall.cid > 0 && _pCall.mState < 2 && _pCall.mIncomingCall) {
+                // ANSWER EXISTING CALL
+                int i = _pCall.answer(200, 1);
+              //LogUtil.d(SipInfoTag, "ANSWER EXISTING CALL");
+                IVvsipService _service = VvsipService.getService();
+                if (_service != null) {
+                    if (i >= 0) {
+                        _service.stopPlayer();
+                        _service.setSpeakerModeOff();
+                        _service.setAudioInCallMode();
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    public static boolean isServiceRunning(Context context, String className) {
+        boolean isRunning = false;
+        ActivityManager activityManager = (ActivityManager) context
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningServiceInfo> serviceList = activityManager
+                .getRunningServices(30);
+
+        if (!(serviceList.size() > 0)) {
+            return false;
+        }
+
+        for (int i = 0; i < serviceList.size(); i++) {
+            if (serviceList.get(i).service.getClassName().equals(className) == true) {
+                isRunning = true;
+                break;
+            }
+        }
+        return isRunning;
+    }
+
+    /**
+     * #############################
+     * <p>
+     * Sip启动服务.
+     * <p>
+     * #############################
+     */
+    private static Handler sipServiceStartHandler = null;
+    private static Runnable sipServiceStartRunnable = null;
+    private static ServiceConnection sipServiceConnection;
+
+    public static Runnable getSipServiceStartRunnable() {
+        return sipServiceStartRunnable;
+    }
+
+    public static Handler getSipServiceStartHandler() {
+        return sipServiceStartHandler;
+    }
+
+    public static ServiceConnection getSipServiceConnection() {
+        return sipServiceConnection;
+    }
+
+    public static void setSipServiceConnection(ServiceConnection sipServiceConnections) {
+        sipServiceConnection = sipServiceConnections;
+    }
+
+    /**
+     * 启动服务
+     */
+    public static Boolean isRegister = false;//是否注册
+
+    public void sipStartService() {
+        sipServiceStartHandler = new Handler();
+
+        sipServiceStartRunnable = new Runnable() {
+            public void run() {
+                if (mContext == null) {
+                    sipServiceStartHandler.postDelayed(sipServiceStartRunnable, 1000);
+                    return;
+                }
+                Intent intent = new Intent(mContext.getApplicationContext(), VvsipService.class);
+                mContext.startService(intent);
+
+                sipServiceConnection = new ServiceConnection() {
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IVvsipService sipservice = ((VvsipServiceBinder) service).getService();
+                        if (sipservice != null && sipListner != null) {
+                            //LogUtil.i(SipInfoTag, "Connected!");
+                            sipservice.addListener(sipListner);
+                            SipHelper.getInstance().obtainSipInfo();//Sip信息获取
+                        }
+                    }
+
+                    public void onServiceDisconnected(ComponentName name) {
+                        //LogUtil.i(SipInfoTag, "Disconnected!");
+                    }
+                };
+
+                isRegister = mContext.bindService(intent, sipServiceConnection, Context.BIND_AUTO_CREATE);
+            }
+        };
+
+        sipServiceStartHandler.postDelayed(sipServiceStartRunnable, 0);
+    }
+
+    /**
+     * 用来解析错误代码
+     */
+    public static String analyseErrorCode(String errorCode) {
+        switch (errorCode) {
+            case "200":
+                return "成功";
+            case "408":
+                return "请求超时";
+            case "400":
+                return "服务器不支持请求的语法";
+            case "401":
+                return "未授权";
+            case "403":
+                return "服务器禁止请求";
+            case "404":
+                return "服务器找不到";
+            default:
+                return "未知错误";
+        }
+    }
+
+}

+ 17 - 0
callingbed2/src/main/java/com/wdkl/app/ncs/callingbed2/sip/SipStatus.java.bak

@@ -0,0 +1,17 @@
+package com.wdkl.app.ncs.callingbed2.sip;
+
+public class SipStatus {
+    /**
+     * 注册中
+     */
+    public static final String REGISTERING = "register_ing";
+    /**
+     * 注册失败
+     */
+    public static final String REGISTERFAIL = "register_fail";
+    /**
+     * 注册完成
+     */
+    public static final String REGISTERCOM = "register_com";
+
+}

+ 7 - 0
callingbed2/src/main/res/anim/slide_down_in.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromYDelta="100%p"
+        android:toYDelta="0%p"
+        android:duration="300"/>
+</set>

+ 7 - 0
callingbed2/src/main/res/anim/slide_left_in.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromXDelta="-100%p"
+        android:toXDelta="0%p"
+        android:duration="300"/>
+</set>

+ 7 - 0
callingbed2/src/main/res/anim/slide_right_out.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromXDelta="0%p"
+        android:toXDelta="100%p"
+        android:duration="300"/>
+</set>

+ 7 - 0
callingbed2/src/main/res/anim/slide_up_out.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+    <translate
+        android:fromYDelta="0%p"
+        android:toYDelta="-100%p"
+        android:duration="300"/>
+</set>

File diff suppressed because it is too large
+ 34 - 0
callingbed2/src/main/res/drawable-v24/ic_launcher_foreground.xml


BIN
callingbed2/src/main/res/drawable/ic_answer.png


BIN
callingbed2/src/main/res/drawable/ic_answer_normal.png


BIN
callingbed2/src/main/res/drawable/ic_answer_press.png


BIN
callingbed2/src/main/res/drawable/ic_hangup.png


BIN
callingbed2/src/main/res/drawable/ic_hangup_normal.png


BIN
callingbed2/src/main/res/drawable/ic_hangup_press.png


+ 170 - 0
callingbed2/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+    <path
+        android:fillColor="#008577"
+        android:pathData="M0,0h108v108h-108z" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M9,0L9,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,0L19,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,0L29,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,0L39,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,0L49,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,0L59,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,0L69,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,0L79,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M89,0L89,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M99,0L99,108"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,9L108,9"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,19L108,19"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,29L108,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,39L108,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,49L108,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,59L108,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,69L108,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,79L108,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,89L108,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M0,99L108,99"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,29L89,29"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,39L89,39"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,49L89,49"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,59L89,59"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,69L89,69"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M19,79L89,79"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M29,19L29,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M39,19L39,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M49,19L49,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M59,19L59,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M69,19L69,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+    <path
+        android:fillColor="#00000000"
+        android:pathData="M79,19L79,89"
+        android:strokeWidth="0.8"
+        android:strokeColor="#33FFFFFF" />
+</vector>

BIN
callingbed2/src/main/res/drawable/ic_nurse.png


BIN
callingbed2/src/main/res/drawable/ic_nurse_call.png


+ 0 - 0
callingbed2/src/main/res/drawable/selector_action_button_bg.xml


Some files were not shown because too many files changed in this diff