Browse Source

新增红米手机模块代码

weizhengliang 2 years ago
parent
commit
a442ac5e8e
100 changed files with 12822 additions and 126 deletions
  1. 11 46
      android_mobile/build.gradle
  2. 1 1
      android_mobile/src/main/yd_w_qin2_2/AndroidManifest.xml
  3. 3 1
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/AppUpdateActivity.kt
  4. 3 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/BaseActivity.kt
  5. 1 2
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/TakeoverActivity.kt
  6. 2 2
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt
  7. 3 2
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java
  8. 3 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/ChannelImItemAdapter.kt
  9. 2 1
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/ClerkAdapter.java
  10. 3 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/NewEventItemAdapter.kt
  11. 3 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemAdapter.kt
  12. 3 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemSearchAdapter.kt
  13. 3 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/WatchCallRecordsItemAdapter.kt
  14. 5 5
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/WatchContactsItemAdapter.kt
  15. 1 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/binding/HomeFragmentBindHelper.kt
  16. 1 5
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/helper/AppUpdateHelper.java
  17. 2 1
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/search/FuzzySearchBaseAdapter.java
  18. 3 1
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/service/FloatingService.java
  19. 3 2
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/CallSingleActivity.java
  20. 2 1
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/FragmentAudio.java
  21. 2 1
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/FragmentVideo.java
  22. 4 3
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/SingleCallFragment.java
  23. 3 2
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/util/ClerkDialogHelper.java
  24. 1 2
      android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/util/TimeTransition.kt
  25. 1 1
      android_mobile/src/main/yd_w_qin2_2/res/layout/activity_channel_im_list.xml
  26. 2 2
      android_mobile/src/main/yd_w_qin2_2/res/layout/activity_event_list.xml
  27. 2 2
      android_mobile/src/main/yd_w_qin2_2/res/layout/activity_takeover.xml
  28. 30 18
      android_mobile/src/main/yd_w_qin2_2/res/layout/activity_voice_msg.xml
  29. 1 1
      android_mobile/src/main/yd_w_qin2_2/res/layout/clerk_dialog_lay.xml
  30. 1 1
      android_mobile/src/main/yd_w_qin2_2/res/layout/watch_activity_call_records.xml
  31. 2 2
      android_mobile/src/main/yd_w_qin2_2/res/layout/watch_contacts_lay.xml
  32. 118 0
      android_mobile/src/main/yd_w_xiaomi_2/AndroidManifest.xml
  33. 7 0
      android_mobile/src/main/yd_w_xiaomi_2/aidl/com/android/internal/telephony/ITelephony.java
  34. 188 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/AppUpdateActivity.kt
  35. 349 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/BaseActivity.kt
  36. 268 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/ChannelImActivity.kt
  37. 200 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/ContactUpdateActivity.kt
  38. 239 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/NewEventListActivity.kt
  39. 73 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/SmsReceivedActivity.kt
  40. 174 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/TakeoverActivity.kt
  41. 224 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/VoiceMsgActivity.kt
  42. 312 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchCallRecordsActivity.kt
  43. 157 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchContactsActivity.kt
  44. 931 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt
  45. 267 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java
  46. 126 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/ChannelImItemAdapter.kt
  47. 45 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/ContactUpdateAdapter.kt
  48. 230 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/NewEventItemAdapter.kt
  49. 56 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/NumAdapter.java
  50. 155 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemAdapter.kt
  51. 152 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemSearchAdapter.kt
  52. 227 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/WatchCallRecordsItemAdapter.kt
  53. 121 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/WatchContactsItemAdapter.kt
  54. 36 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/binding/HomeFragmentBindHelper.kt
  55. 53 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/broadcast/BatteryBroadcastReceiver.java
  56. 34 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/broadcast/MyMediaButtonReceiver.kt
  57. 22 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/broadcast/NetworkBroadcastReceiver.kt
  58. 36 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/di/HomeComponent.kt
  59. 50 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/entity/ContactItemEntity.java
  60. 244 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/helper/AppUpdateHelper.java
  61. 203 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/helper/HttpHelper.java
  62. 64 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/helper/SerialPortHelper.java
  63. 27 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/launch/HomeLaunch.kt
  64. 35 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/DefaultFuzzySearchRule.java
  65. 87 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/FuzzySearchBaseAdapter.java
  66. 12 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/IAZItem.java
  67. 24 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/IFuzzySearchItem.java
  68. 20 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/IFuzzySearchRule.java
  69. 897 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/PinyinUtil.java
  70. 276 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/FloatingService.java
  71. 197 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/PhoneStateReceiver.java
  72. 21 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/WdBootReceiver.java
  73. 1059 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt
  74. 108 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/settingconfig/SettingConfig.java
  75. 464 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/CallSingleActivity.java
  76. 437 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/FragmentAudio.java
  77. 286 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/FragmentVideo.java
  78. 579 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/SingleCallFragment.java
  79. 15 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/ActivityStackUtil.java
  80. 234 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/AnrFcExceptionUtil.java
  81. 76 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/AppUtils.java
  82. 187 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/AsyncPlayer.java
  83. 257 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/DeviceIdUtils.java
  84. 14 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/EthernetWifiCallBack.java
  85. 64 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/HandleTcpConnect.kt
  86. 90 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/HomeWatcher.java
  87. 63 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/IMDialogHelper.java
  88. 6 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/ISpeechCallback.java
  89. 82 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/ImPlayDialogHelper.java
  90. 140 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/LanguageSetDialogHelper.java
  91. 83 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/LocaleMangerUtils.java
  92. 52 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/LogUpload.java
  93. 303 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/MediaPlayHelper.java
  94. 115 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/MediaPlayer.kt
  95. 600 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/NetHelper.java
  96. 72 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/NetWorkChangeReceiver.kt
  97. 127 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/PasswordDialogHelper.java
  98. 112 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/PhoneCallUtil.java
  99. 133 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/PhoneNumberDialogHelper.java
  100. 0 0
      android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/RecordHelper.java

+ 11 - 46
android_mobile/build.gradle

@@ -1,9 +1,4 @@
-if (componentTag) {
-    apply plugin: 'com.android.application'
-    apply plugin: 'com.enation.javashop.aspectjrt'
-} else {
-    apply plugin: 'com.android.library'
-}
+apply plugin: 'com.android.library'
 
 apply plugin: 'kotlin-android'
 apply plugin: 'kotlin-android-extensions'
@@ -23,10 +18,6 @@ android {
     aaptOptions.noCompress("mp3","wav")
 
     defaultConfig {
-        if (componentTag) {
-            applicationId "com.wdkl.ncs.android.component.home"
-            multiDexEnabled true
-        }
         minSdkVersion min_sdk_version
         targetSdkVersion target_sdk_version
         versionCode app_version_code
@@ -55,6 +46,11 @@ android {
             main.java.srcDirs += 'src/main/yd_w_qin2_2/aidl'
             main.res.srcDirs += 'src/main/yd_w_qin2_2/res'
             main.manifest.srcFile 'src/main/yd_w_qin2_2/AndroidManifest.xml'
+        } else if ("yd_w_xiaomi_2" == app_device_type) {
+            main.java.srcDirs += 'src/main/yd_w_xiaomi_2/code'
+            main.java.srcDirs += 'src/main/yd_w_xiaomi_2/aidl'
+            main.res.srcDirs += 'src/main/yd_w_xiaomi_2/res'
+            main.manifest.srcFile 'src/main/yd_w_xiaomi_2/AndroidManifest.xml'
         }
     }
     compileOptions {
@@ -73,32 +69,16 @@ String getDate() {
 
 dependencies {
     compile fileTree(dir: 'libs', include: ['*.aar'])
-    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')
-
-    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'
-    }
+    api project(':middleware')
 
     /**
      * Dagger编译依赖
@@ -106,11 +86,6 @@ dependencies {
     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"
@@ -118,25 +93,15 @@ dependencies {
     /**
      *  constraint-layout布局依赖
      */
-    implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    //implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
+    //implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
     //web rtc
     implementation project(':keepalive')
     implementation project(':janus')
     implementation 'org.webrtc:google-webrtc:1.0.32006'
 }
 
-/**
- * kawo组件化框架配置
- */
-if (componentTag) {
-    kawo {
-        /**
-         * Aop注解排除Jar
-         */
-        aspectExcludeJarFilter 'com.enation.geamtear.pay', 'AlipaySdk'
-    }
-}
 repositories {
 
 }

+ 1 - 1
android_mobile/src/main/yd_w_qin2_2/AndroidManifest.xml

@@ -87,7 +87,7 @@
         </receiver>
 
         <provider
-            android:name="android.support.v4.content.FileProvider"
+            android:name="androidx.core.content.FileProvider"
             android:authorities="${applicationId}.provider"
             tools:replace="android:authorities"
             android:grantUriPermissions="true"

+ 3 - 1
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/AppUpdateActivity.kt

@@ -22,6 +22,7 @@ import com.wdkl.ncs.android.middleware.common.Constants
 import com.wdkl.ncs.android.middleware.logic.contract.home.AppUpdateContract
 import com.wdkl.ncs.android.middleware.logic.presenter.home.AppUpdatePresenter
 import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.utils.CommonUtils
 import kotlinx.android.synthetic.main.activity_app_update.*
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
@@ -64,7 +65,8 @@ class AppUpdateActivity : BaseActivity<AppUpdatePresenter, ActivityAppUpdateBind
     }
 
     override fun handleAppVersion(appInfo: AppVersionDO) {
-        if (BuildConfig.VERSION_CODE < appInfo.versionNo){
+        val versionCode = CommonUtils.getAppVersionCode(activity)
+        if (versionCode < appInfo.versionNo){
             if (!TextUtils.isEmpty(appInfo.appPath)) {
                 downLoadAPK(urlManager.base + ":8006/" + appInfo.appPath)
             } else {

+ 3 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/BaseActivity.kt

@@ -2,8 +2,6 @@ package com.wdkl.ncs.android.component.home.activity
 
 import android.content.Context
 import android.content.Intent
-import android.databinding.DataBindingUtil
-import android.databinding.ViewDataBinding
 import android.os.Bundle
 import android.os.Handler
 import android.os.Looper
@@ -12,6 +10,8 @@ import android.view.KeyEvent
 import android.view.View
 import android.view.WindowManager
 import android.view.inputmethod.InputMethodManager
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
 import com.enation.javashop.android.jrouter.JRouter
 import com.enation.javashop.utils.base.tool.BaseToolActivity
 import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
@@ -105,7 +105,7 @@ abstract class BaseActivity<PresenterType : BaseContract.BasePresenter, DataBind
         /**创建根视图*/
         val rootView = layoutInflater.inflate(getLayId(), null, false)
         /**初始化Databinding对象*/
-        mViewBinding = DataBindingUtil.bind(rootView)
+        mViewBinding = DataBindingUtil.bind(rootView)!!
         /**设置根视图到Activity*/
         setContentView(rootView)
         /**执行抽象方法初始化Dagger相应操作*/

+ 1 - 2
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/TakeoverActivity.kt

@@ -1,9 +1,8 @@
 package com.wdkl.ncs.android.component.home.activity
 
-import android.support.v7.widget.SearchView
-import android.text.TextUtils
 import android.util.Log
 import android.view.View
+import androidx.appcompat.widget.SearchView
 import com.alibaba.android.vlayout.VirtualLayoutManager
 import com.enation.javashop.net.engine.model.NetState
 import com.google.gson.JsonArray

+ 2 - 2
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt

@@ -113,7 +113,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         //注册广播
         regReceiver()
 
-        tv_register_version.text = "v" + BuildConfig.VERSION_NAME
+        tv_register_version.text = "v" + CommonUtils.getAppVersionName(activity)
 
         //TTS初始化
 //        SpeechUtil.getInstance().init(BaseApplication.appContext)
@@ -384,7 +384,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
     }
 
     override fun handleAppVersion(appInfo: AppVersionDO) {
-        if (BuildConfig.VERSION_CODE < appInfo.versionNo && !AppUpdateActivity.opened) {
+        if (CommonUtils.getAppVersionCode(activity) < appInfo.versionNo && !AppUpdateActivity.opened) {
             showMessage(R.string.new_version_tips)
             val intent = Intent()
             intent.setClass(this, AppUpdateActivity::class.java)

+ 3 - 2
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java

@@ -5,7 +5,6 @@ import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.provider.Settings;
-import android.support.annotation.Nullable;
 import android.util.Log;
 import android.view.View;
 import android.widget.AdapterView;
@@ -17,6 +16,8 @@ import android.widget.Spinner;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
 import com.wdkl.ncs.android.component.home.BuildConfig;
 import com.wdkl.ncs.android.component.home.R;
 import com.wdkl.ncs.android.component.home.service.WdKeepAliveService;
@@ -81,7 +82,7 @@ public class WatchUserSettingActivity extends Activity {
         imChannelYes = findViewById(R.id.rb_im_channel_yes);
         imChannelNo = findViewById(R.id.rb_im_channel_no);
 
-        tvAppVersion.setText(BuildConfig.VERSION_NAME + "_" + BuildConfig.BUILD_TIME);
+        tvAppVersion.setText(CommonUtils.getAppVersionName(BaseApplication.appContext) + "_" + BuildConfig.BUILD_TIME);
         tvDeviceId.setText("" + Constants.Companion.getDeviceId());
         tvDeviceImei.setText(Constants.Companion.getImei());
         tvDeviceIp.setText(NetHelper.getInstance().getLocalIP());

+ 3 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/ChannelImItemAdapter.kt

@@ -63,15 +63,15 @@ class ChannelImItemAdapter(val data: ArrayList<ChannelIMVO>, val imActivity: Act
     /**
      * 创建ViewHolder
      */
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding> {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding> {
         return BaseRecyclerViewHolder.build(parent, R.layout.adapter_channel_im_records_item)
     }
 
     /**
      * 绑定数据
      */
-    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding>?, position: Int) {
-        holder?.bind { binding ->
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding>, position: Int) {
+        holder.bind { binding ->
             val itemData = getItem(position)
 
             binding.imCallTimeTv.text = TimeTransition().stampToTime(itemData.sendTime*1000)

+ 2 - 1
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/ClerkAdapter.java

@@ -1,6 +1,5 @@
 package com.wdkl.ncs.android.component.home.adapter;
 
-import android.support.v7.widget.RecyclerView;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -9,6 +8,8 @@ import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.wdkl.ncs.android.component.home.R;
 import com.wdkl.ncs.android.middleware.common.Constants;
 import com.wdkl.ncs.android.middleware.model.dos.ClerkDO;

+ 3 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/NewEventItemAdapter.kt

@@ -47,7 +47,7 @@ class NewEventItemAdapter(var data:ArrayList<InteractionVO>, val activity: Activ
         return true
     }
 
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseRecyclerViewHolder<EventListItemBinding> {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<EventListItemBinding> {
         return BaseRecyclerViewHolder.build(parent, R.layout.event_list_item)
     }
 
@@ -60,8 +60,8 @@ class NewEventItemAdapter(var data:ArrayList<InteractionVO>, val activity: Activ
         return LinearLayoutHelper(0,data.size)
     }
 
-    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<EventListItemBinding>?, position: Int) {
-        holder?.bind { binding ->
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<EventListItemBinding>, position: Int) {
+        holder.bind { binding ->
             val itemData = getItem(position)
 
             var toDeviceId:Int?

+ 3 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemAdapter.kt

@@ -54,12 +54,12 @@ class TakeoverItemAdapter(var data:ArrayList<JsonObject>, val context: Context)
         return LinearLayoutHelper(0,data.size)
     }
 
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseRecyclerViewHolder<TakeoverItemBinding> {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<TakeoverItemBinding> {
         return BaseRecyclerViewHolder.build(parent, R.layout.takeover_item)
     }
 
-    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<TakeoverItemBinding>?, position: Int) {
-        holder?.bind {
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<TakeoverItemBinding>, position: Int) {
+        holder.bind {
             binding ->
             val itemData = getItem(position)
             binding.clerkName.setText(R.string.str_null)

+ 3 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemSearchAdapter.kt

@@ -3,7 +3,6 @@ package com.wdkl.ncs.android.component.home.adapter
 import android.content.Context
 import android.content.Intent
 import android.net.Uri
-import android.support.v7.widget.RecyclerView
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.View
@@ -11,6 +10,7 @@ import android.view.ViewGroup
 import android.widget.Button
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
 import com.enation.javashop.utils.base.tool.CommonTool
 import com.google.common.base.Strings
 import com.wdkl.ncs.android.component.home.R
@@ -44,8 +44,8 @@ class TakeoverItemSearchAdapter : FuzzySearchBaseAdapter<ContactItemEntity, Take
         this.context = context
     }
 
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ItemHolder {
-        val view = LayoutInflater.from(parent!!.getContext()).inflate(R.layout.takeover_item, parent, false)
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
+        val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.takeover_item, parent, false)
         return ItemHolder(view)
     }
 

+ 3 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/WatchCallRecordsItemAdapter.kt

@@ -52,15 +52,15 @@ class WatchCallRecordsItemAdapter(val data: ArrayList<InteractionVO>) : BaseDele
     /**
      * 创建ViewHolder
      */
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding> {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding> {
         return BaseRecyclerViewHolder.build(parent, R.layout.adapter_watch_call_records_item)
     }
 
     /**
      * 绑定数据
      */
-    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding>?, position: Int) {
-        holder?.bind { binding ->
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding>, position: Int) {
+        holder.bind { binding ->
             val itemData = getItem(position)
 
             if (itemData.createDate != null) {

+ 5 - 5
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/adapter/WatchContactsItemAdapter.kt

@@ -3,9 +3,9 @@ package com.wdkl.ncs.android.component.home.adapter
 import android.content.Context
 import android.content.Intent
 import android.net.Uri
-import android.support.v7.widget.RecyclerView
 import android.util.Log
 import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
 import com.alibaba.android.vlayout.LayoutHelper
 import com.alibaba.android.vlayout.layout.LinearLayoutHelper
 import com.enation.javashop.utils.base.tool.CommonTool
@@ -53,17 +53,17 @@ class WatchContactsItemAdapter(val data:ArrayList<WatchContactsVO>, val context:
         return LinearLayoutHelper(0,data.size)
     }
 
-    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseRecyclerViewHolder<AdapterWatchContactsItemBinding> {
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterWatchContactsItemBinding> {
         return BaseRecyclerViewHolder.build(parent, R.layout.adapter_watch_contacts_item)
     }
 
-    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView?) {
+    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
         super.onDetachedFromRecyclerView(recyclerView)
         loadingDialog.dismiss()
     }
 
-    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterWatchContactsItemBinding>?, position: Int) {
-        holder?.bind {
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterWatchContactsItemBinding>, position: Int) {
+        holder.bind {
             binding ->
             val itemData = getItem(position)
 //            if(position == thisPosition){

+ 1 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/binding/HomeFragmentBindHelper.kt

@@ -1,8 +1,6 @@
 package com.wdkl.ncs.android.component.home.binding
 
-import android.databinding.BindingAdapter
-import android.databinding.ObservableField
-import android.support.constraint.ConstraintLayout
+import androidx.databinding.ObservableField
 
 /**
  * @author LDD

+ 1 - 5
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/helper/AppUpdateHelper.java

@@ -8,13 +8,9 @@ import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Environment;
-import android.support.v4.BuildConfig;
-import android.support.v4.content.FileProvider;
 import android.util.Log;
 
-import com.google.common.base.Strings;
-import com.wdkl.ncs.android.component.home.activity.WatchHome2Activity;
-import com.wdkl.ncs.android.lib.utils.ShellUtils;
+import androidx.core.content.FileProvider;
 
 import java.io.BufferedReader;
 import java.io.File;

+ 2 - 1
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/search/FuzzySearchBaseAdapter.java

@@ -1,10 +1,11 @@
 package com.wdkl.ncs.android.component.home.search;
 
-import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.widget.Filter;
 import android.widget.Filterable;
 
+import androidx.recyclerview.widget.RecyclerView;
+
 import java.util.ArrayList;
 import java.util.List;
 

+ 3 - 1
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/service/FloatingService.java

@@ -7,7 +7,6 @@ import android.content.Intent;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
-import android.support.annotation.Nullable;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -16,6 +15,8 @@ import android.view.View;
 import android.view.WindowManager;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.google.gson.Gson;
 import com.wdkl.ncs.android.component.home.R;
 import com.wdkl.ncs.android.component.home.ui.CallSingleActivity;
@@ -107,6 +108,7 @@ public class FloatingService extends Service {
 
         wmParams.gravity = Gravity.LEFT | Gravity.TOP;
         //wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        wmParams.flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
         //设置悬浮窗口长宽数据
         wmParams.width = 220;
         wmParams.height = 248;

+ 3 - 2
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/CallSingleActivity.java

@@ -15,8 +15,6 @@ import android.os.IBinder;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
-import android.support.v4.app.FragmentManager;
-import android.support.v7.app.AppCompatActivity;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
@@ -24,6 +22,9 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Toast;
 
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentManager;
+
 import com.enation.javashop.utils.base.tool.CommonTool;
 import com.enation.javashop.utils.base.widget.LoadingDialog;
 import com.google.gson.Gson;

+ 2 - 1
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/FragmentAudio.java

@@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
-import android.support.annotation.NonNull;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
@@ -12,6 +11,8 @@ import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+
 import com.blankj.utilcode.util.BarUtils;
 import com.enation.javashop.utils.base.tool.CommonTool;
 import com.google.gson.Gson;

+ 2 - 1
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/FragmentVideo.java

@@ -2,7 +2,6 @@ package com.wdkl.ncs.android.component.home.ui;
 
 import android.content.Context;
 import android.os.Build;
-import android.support.annotation.NonNull;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
@@ -12,6 +11,8 @@ import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
+import androidx.annotation.NonNull;
+
 import com.blankj.utilcode.util.BarUtils;
 import com.wdkl.ncs.android.component.home.R;
 import com.wdkl.rtc.rtc.WebRTCEngine;

+ 4 - 3
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/ui/SingleCallFragment.java

@@ -10,9 +10,6 @@ import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -24,6 +21,10 @@ import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
 import com.google.gson.Gson;
 import com.wdkl.ncs.android.component.home.util.HandleTcpConnect;
 import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;

+ 3 - 2
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/util/ClerkDialogHelper.java

@@ -2,8 +2,6 @@ package com.wdkl.ncs.android.component.home.util;
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -11,6 +9,9 @@ import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Button;
 
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
 import com.wdkl.ncs.android.component.home.R;
 import com.wdkl.ncs.android.component.home.adapter.ClerkAdapter;
 import com.wdkl.ncs.android.middleware.model.dos.ClerkDO;

+ 1 - 2
android_mobile/src/main/yd_w_qin2_2/code/com/wdkl/ncs/android/component/home/util/TimeTransition.kt

@@ -24,7 +24,6 @@ class TimeTransition {
         val date = Date(lt)
         res = simpleDateFormat.format(date)
         return res
-}
-
+    }
 
 }

+ 1 - 1
android_mobile/src/main/yd_w_qin2_2/res/layout/activity_channel_im_list.xml

@@ -53,7 +53,7 @@
             android:layout_marginRight="4dp"
             bind:srlEnableLoadMore="true"
             bind:srlEnableRefresh="true">
-            <android.support.v7.widget.RecyclerView
+            <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/rv_channel_im_list"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"

+ 2 - 2
android_mobile/src/main/yd_w_qin2_2/res/layout/activity_event_list.xml

@@ -23,12 +23,12 @@
             android:layout_marginRight="3sp"
             bind:srlEnableLoadMore="false"
             bind:srlEnableRefresh="false">
-            <android.support.v7.widget.RecyclerView
+            <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/rv_event_list"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:background="#FFBDC3">
-            </android.support.v7.widget.RecyclerView>
+            </androidx.recyclerview.widget.RecyclerView>
         </com.scwang.smartrefresh.layout.SmartRefreshLayout>
 
     </RelativeLayout>

+ 2 - 2
android_mobile/src/main/yd_w_qin2_2/res/layout/activity_takeover.xml

@@ -6,7 +6,7 @@
         android:layout_height="match_parent"
         android:background="#FFBDC3">
 
-        <android.support.v7.widget.SearchView
+        <androidx.appcompat.widget.SearchView
             android:id="@+id/search_view"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -74,7 +74,7 @@
             bind:srlEnableLoadMore="false"
             bind:srlEnableRefresh="true">
 
-            <android.support.v7.widget.RecyclerView
+            <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/takeover_list"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"

+ 30 - 18
android_mobile/src/main/yd_w_qin2_2/res/layout/activity_voice_msg.xml

@@ -7,30 +7,42 @@
     android:gravity="center"
     android:orientation="vertical">
 
-    <Chronometer
-        android:id="@+id/voice_call_timer"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
         android:gravity="center"
-        android:textColor="#B4B4B4"
-        android:textSize="24dp"
-        android:text="00:00" />
+        android:orientation="vertical">
+        <Chronometer
+            android:id="@+id/voice_call_timer"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="#B4B4B4"
+            android:textSize="24sp"
+            android:text="00:00" />
 
-    <Button
-        android:id="@+id/tv_voice_button"
-        android:layout_width="100dp"
-        android:layout_height="100dp"
-        android:layout_marginTop="20dp"
-        android:background="@drawable/selector_record_btn"/>
+        <Button
+            android:id="@+id/tv_voice_button"
+            android:layout_width="100dp"
+            android:layout_height="100dp"
+            android:layout_marginTop="20dp"
+            android:background="@drawable/selector_record_btn"/>
+    </LinearLayout>
 
     <TextView
         android:id="@+id/tv_voice_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="20dp"
-        android:gravity="center"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:paddingTop="20dp"
+        android:gravity="center_horizontal"
         android:text="@string/str_voice_msg_btn_text"
         android:textColor="#B4B4B4"
-        android:textSize="28dp"/>
+        android:textSize="24sp"/>
 </LinearLayout>
 </layout>

+ 1 - 1
android_mobile/src/main/yd_w_qin2_2/res/layout/clerk_dialog_lay.xml

@@ -17,7 +17,7 @@
         android:layout_below="@id/tv_clerks_title"
         android:background="@color/main_color"/>
 
-    <android.support.v7.widget.RecyclerView
+    <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/recycle_view_clerks"
         android:layout_width="match_parent"
         android:layout_height="200dp"

+ 1 - 1
android_mobile/src/main/yd_w_qin2_2/res/layout/watch_activity_call_records.xml

@@ -15,7 +15,7 @@
             bind:srlEnableLoadMore="true"
             bind:srlEnableRefresh="true">
 
-            <android.support.v7.widget.RecyclerView
+            <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/call_records_recyv"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"

+ 2 - 2
android_mobile/src/main/yd_w_qin2_2/res/layout/watch_contacts_lay.xml

@@ -27,12 +27,12 @@
             bind:srlEnableLoadMore="true"
             bind:srlEnableRefresh="true">
 
-            <android.support.v7.widget.RecyclerView
+            <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/watch_contacts_list"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent">
 
-            </android.support.v7.widget.RecyclerView>
+            </androidx.recyclerview.widget.RecyclerView>
         </com.scwang.smartrefresh.layout.SmartRefreshLayout>
 
     </RelativeLayout>

+ 118 - 0
android_mobile/src/main/yd_w_xiaomi_2/AndroidManifest.xml

@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.wdkl.ncs.android.component.home">
+
+    <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+    <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
+    <uses-permission android:name="android.permission.BLUETOOTH"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
+        tools:ignore="ProtectedPermissions" />
+    <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.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+    <!--<uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"/>-->
+    <!-- 读写通讯录权限-->
+    <uses-permission android:name="android.permission.READ_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+    <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
+    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
+
+    <uses-permission android:name="android.permission.SEND_SMS"/>
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
+    <uses-permission android:name="android.permission.WRITE_SMS" />
+
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.READ_CALL_LOG" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
+
+    <queries>
+        <intent>
+            <action android:name="android.intent.action.TTS_SERVICE" />
+        </intent>
+    </queries>
+
+    <application
+        android:allowBackup="true"
+        android:supportsRtl="true">
+
+        <activity android:screenOrientation="portrait" android:name=".activity.WatchContactsActivity" />
+        <activity android:screenOrientation="portrait" android:name=".activity.WatchCallRecordsActivity" />
+        <activity android:screenOrientation="portrait" android:name=".activity.WatchUserSettingActivity" />
+        <activity android:screenOrientation="portrait" android:name=".activity.TakeoverActivity" />
+        <activity android:screenOrientation="portrait" android:name=".activity.AppUpdateActivity" />
+        <activity android:screenOrientation="portrait" android:name=".activity.NewEventListActivity"
+            android:showOnLockScreen="true"
+            android:showWhenLocked="true"
+            android:launchMode="singleTask"/>
+        <activity android:name=".activity.VoiceMsgActivity" android:screenOrientation="portrait"/>
+        <activity android:name=".activity.ChannelImActivity" android:screenOrientation="portrait"/>
+        <activity android:name=".activity.ContactUpdateActivity" android:screenOrientation="portrait"/>
+        <activity android:name=".activity.SmsReceivedActivity" android:screenOrientation="portrait"/>
+
+        <activity
+            android:name=".ui.CallSingleActivity"
+            android:showOnLockScreen="true"
+            android:showWhenLocked="true"
+            android:excludeFromRecents="true"
+            android:screenOrientation="portrait"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:launchMode="singleInstance">
+            <intent-filter>
+                <action android:name="${applicationId}.kit.voip.single" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <service android:name=".service.FloatingService"/>
+
+        <service android:name="com.wdkl.ncs.android.component.home.service.WdKeepAliveService">
+            <intent-filter>
+                <action android:name="com.wdkl.app.ncs.service.WdKeepAliveService"/>
+            </intent-filter>
+        </service>
+<!--        <receiver android:name=".broadcast.MyMediaButtonReceiver">-->
+<!--            <intent-filter>-->
+<!--                <action android:name="android.intent.action.MEDIA_BUTTON" />-->
+<!--                <action android:name="android.media.AUDIO.BECOMING_NOISY" />-->
+<!--            </intent-filter>-->
+<!--        </receiver>-->
+        <service android:name="com.wdkl.ncs.keepbackground.watch.WatchDogService">
+            <intent-filter>
+                <action android:name="com.wdkl.ncs.keepbackground.watch.WatchDogService"/>
+            </intent-filter>
+        </service>
+
+        <receiver android:name=".util.NetWorkChangeReceiver" >
+            <intent-filter>
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+            </intent-filter>
+        </receiver>
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.provider"
+            tools:replace="android:authorities"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
+    </application>
+
+</manifest>

+ 7 - 0
android_mobile/src/main/yd_w_xiaomi_2/aidl/com/android/internal/telephony/ITelephony.java

@@ -0,0 +1,7 @@
+package com.android.internal.telephony;
+
+public interface ITelephony {
+    boolean endCall();
+    void answerRingingCall();
+    void silenceRinger();
+}

+ 188 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/AppUpdateActivity.kt

@@ -0,0 +1,188 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import android.view.WindowManager
+import com.enation.javashop.android.jrouter.external.annotation.Router
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.ncs.android.component.home.BuildConfig
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.databinding.ActivityAppUpdateBinding
+import com.wdkl.ncs.android.component.home.helper.AppUpdateHelper
+import com.wdkl.ncs.android.component.home.helper.HttpHelper
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService
+import com.wdkl.ncs.android.lib.utils.AppTool
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.api.ApiManager
+import com.wdkl.ncs.android.middleware.api.UrlManager
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.logic.contract.home.AppUpdateContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.AppUpdatePresenter
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.utils.CommonUtils
+import kotlinx.android.synthetic.main.activity_app_update.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+@Router(path = "/app/update")
+class AppUpdateActivity : BaseActivity<AppUpdatePresenter, ActivityAppUpdateBinding>(), AppUpdateContract.View {
+    private val TAG = "AppUpdateActivity"
+
+    companion object instance {
+        var opened = false;
+    }
+
+    override fun getLayId(): Int {
+        return R.layout.activity_app_update
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun init() {
+        val window = activity.window
+        //全屏+锁屏+常亮显示
+        window.addFlags(
+            WindowManager.LayoutParams.FLAG_FULLSCREEN or
+                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
+                    WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
+                    WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+        )
+
+        Constants.allowVoiceMsg = false
+        opened = true
+        checkAppVersion()
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun handleAppVersion(appInfo: AppVersionDO) {
+        val versionCode = CommonUtils.getAppVersionCode(activity)
+        if (versionCode > 0 && versionCode < appInfo.versionNo){
+            if (!TextUtils.isEmpty(appInfo.appPath)) {
+                downLoadAPK(ApiManager.urlManager.device_url + appInfo.appPath)
+            } else {
+                showMessage(R.string.download_error)
+                finish()
+            }
+        } else {
+            showMessage(R.string.update_no_required)
+            activity_calling_bed_text_download.setText(R.string.update_no_required)
+            activity_appupdate_dialog_progressview.visibility = View.GONE
+            WdKeepAliveService.updateLastTime = System.currentTimeMillis() / 1000
+            AppTool.Time.delay(1000){
+                finish()
+            }
+        }
+    }
+
+    fun checkAppVersion() {
+        if (Constants.partId != -1) {
+            presenter.getAppVersion(Constants.partId, 7)
+        }
+    }
+
+    /**
+     * 下载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.setText(R.string.updating)
+                }
+                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(R.string.update_fail)
+                        finish()
+                    }
+                }
+
+                override fun onSuccess() {
+                    runOnUiThread {
+                        showMessage(R.string.update_success)
+                        finish()
+                    }
+                }
+            })
+        }.start()
+    }
+
+    override fun bindEvent() {
+        //
+    }
+
+    override fun onBackPressed() {
+        //禁用退出
+    }
+
+    override fun destory() {
+        Constants.allowVoiceMsg = true
+        opened = false
+        //退出时重置呼叫状态
+        //DeviceChannel.calling = false
+    }
+
+    //数据加载错误
+    override fun onError(message: String, type: Int) {
+        showMessage(message)
+        finish()
+    }
+
+    //数据加载完成
+    override fun complete(message: String, type: Int) {
+        //
+    }
+
+    //开始获取数据
+    override fun start() {
+        //
+    }
+
+    //网络监听
+    override fun networkMonitor(state: NetState) {
+        state.filter(onMobile = {
+
+        },onWifi = {
+
+        },offline = {
+
+        })
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+
+    }
+
+
+}

+ 349 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/BaseActivity.kt

@@ -0,0 +1,349 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.annotation.TargetApi
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import android.view.WindowManager
+import android.view.inputmethod.InputMethodManager
+import androidx.databinding.DataBindingUtil
+import androidx.databinding.ViewDataBinding
+import com.enation.javashop.android.jrouter.JRouter
+import com.enation.javashop.utils.base.tool.BaseToolActivity
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils
+import com.wdkl.ncs.android.lib.base.*
+import com.wdkl.ncs.android.lib.utils.debugLog
+import com.wdkl.ncs.android.lib.utils.errorLog
+import org.greenrobot.eventbus.EventBus
+import java.lang.reflect.Field
+import javax.inject.Inject
+
+/**
+ * @author  LDD
+ * @Data   2017/12/26 上午9:26
+ * @From   com.wdkl.ncs.android.lib.base
+ * @Note   Activity基类
+ */
+abstract class BaseActivity<PresenterType : BaseContract.BasePresenter, DataBindingType : ViewDataBinding> : BaseToolActivity(), BaseControl {
+
+    protected var pressDownTime : Long = 0
+    protected var pressUpTime : Long = 0
+    protected var voiceMsgFile : String? = null
+    protected var pressDown : Boolean = false
+
+    private var clickTime : Long = 0
+    private var recording : Boolean = false
+
+
+    /**
+     * @Name  presenter
+     * @Type  T : BaseContract.BasePresenter
+     * @Note  Activity中Presenter Dagger自动注入
+     */
+    @Inject
+    protected lateinit var presenter: PresenterType
+
+    /**
+     * @Name  presenter
+     * @Type  T2 : ViewDataBinding
+     * @Note  DataBinding对象
+     */
+    protected lateinit var mViewBinding: DataBindingType
+
+    /**
+     * @Name  lifecycleCalls
+     * @Type  ArrayList<((state :Int) ->Unit)>
+     * @Note  回调集合
+     */
+    private val lifecycleCalls by lazy { ArrayList<((state :Int) ->Unit)>() }
+
+    /**
+     * @Name  disposableManager
+     * @Type  DisposableManager
+     * @Note  Rx索引管理
+     */
+    protected val disposableManager by lazy { DisposableManager() }
+
+    protected val screen_flags = (
+            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:30
+     * @Note   Activity创建时进行相关的配置
+     */
+    override fun onCreate(savedInstanceState: Bundle?) {
+        //切换语言
+        val languageId: Int = SettingConfig.getLanguageId(this)
+        LocaleMangerUtils.setApplicationLanguageByIndex(this, languageId)
+
+        try {
+            JRouter.prepare().inject(this)
+        }catch (e :Exception){
+            debugLog("Init","首页初始化完毕")
+        }
+        /**父类初始化*/
+        super.onCreate(savedInstanceState)
+
+        window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
+
+        /**执行生命周期监听*/
+        lifeCycleDo(LIFE_CYCLE_CREATE)
+        /**创建根视图*/
+        val rootView = layoutInflater.inflate(getLayId(), null, false)
+        /**初始化Databinding对象*/
+        mViewBinding = DataBindingUtil.bind(rootView)!!
+        /**设置根视图到Activity*/
+        setContentView(rootView)
+        /**执行抽象方法初始化Dagger相应操作*/
+        bindDagger()
+        /**Presenter绑定View*/
+        attachView()
+        /**执行初始化操作*/
+        init()
+        /**执行绑定event操作*/
+        bindEvent()
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/3/29 上午11:46
+     * @Note   执行生命周期监听 恢复
+     */
+    override fun onResume() {
+        super.onResume()
+        /**执行生命周期监听*/
+        lifeCycleDo(LIFE_CYCLE_RESUME)
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/3/29 上午11:46
+     * @Note   执行生命周期监听 暂停
+     */
+    override fun onPause() {
+        super.onPause()
+        /**执行生命周期监听*/
+        lifeCycleDo(LIFE_CYCLE_PAUSE)
+    }
+
+    override fun onStart() {
+        super.onStart()
+        if(!EventBus.getDefault().isRegistered(this)){
+            EventBus.getDefault().register(this)
+        }
+    }
+
+
+    override fun onStop() {
+        super.onStop()
+        if (EventBus.getDefault().isRegistered(this)) {//加上判断
+            EventBus.getDefault().unregister(this)
+        }
+//        EventBus.getDefault().unregister(this)
+    }
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:34
+     * @Note   Activity销毁回调
+     */
+    override fun onDestroy() {
+        super.onDestroy()
+
+        /**Presenter解除绑定*/
+        detachView()
+        /**DataBinding解除绑定*/
+        mViewBinding.unbind()
+        /**执行抽象方法destory()*/
+        destory()
+        /**解除RX引用*/
+        disposableManager.unDisposable()
+        /**执行生命周期监听*/
+        lifeCycleDo(LIFE_CYCLE_DESTORY)
+        /**清除声明周期监听引用*/
+        removeAllCallBack()
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
+            /**处理android4.4.2 底层内存泄漏*/
+            fixInputMethodManagerLeak(activity)
+        }
+        errorLog("PageDestory","页面销毁======>$localClassName")
+    }
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:39
+     * @Note   获取Activity_LayoutId
+     */
+    protected abstract fun getLayId(): Int
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:44
+     * @Note   执行绑定Dagger操作
+     */
+    protected abstract fun bindDagger()
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:45
+     * @Note   执行初始化操作
+     */
+    protected abstract fun init()
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:45
+     * @Note   执行绑定事件操作
+     */
+    protected abstract fun bindEvent()
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:46
+     * @Note   执行销毁相关操作
+     */
+    protected abstract fun destory()
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/1/19 下午6:03
+     * @Note   绑定View接口
+     */
+    protected open fun attachView() {
+       presenter.attachView(this )
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/5/10 下午3:17
+     * @Note   解绑View接口
+     */
+    protected open fun detachView(){
+        presenter.detachView()
+    }
+
+
+    //是否启用耳机线控留言功能
+    protected abstract fun enableHeadsetVoiceMsg(): Boolean
+
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/3/29 上午11:37
+     * @Note   添加callBack
+     * @param  listener 监听回调
+     */
+    override fun addLifeCycleListener(listener: (state: Int) -> Unit) {
+        lifecycleCalls.add(listener)
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/3/29 上午11:43
+     * @Note   清除生命周期监听
+     */
+    private fun removeAllCallBack(){
+        lifecycleCalls.clear()
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/3/29 上午11:44
+     * @Note   在各个生命周期监听执行
+     * @param  state 生命周期状态
+     */
+    private fun lifeCycleDo(state :Int){
+        lifecycleCalls.forEach {
+            item ->
+            item.invoke(state)
+        }
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/5/27 下午1:22
+     * @Note   页面返回数据
+     * @param  resultCode 返回码
+     * @param  data       数据
+     */
+    open fun resultHandle(resultCode: Int,data: Intent?){
+
+    }
+
+    /**
+     * @author LDD
+     * @From   BaseActivity
+     * @Date   2018/5/27 下午1:22
+     * @Note   页面返回数据回调
+     */
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        resultHandle(resultCode,data)
+    }
+
+//    /**
+//     * 修改默认加载Dialog
+//     */
+//    override fun loadingDialogProvider(): LoadingDialog {
+//        return CommonTool.createLoadingDialog(this, R.layout.custom_loading,R.id.loadding_image)
+//    }
+
+    /**
+     * @author  LDD
+     * @From   com.wdkl.ncs.android.component.home.activity.BaseActivity
+     * @Data   2017/12/26 上午9:50
+     * @Note   处理4.4.2 Android底层内存泄漏
+     * @param  destContext 上下文
+     */
+    private fun fixInputMethodManagerLeak(destContext: Context?) {
+        if (destContext == null) {
+            return
+        }
+        val imm = destContext!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager ?: return
+        val arr = arrayOf("mCurRootView", "mServedView", "mNextServedView")
+        var f: Field?
+        var obj_get: Any?
+        arr.indices
+                .asSequence()
+                .map { arr[it] }
+                .forEach {
+                    try {
+                        f = imm.javaClass.getDeclaredField(it)
+                        if (f!!.isAccessible === false) {
+                            f!!.isAccessible = true
+                        }
+                        obj_get = f!!.get(imm)
+                        if (obj_get != null && obj_get is View) {
+                            val v_get = obj_get as View?
+                            if (v_get!!.getContext() === destContext) { // 被InputMethodManager持有引用的context是想要目标销毁的
+                                f!!.set(imm, null) // 置空,破坏掉path to gc节点
+                            }
+                        }
+                    } catch (t: Throwable) {
+                        t.printStackTrace()
+                    }
+                }
+    }
+}

+ 268 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/ChannelImActivity.kt

@@ -0,0 +1,268 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.PowerManager
+import android.view.MotionEvent
+import android.view.View
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.adapter.ChannelImItemAdapter
+import com.wdkl.ncs.android.component.home.databinding.ActivityChannelImListBinding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.util.*
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.common.Constants.Companion.channelId
+import com.wdkl.ncs.android.middleware.common.Constants.Companion.deviceId
+import com.wdkl.ncs.android.middleware.common.Constants.Companion.memberId
+import com.wdkl.ncs.android.middleware.logic.contract.home.ChannelImContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.ChannelImPresenter
+import com.wdkl.ncs.android.middleware.model.dos.ChannelImDO
+import com.wdkl.ncs.android.middleware.model.vo.ChannelIMVO
+import com.wdkl.ncs.android.middleware.tcp.NettyClient.Companion.instance
+import com.wdkl.ncs.android.middleware.tcp.channel.ChannelImUtil
+import kotlinx.android.synthetic.main.activity_channel_im_list.*
+import okhttp3.MediaType
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.io.File
+import java.util.*
+import kotlin.collections.ArrayList
+
+class ChannelImActivity : BaseActivity<ChannelImPresenter, ActivityChannelImListBinding>(), ChannelImContract.View, ChannelImItemAdapter.CallBack {
+
+    private lateinit var adapter: ChannelImItemAdapter
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    private lateinit var delegateAdapter: DelegateAdapter
+    private var page: Int = 1
+    private val page_size: Int = 10
+
+    var downTime : Long = 0
+    var upTime : Long = 0
+    var downY : Float = 0f
+    var cancel : Boolean = false
+    var voiceFile : String? = null
+
+    private var language = "zh"
+
+    lateinit var wakeLock : PowerManager.WakeLock
+
+    override fun getLayId(): Int {
+        return R.layout.activity_channel_im_list
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    @SuppressLint("InvalidWakeLockTag")
+    override fun init() {
+        // 获取WakeLock对象
+        val powerManager= getSystemService(Context.POWER_SERVICE) as PowerManager
+        wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.SCREEN_DIM_WAKE_LOCK, "voice_msg_on")
+
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        /**初始化适配器*/
+        adapter = ChannelImItemAdapter(ArrayList(), activity)
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+        delegateAdapter.addAdapter(adapter)
+        /**配置到RecycleView*/
+        rv_channel_im_list.layoutManager = virtualLayoutManager
+        rv_channel_im_list.adapter = delegateAdapter
+
+        adapter.setCallBack(this)
+
+        Constants.allowVoiceMsg = false
+
+        presenter.getChannelIm(page, page_size, memberId!!, channelId)
+
+        if (RecordHelper.getInstance().isRecording()) {
+            IMDialogHelper.showIMDialog(activity)
+        }
+
+        language = LocaleMangerUtils.getApplicationLocale().language
+    }
+
+    override fun bindEvent() {
+        srl_channel_im_list.setOnLoadMoreListener {
+            page += 1
+            presenter.getChannelIm(page, page_size, memberId!!, channelId)
+        }
+        srl_channel_im_list.setOnRefreshListener {
+            page = 1
+            presenter.getChannelIm(page, page_size, memberId!!, channelId)
+        }
+
+
+        //长按群留言
+        btn_channel_msg.setOnTouchListener { v, event ->
+            when(event.action) {
+                MotionEvent.ACTION_DOWN -> {
+                    cancel = false
+                    downTime = System.currentTimeMillis()
+                    downY = event.getY()
+                    //先停止其他语音或铃声
+                    RingPlayHelper.stopRingTone()
+                    SpeechUtil.getInstance().stopSpeak()
+                    MediaPlayHelper.getInstance().stopMusic(true)
+                    //EventBus.getDefault().post(MessageEvent(false, Constants.EVENT_CLEAR_IM))
+                    RecordHelper.getInstance().startRecord()
+                    tv_channel_text.setText(R.string.str_voice_msg_btn_title)
+                    voiceFile = RecordHelper.getInstance().audiofilePath
+
+                    //按下时保持常亮
+                    wakeLock.acquire()
+                }
+
+                MotionEvent.ACTION_MOVE -> {
+                    val moveY = event.getY()
+                    if (Math.abs(downY - moveY) > 100) {
+                        cancel = true
+                    }
+                }
+
+                MotionEvent.ACTION_UP -> {
+                    upTime = System.currentTimeMillis()
+                    RecordHelper.getInstance().stopRecord()
+                    tv_channel_text.setText(R.string.str_voice_msg_btn_text)
+
+                    if (Math.abs(upTime - downTime) <= 1000) {
+                        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+                        showMessage(R.string.str_voice_msg_record_loss)
+                    } else if(cancel) {
+                        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+                        showMessage(R.string.str_voice_msg_record_cancel)
+                    } else {
+                        //上传语音留言
+                        if (voiceFile != null) {
+                            val part = MultipartBody.Part.createFormData("file", voiceFile, RequestBody.create(
+                                MediaType.parse("multipart/form-data"), File(voiceFile)
+                            ))
+                            presenter.uploadVoiceMsg(part)
+                        }
+                    }
+
+                    //松开时释放
+                    wakeLock.release()
+                }
+            }
+
+            return@setOnTouchListener false
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    override fun uploadResponse(result: String) {
+        //上传完成后删除本地文件
+        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+        val channelImDO = ChannelImDO()
+        channelImDO.channelId = channelId
+        channelImDO.senderMemberId = memberId
+        channelImDO.audioPath = result
+        val tcpModel = ChannelImUtil.channelImMsg(deviceId, channelImDO)
+        instance.sendMsg(tcpModel.toJson()).subscribe { it: Boolean ->
+            if (it) {
+                if (Locale.CHINESE.getLanguage().equals(language)) {
+                    SoundPoolManager.getInstance().playSound(3)
+                }
+                showMessage(R.string.str_voice_msg_send_success)
+                //刷新列表
+                page = 1
+                presenter.getChannelIm(page, page_size, memberId!!, channelId)
+            } else {
+                showMessage(R.string.str_voice_msg_send_fail)
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        if (!MediaPlayHelper.getInstance().isMediaPlaying) {
+            ImPlayDialogHelper.dismissIMDialog()
+        }
+    }
+
+    override fun destory() {
+        Constants.allowVoiceMsg = true
+        IMDialogHelper.dismissIMDialog()
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun showChannelIm(data: ArrayList<ChannelIMVO>) {
+        srl_channel_im_list.finishRefresh()
+
+        if (page == 1) {
+            srl_channel_im_list.resetNoMoreData()
+            if (data.size > 0) {
+                adapter.data.clear()
+                adapter.data.addAll(data)
+                adapter.notifyDataSetChanged()
+            } else {
+                tv_empty_channel_im.visibility = View.VISIBLE
+            }
+            srl_channel_im_list.finishLoadMore()
+        } else {
+            if (data.size > 0) {
+                adapter.data.addAll(data)
+                adapter.notifyDataSetChanged()
+                srl_channel_im_list.finishLoadMore()
+            } else {
+                srl_channel_im_list.finishLoadMoreWithNoMoreData()
+            }
+        }
+    }
+
+    override fun onChannelImRead(item: ChannelIMVO, pos: Int) {
+        //该条留言已读,刷新状态
+        var update = false
+        for (e in adapter.data) {
+            if (e.id == item.id) {
+                e.readed = true
+                update = true
+                break
+            }
+        }
+
+        if (update && pos < adapter.data.size) {
+            runOnUiThread {
+                adapter.notifyItemChanged(pos)
+            }
+        }
+    }
+
+    override fun onError(message: String, type: Int) {
+        srl_channel_im_list.finishRefresh()
+    }
+
+    override fun complete(message: String, type: Int) {
+        //
+    }
+
+    override fun start() {
+        //
+    }
+
+    override fun networkMonitor(state: NetState) {
+        //
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 3 || messageEvent.tag == Constants.EVENT_CHANNEL_IM_UPDATE) {
+            //刷新列表
+            page = 1
+            presenter.getChannelIm(page, page_size, memberId!!, channelId)
+        }
+    }
+}

+ 200 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/ContactUpdateActivity.kt

@@ -0,0 +1,200 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.graphics.Color
+import android.view.View
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.adapter.ContactUpdateAdapter
+import com.wdkl.ncs.android.component.home.databinding.ActivityContactUpdateBinding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.logic.contract.home.WatchHomeActivityContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.WatchHomeActivityPresenter
+import com.wdkl.ncs.android.middleware.model.ServerIpInfo
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.model.dos.ChannelDO
+import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
+import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
+import com.wdkl.ncs.android.middleware.model.vo.DeviceVO
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactVO
+import com.wdkl.ncs.android.middleware.utils.ContactHelper
+import kotlinx.android.synthetic.main.activity_contact_update.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class ContactUpdateActivity: BaseActivity<WatchHomeActivityPresenter, ActivityContactUpdateBinding>(), WatchHomeActivityContract.View {
+    private lateinit var adapter: ContactUpdateAdapter
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    private var clickTime: Long = 0
+
+    override fun getLayId(): Int {
+        return R.layout.activity_contact_update
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun init() {
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        adapter = ContactUpdateAdapter(ArrayList(), activity)
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+        delegateAdapter.addAdapter(adapter)
+
+        update_contact_list.adapter = delegateAdapter
+        update_contact_list.layoutManager = virtualLayoutManager
+
+        if (Constants.deviceId > 0) {
+            tv_contact_title.setText(R.string.contact_start_load)
+            presenter.getWatchContacts(Constants.deviceId)
+            showMessage(R.string.contact_start_update)
+        } else {
+            tv_contact_title.setText(R.string.device_not_registered)
+            tv_contact_title.setTextColor(Color.RED)
+            showMessage(R.string.device_not_registered)
+        }
+    }
+
+    override fun bindEvent() {
+        //
+    }
+
+    override fun destory() {
+        //
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun handleAppVersion(appInfo: AppVersionDO) {
+    }
+
+    override fun setDeviceDo(data: DeviceVO) {
+    }
+
+    override fun setTcpServerHost(tcpSeverDTO: TcpSeverDTO) {
+    }
+
+    override fun setServerInfo(serverIpInfo: ServerIpInfo) {
+    }
+
+    override fun setDeviceSettingData(partSettingDO: PartSettingDO) {
+    }
+
+    override fun setDeviceVoiceChannel(channelList: ArrayList<ChannelDO>) {
+    }
+
+    override fun setPhoneNumberWhiteList(phoneList: ArrayList<String>) {
+    }
+
+    override fun setContact(contactsVOs: List<WatchContactVO>) {
+        tv_contact_title.text = getString(R.string.contact_load_count, contactsVOs.size)
+        tv_contact_title.setTextColor(Color.GREEN)
+        if (contactsVOs.size > 0) {
+            val list = ArrayList<String>()
+            for (contact in contactsVOs) {
+                val str = getString(R.string.contact_list_item, contact.name, contact.phoneNumber)
+                list.add(str)
+            }
+            list.add("\n")
+            list.add(getString(R.string.contact_updating))
+            list.add("\n")
+            adapter.data.addAll(list)
+            adapter.notifyDataSetChanged()
+
+            Thread {
+                ContactHelper.batchUpdate(applicationContext, contactsVOs, object : ContactHelper.Callback {
+                    override fun onBack(str: String) {
+                        runOnUiThread {
+                            adapter.data.add(str)
+                            if (adapter.data.size > 10) {
+                                update_contact_list.smoothScrollToPosition(adapter.itemCount - 1)
+                            }
+                            adapter.notifyDataSetChanged()
+                        }
+                    }
+
+                    override fun onUpdate(progress: Int) {
+                        runOnUiThread {
+                            progress_contact_update.setCurProgress(progress)
+                            if (progress == 100) {
+                                val text = getString(R.string.contact_update_complete)
+                                tv_contact_update_progress.setText(text + progress + "%")
+                            } else {
+                                val text = getString(R.string.contact_update_porgress)
+                                tv_contact_update_progress.setText(text + progress + "%")
+                            }
+                        }
+                    }
+                })
+            }.start()
+
+            /*Thread {
+                ContactHelper.updateContacts(applicationContext, contactsVOs, object : ContactHelper.Callback {
+                    override fun onBack(str: String) {
+                        runOnUiThread {
+                            adapter.data.add(str)
+                            if (adapter.data.size > 10) {
+                                update_contact_list.smoothScrollToPosition(adapter.itemCount - 1)
+                            }
+                            adapter.notifyDataSetChanged()
+                        }
+                    }
+
+                    override fun onUpdate(progress: Int) {
+                        runOnUiThread {
+                            progress_contact_update.setCurProgress(progress)
+                            if (progress == 100) {
+                                tv_contact_update_progress.setText("更新完成: " + progress + "%")
+                            } else {
+                                tv_contact_update_progress.setText("正在更新: " + progress + "%")
+                            }
+                        }
+                    }
+                })
+            }.start()*/
+        }
+    }
+
+    override fun onBackPressed() {
+        if (System.currentTimeMillis() - clickTime > 2000) {
+            showMessage(R.string.click_twice_to_back)
+        } else {
+            ContactHelper.exitUpdateContact()
+            super.onBackPressed()
+        }
+        clickTime = System.currentTimeMillis()
+    }
+
+    override fun onError(message: String, type: Int) {
+        showMessage(message)
+    }
+
+    override fun complete(message: String, type: Int) {
+
+    }
+
+    override fun start() {
+
+    }
+
+    override fun networkMonitor(state: NetState) {
+
+    }
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        //
+    }
+}

+ 239 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/NewEventListActivity.kt

@@ -0,0 +1,239 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.content.Context
+import android.os.Vibrator
+import android.util.Log
+import android.view.View
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.google.gson.Gson
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.adapter.NewEventItemAdapter
+import com.wdkl.ncs.android.component.home.databinding.ActivityEventListBinding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService
+import com.wdkl.ncs.android.component.home.util.ImPlayDialogHelper
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper
+import com.wdkl.ncs.android.component.home.util.SpeechUtil
+import com.wdkl.ncs.android.lib.base.LIFE_CYCLE_RESUME
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.utils.AppTool
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.logic.contract.home.NewEventListContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.NewEventListPresenter
+import com.wdkl.ncs.android.middleware.model.dos.ClerkDO
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+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.activity_event_list.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class NewEventListActivity : BaseActivity<NewEventListPresenter, ActivityEventListBinding>(),NewEventListContract.View {
+    var TAG = NewEventListActivity::class.java.getSimpleName()
+
+    lateinit var mVibrator: Vibrator
+
+    private val adapter = NewEventItemAdapter(ArrayList(),this)
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    //参数自动注入
+//    @Autowired(name = "tcpModelStr", required = true)
+//    @JvmField
+//    var tcpModelStr:String = ""
+//    @Autowired(name = "newEvent", required = false)
+//    @JvmField
+//    var newEvent = false
+
+    override fun getLayId(): Int {
+        return R.layout.activity_event_list
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun init() {
+        //震动
+        mVibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        /**初始化适配器*/
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+        delegateAdapter.addAdapter(adapter)
+        /**配置到RecycleView*/
+        rv_event_list.layoutManager = virtualLayoutManager
+        rv_event_list.adapter = delegateAdapter
+    }
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        super.onWindowFocusChanged(hasFocus)
+        if (!Constants.oldEvent) {
+            window.decorView.systemUiVisibility = screen_flags
+        }
+    }
+
+    override fun bindEvent() {
+
+    }
+
+    override fun onResume() {
+        super.onResume()
+        if (!Constants.oldEvent) {
+            window.decorView.systemUiVisibility = screen_flags
+        }
+        WdKeepAliveService.mNewEventListActive = true
+        renderData(Constants.eventList)
+        if (!MediaPlayHelper.getInstance().isMediaPlaying) {
+            ImPlayDialogHelper.dismissIMDialog()
+        }
+    }
+
+    override fun onPause() {
+        super.onPause()
+        WdKeepAliveService.mNewEventListActive = false
+    }
+
+    override fun destory() {
+        //SpeechUtil.getInstance().stopSpeak()
+        MediaPlayHelper.getInstance().stopMusic(true)
+        RingPlayHelper.stopRingTone()
+
+        if (mVibrator!=null) {
+            mVibrator.cancel()
+        }
+        Constants.eventList.clear()
+    }
+
+    override fun renderData(data: ArrayList<InteractionVO>) {
+        Log.e(TAG,"返回的数据 "+data.size)
+        //data.sortByDescending { it.id }
+        //srl_event_list.finishRefresh()
+        if (data.size > 0) {
+            adapter.data.clear()
+            adapter.data.addAll(data)
+            adapter.notifyDataSetChanged()
+        } else {
+            tv_empty_event.visibility = View.VISIBLE
+            AppTool.Time.delay(800){
+                finish()
+            }
+        }
+    }
+
+    override fun showClerks(clerks: ArrayList<ClerkDO>) {
+
+    }
+
+    override fun updateInteractionRes(interaction: InteractionVO) {
+
+    }
+
+    override fun onError(message: String, type: Int) {
+        finish()
+    }
+
+    override fun complete(message: String, type: Int) {
+
+    }
+
+    override fun start() {
+
+    }
+
+    override fun networkMonitor(state: NetState) {
+
+    }
+
+    override fun onBackPressed() {
+        if (!Constants.oldEvent) {
+            showMessage(R.string.str_event_handle_warning)
+        } else {
+            super.onBackPressed()
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 0) {
+            if (mVibrator!=null) mVibrator.cancel()
+            val resTcpModel = messageEvent.getMessage() as TcpModel
+            if (resTcpModel.type === TcpType.CALLBACK && resTcpModel.action === TcpAction.VoiceAction.FAILED) {
+                showMessage(R.string.call_failed)
+                dismissDialog()
+            }
+        } else if (messageEvent.tag == 2) {
+            if (mVibrator!=null) mVibrator.cancel()
+            val resTcpModel = messageEvent.getMessage() as TcpModel
+            if (resTcpModel.action == TcpAction.VoiceAction.CALLING) {
+                showMessage(R.string.call_busy)
+            }
+        } else if (messageEvent.tag == 4) {
+            val resTcpModel = messageEvent.getMessage() as TcpModel
+            if (resTcpModel.type == TcpType.DATA && resTcpModel.action == TcpAction.DataAction.INTERACTION) {
+                if (resTcpModel.data != null) {
+                    dismissDialog()
+                    if (mVibrator!=null) mVibrator.cancel()
+                    val responseInteractionVO = Gson().fromJson(resTcpModel.data.toString(), InteractionVO::class.java)
+                    val text = getString(R.string.str_event_response, responseInteractionVO.fromFrameFullName, responseInteractionVO.data)
+                    showMessage(text)
+
+                    var doFinish = false
+                    var iterator = Constants.eventList.iterator()
+                    while (iterator.hasNext()){
+                        val it = iterator.next()
+                        if (it.id == responseInteractionVO.id) {
+                            if (responseInteractionVO.actionType.equals(TcpType.IM.name)){
+                                AppTool.Time.delay(1000){
+                                    Thread {
+                                        while (MediaPlayHelper.getInstance().isMediaPlaying){
+                                            Thread.sleep(1000)
+                                        }
+
+                                        runOnUiThread {
+                                            Constants.eventList.remove(it)
+                                            adapter.data.remove(it)
+                                            adapter.notifyDataSetChanged()
+                                            if (Constants.eventList.isEmpty() || Constants.eventList.size==0){
+                                                AppTool.Time.delay(800) {
+                                                    finish()
+                                                }
+                                            } else {
+                                                this.renderData(Constants.eventList)
+                                            }
+                                        }
+                                    }.start()
+                                }
+                            } else {
+                                Constants.eventList.remove(it)
+                                this.adapter.data.remove(it)
+                                this.adapter.notifyDataSetChanged()
+                                doFinish = true
+                            }
+                        }
+                    }
+                    if ((Constants.eventList.isEmpty() || Constants.eventList.size==0) && doFinish){
+                        AppTool.Time.delay(800) {
+                            finish()
+                        }
+                    } else {
+                        this.renderData(Constants.eventList)
+                    }
+                }
+            }
+        } else if (messageEvent.tag == Constants.EVENT_UPDATE_EVENT) { //新事件到来
+            renderData(Constants.eventList)
+        }
+    }
+}

+ 73 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/SmsReceivedActivity.kt

@@ -0,0 +1,73 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.app.Activity
+import android.os.Bundle
+import android.text.TextUtils
+import android.text.method.ScrollingMovementMethod
+import android.view.View
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.util.AppUtils
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceUtil
+import kotlinx.android.synthetic.main.activity_sms_received.*
+
+class SmsReceivedActivity : Activity() {
+
+    private val FULL_SCREEN_FLAG = (
+            View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                    or View.SYSTEM_UI_FLAG_FULLSCREEN)
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        setContentView(R.layout.activity_sms_received)
+
+        //Constants.smsCheck = false
+
+        setFinishOnTouchOutside(false)
+        //window.decorView.systemUiVisibility = FULL_SCREEN_FLAG
+
+        tv_sms_reply.movementMethod = ScrollingMovementMethod.getInstance()
+
+        val address = intent.getStringExtra("address")
+        val data = intent.getStringExtra("sms_data")
+
+        tv_sms_title.text = "SMS from: " + address
+        tv_sms_reply.text = data
+
+        //提取手机号
+        //val regex = "((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0-9]))\\d{8}"
+        val regex = "(1[3-9][0-9])\\d{8}"
+        val phone = AppUtils.getMobileNumber(data, regex)
+        edit_sms_phone.setText(phone)
+
+
+        btn_number_cancel.setOnClickListener {
+            Constants.smsCheck = false
+            finish()
+        }
+
+        btn_number_confirm.setOnClickListener {
+            Constants.smsCheck = false
+            val phoneNo = edit_sms_phone.text.toString()
+            if (!TextUtils.isEmpty(phoneNo)) {
+                val tcpModel = DeviceUtil.deviceConnect2(Constants.Companion.imei, phoneNo)
+                NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe {
+                    if (it) {
+                        showMessage(R.string.device_number_update_success)
+                    } else {
+                        showMessage(R.string.device_number_update_failed)
+                    }
+                }
+                finish()
+            } else {
+                showMessage(R.string.device_number_update_empty)
+            }
+        }
+    }
+}

+ 174 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/TakeoverActivity.kt

@@ -0,0 +1,174 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.util.Log
+import android.view.View
+import androidx.appcompat.widget.SearchView
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.net.engine.model.NetState
+import com.google.gson.JsonArray
+import com.google.gson.JsonObject
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.adapter.TakeoverItemSearchAdapter
+import com.wdkl.ncs.android.component.home.databinding.ActivityTakeoverBinding
+import com.wdkl.ncs.android.component.home.entity.ContactItemEntity
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.search.PinyinUtil
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.logic.contract.home.TakeoverContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.TakeoverPresenter
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import kotlinx.android.synthetic.main.activity_takeover.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.*
+import kotlin.collections.ArrayList
+
+class TakeoverActivity: BaseActivity<TakeoverPresenter, ActivityTakeoverBinding>(), TakeoverContract.View {
+    var TAG = TakeoverActivity::class.java.getSimpleName()
+
+    //private val adapter = TakeoverItemAdapter(ArrayList(),this)
+    private val adapter = TakeoverItemSearchAdapter(null, ArrayList(), this)
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+    //private lateinit var delegateAdapter: DelegateAdapter
+    var partId: Int = -1
+    val reg = Regex("[a-z]+", RegexOption.IGNORE_CASE)
+
+    override fun getLayId(): Int {
+        return R.layout.activity_takeover
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun onError(message: String, type: Int) {
+        showMessage(message)
+        take_over_refresh.finishRefresh()
+    }
+
+    override fun complete(message: String, type: Int) {
+        showMessage(message)
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+    }
+
+    override fun init(){
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        /**初始化适配器*/
+        //delegateAdapter = DelegateAdapter(virtualLayoutManager)
+        //delegateAdapter.addAdapter(adapter)
+        /**配置到RecycleView*/
+        takeover_list.layoutManager = virtualLayoutManager
+        takeover_list.adapter = adapter
+        partId = Constants.partId
+    }
+
+    override fun onResume() {
+        super.onResume()
+        presenter.loadList(partId)
+    }
+
+    override fun renderData(data: JsonArray) {
+        Log.i(TAG,"返回的数据 "+data)
+        take_over_refresh.finishRefresh()
+
+        if (data.size() > 0) {
+            var sortList = ArrayList<ContactItemEntity>()
+            for (item in data) {
+                var letter: String
+                //汉字转换成拼音
+                val _item = item as JsonObject
+                if (_item.get("id").asInt != Constants.deviceId) {
+                    if (_item.get("clerk_name").isJsonNull) {
+                        break
+                    }
+
+                    val pinyinList = PinyinUtil.getPinYinList(_item.get("clerk_name").asString)
+                    letter = if (pinyinList != null && pinyinList.isNotEmpty()) {
+                        // A-Z导航
+                        val letters = pinyinList[0].substring(0, 1)
+                        // 正则表达式,判断首字母是否是英文字母
+                        if (letters.matches(reg)) {
+                            letters.toUpperCase(Locale.ROOT)
+                        } else {
+                            "#"
+                        }
+                    } else {
+                        "#"
+                    }
+
+                    if (!_item.get("id").isJsonNull) {
+                        var name = ""
+                        var number = ""
+                        if (!_item.get("clerk_name").isJsonNull) {
+                            name = _item.get("clerk_name").asString
+                        }
+                        if (!_item.get("phone_number").isJsonNull) {
+                            number = _item.get("phone_number").asString
+                        }
+                        val itemEntity = ContactItemEntity(
+                            name,
+                            number,
+                            _item.get("id").asInt,
+                            letter,
+                            pinyinList
+                        )
+                        sortList.add(itemEntity)
+                    }
+                }
+            }
+
+            adapter.setDataList(sortList)
+        } else {
+            tv_empty_takeover.visibility = View.VISIBLE
+        }
+    }
+
+    override fun bindEvent() {
+        take_over_refresh.setOnRefreshListener {
+            presenter.loadList(partId)
+        }
+
+        search_view.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
+            override fun onQueryTextSubmit(query: String): Boolean {
+                return false
+            }
+
+            override fun onQueryTextChange(newText: String): Boolean {
+                adapter.getFilter().filter(newText)
+                return false
+            }
+        })
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 2) {
+            val resTcpModel = messageEvent.getMessage() as TcpModel
+            if (resTcpModel.action  == TcpAction.VoiceAction.FAILED){
+                showMessage(R.string.call_failed)
+            } else if (resTcpModel.action == TcpAction.VoiceAction.CALLING){
+                showMessage(R.string.call_busy)
+            }
+        } else if (messageEvent.tag == 4) {
+            presenter.loadList(partId)
+        }
+    }
+
+    override fun destory() {
+    }
+}

+ 224 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/VoiceMsgActivity.kt

@@ -0,0 +1,224 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.PowerManager
+import android.os.SystemClock
+import android.view.MotionEvent
+import com.enation.javashop.android.jrouter.external.annotation.Autowired
+import com.enation.javashop.net.engine.model.NetState
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.databinding.ActivityVoiceMsgBinding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.util.*
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.lib.vo.filter
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.logic.contract.home.VoiceMsgContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.VoiceMsgPresenter
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+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.TcpAction
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import kotlinx.android.synthetic.main.activity_voice_msg.*
+import okhttp3.MediaType
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.io.File
+import java.util.*
+
+class VoiceMsgActivity:BaseActivity<VoiceMsgPresenter,ActivityVoiceMsgBinding>(), VoiceMsgContract.View {
+    var TAG = VoiceMsgActivity::class.java.simpleName
+
+    companion object instance{
+        const val TO_DEVICE_ID = "toDeviceId"
+    }
+
+    var downTime : Long = 0
+    var upTime : Long = 0
+    var downY : Float = 0f
+    var cancel : Boolean = false
+    var voiceFile : String? = null
+    lateinit var wakeLock : PowerManager.WakeLock
+
+    private var language = "zh"
+
+    @Autowired(name = TO_DEVICE_ID, required = true)
+    @JvmField
+    var toDeviceId = 0
+
+    override fun getLayId(): Int {
+        return R.layout.activity_voice_msg
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    @SuppressLint("InvalidWakeLockTag")
+    override fun init() {
+        //RecordHelper.getInstance().init()
+        Constants.allowVoiceMsg = false
+        if (RecordHelper.getInstance().isRecording()) {
+            IMDialogHelper.showIMDialog(activity)
+        }
+
+        // 获取WakeLock对象
+        val powerManager= getSystemService(Context.POWER_SERVICE) as PowerManager
+        wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.SCREEN_DIM_WAKE_LOCK, "voice_msg_on")
+
+        language = LocaleMangerUtils.getApplicationLocale().language
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    @SuppressLint("ClickableViewAccessibility")
+    override fun bindEvent() {
+        tv_voice_button.setOnTouchListener { v, event ->
+            when(event.action) {
+                MotionEvent.ACTION_DOWN -> {
+                    cancel = false
+                    downTime = System.currentTimeMillis()
+                    downY = event.getY()
+                    //先停止其他语音或铃声
+                    RingPlayHelper.stopRingTone()
+                    SpeechUtil.getInstance().stopSpeak()
+                    MediaPlayHelper.getInstance().stopMusic(true)
+                    //EventBus.getDefault().post(MessageEvent(false, Constants.EVENT_CLEAR_IM))
+                    RecordHelper.getInstance().startRecord()
+                    tv_voice_text.setText(R.string.str_voice_msg_btn_title)
+                    voice_call_timer.base = SystemClock.elapsedRealtime()
+                    voice_call_timer.start()
+                    voiceFile = RecordHelper.getInstance().audiofilePath
+
+                    //按下时保持常亮
+                    wakeLock.acquire()
+                }
+
+                MotionEvent.ACTION_MOVE -> {
+                    val moveY = event.getY()
+                    if (Math.abs(downY - moveY) > 100) {
+                        cancel = true
+                    }
+                }
+
+                MotionEvent.ACTION_UP -> {
+                    upTime = System.currentTimeMillis()
+                    RecordHelper.getInstance().stopRecord()
+                    tv_voice_text.setText(R.string.str_voice_msg_btn_text)
+                    voice_call_timer.base = SystemClock.elapsedRealtime()
+                    voice_call_timer.stop()
+
+                    if (Math.abs(upTime - downTime) <= 1000) {
+                        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+                        showMessage(R.string.str_voice_msg_record_loss)
+                    } else if(cancel) {
+                        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+                        showMessage(R.string.str_voice_msg_record_cancel)
+                    } else {
+                        //上传语音留言
+                        if (voiceFile != null) {
+                            val part = MultipartBody.Part.createFormData("file", voiceFile, RequestBody.create(MediaType.parse("multipart/form-data"), File(voiceFile)))
+                            presenter.uploadVoiceMsg(part)
+                        }
+                    }
+
+                    //松开时释放
+                    wakeLock.release()
+                }
+            }
+
+            return@setOnTouchListener false
+        }
+    }
+
+    override fun destory() {
+        Constants.allowVoiceMsg = true
+        IMDialogHelper.dismissIMDialog()
+    }
+
+    @SuppressLint("CheckResult")
+    override fun uploadResponse(result: String) {
+        //上传完成后删除本地文件
+        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+        val tcpModel = ImUtil.imMsg(Constants.deviceId, toDeviceId, result)
+        NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe{
+            if (it){
+                if (Locale.CHINESE.getLanguage().equals(language)) {
+                    SoundPoolManager.getInstance().playSound(3)
+                }
+                showMessage(R.string.str_voice_msg_send_success)
+            } else {
+                showMessage(R.string.str_voice_msg_send_fail)
+            }
+            finish()
+        }
+    }
+
+    override fun onError(message: String, type: Int) {
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+        state.filter(onWifi = {
+
+        },onMobile = {
+
+        },offline = {
+
+        })
+    }
+
+    private fun cancelRecord() {
+        if (voice_call_timer != null) {
+            voice_call_timer.base = SystemClock.elapsedRealtime()
+            voice_call_timer.stop()
+        }
+        RecordHelper.getInstance().stopRecord()
+        RecordHelper.getInstance().deleteAudioFile(voiceFile)
+        showMessage(R.string.str_voice_msg_record_cancel)
+        finish()
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent){
+        if (messageEvent.tag == 1) {
+            val tcpModel = messageEvent.getMessage() as TcpModel
+            if (tcpModel.type == TcpType.VOICE) {
+                if (tcpModel.action == TcpAction.VoiceAction.CALL) {
+                    //新来电
+                    cancelRecord()
+                }
+            }
+        } else if (messageEvent.tag == 3) {
+            //收到新事件
+            val tcpModel = messageEvent.getMessage() as TcpModel
+            if (tcpModel.type == TcpType.EVENT) {
+                if (tcpModel.action == TcpAction.EventAction.KEY_CLICK) {
+                    //取消语音留言录音并退出
+                    cancelRecord()
+                }
+            } else if (tcpModel.type == TcpType.IM) {
+                /*if (tcpModel.action == TcpAction.IMAction.MSG) {
+                    //取消语音留言录音并退出
+                    cancelRecord()
+                }*/
+            }
+        } else if (messageEvent.tag == 999) {
+            //紧急呼叫
+            cancelRecord()
+        }
+    }
+}

+ 312 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchCallRecordsActivity.kt

@@ -0,0 +1,312 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.content.Intent
+import android.net.Uri
+import android.os.CountDownTimer
+import android.util.Log
+import android.view.View
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.android.jrouter.external.annotation.Router
+import com.enation.javashop.net.engine.model.NetState
+import com.enation.javashop.utils.base.widget.LoadingDialog
+import com.scwang.smartrefresh.layout.footer.ClassicsFooter
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.adapter.WatchCallRecordsItemAdapter
+import com.wdkl.ncs.android.component.home.databinding.WatchActivityCallRecordsBinding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.logic.contract.home.WatchCallRecordsFragmentContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.WatchCallRecordsFragmentPresenter
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactsVO
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+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.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.channel.PhoneUtil
+import com.wdkl.ncs.android.middleware.tcp.enums.CommunicationEnum
+import kotlinx.android.synthetic.main.watch_activity_call_records.*
+import kotlinx.android.synthetic.main.watch_activity_call_records.refresh
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+
+@Router(path = "/watch/callrecords")
+class WatchCallRecordsActivity : BaseActivity<WatchCallRecordsFragmentPresenter, WatchActivityCallRecordsBinding>(), WatchCallRecordsFragmentContract.View {
+    var TAG = WatchCallRecordsActivity::class.java.getSimpleName()
+
+    /**
+     * @Name  virtualLayoutManager
+     * @Type  VirtualLayoutManager
+     * @Note  VLayoutManager
+     */
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+
+    private var page: Int = 1
+    /**
+     * @Name  delegateAdapter
+     * @Type  DelegateAdapter
+     * @Note  七巧板适配器
+     */
+    private lateinit var delegateAdapter: DelegateAdapter
+
+    private val watchCallRecordsItemAdapter = WatchCallRecordsItemAdapter(ArrayList())
+
+    private lateinit var loadingDialog: LoadingDialog
+
+    private var customerId = ""
+
+    var all = "ALL"
+    var customerRecords = "CUSTOMER_RECORDS"
+    var unread = "UNREAD"
+
+    var action: String? = null
+    var receivedData: WatchContactsVO? = null
+
+    private var CALL_TIMEOUT = 5 //多久可以再次点击
+    //呼叫倒计时
+    lateinit var countDownTimer: CountDownTimer
+
+    override fun getLayId(): Int {
+        return R.layout.watch_activity_call_records
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun init() {
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        /**初始化适配器*/
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+        delegateAdapter.addAdapter(watchCallRecordsItemAdapter)
+        mViewBinding.refresh.setRefreshFooter(ClassicsFooter(activity))
+
+        /**配置到RecycleView*/
+        call_records_recyv.layoutManager = virtualLayoutManager
+        call_records_recyv.adapter = delegateAdapter
+
+        //loadingDialog = CommonTool.createLoadingDialog(this, R.layout.custom_loading,R.id.loadding_image)
+
+        receivedData = intent.getSerializableExtra("data") as WatchContactsVO?
+        action = intent.getStringExtra("action")
+        customerId = intent.getStringExtra("customer_id").toString()
+
+        when(action){
+            all->{
+                call_relyout.visibility = View.GONE
+                presenter.loadPage(all, 1, 30, Constants.deviceId, 0, 0)
+
+                refresh.setOnLoadMoreListener {
+                    page += 1
+                    presenter.loadPage(all, page, 30, Constants.deviceId, 0, 0)
+                }
+                refresh.setOnRefreshListener {
+                    page = 1
+                    presenter.loadPage(all, 1, 30, Constants.deviceId, 0, 0)
+                }
+            }
+            unread->{
+                call_relyout.visibility = View.GONE
+                presenter.loadPage(all, 1, 30, Constants.deviceId, 0, 1)
+
+                refresh.setOnLoadMoreListener {
+                    page += 1
+                    presenter.loadPage(all, page, 30, Constants.deviceId, 0, 1)
+                }
+                refresh.setOnRefreshListener {
+                    page = 1
+                    presenter.loadPage(all, 1, 30, Constants.deviceId, 0, 1)
+                }
+            }
+            customerRecords->{
+                //通话处理
+                call_relyout.visibility = View.VISIBLE
+                call_relyout.setOnClickListener {
+                    call_relyout.isEnabled = false
+
+                    if (Constants.bedPhoneType == CommunicationEnum.MOBILE_PHONE.value()) {
+                        try {
+                            if (receivedData!!.phoneNumber != null) {
+                                val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:" + receivedData!!.phoneNumber))
+                                startActivity(intent)
+
+                                if (!Constants.uploadCalllog) {
+                                    //发送tcp
+                                    val tcpModel = PhoneUtil.phoneCall(Constants.deviceId, receivedData!!.deviceId)
+                                    NettyClient.instance.sendMsg(tcpModel.toJson())
+                                        .subscribe { res: Boolean ->
+                                            if (res) {
+                                                Log.d(TAG, "TCP.发送消息完成")
+                                            } else {
+                                                Log.e(TAG, "TCP.发送消息失败")
+                                                HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                            }
+                                        }
+                                }
+                            } else {
+                                showMessage(R.string.call_phone_failed)
+                            }
+                        } catch (e: Exception) {
+                            showMessage(R.string.call_phone_failed)
+                        }
+                    } else {
+                        DeviceChannel.calling = true
+                        val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, receivedData!!.deviceId)
+                        //启动通话界面
+                        val intent = Intent(this@WatchCallRecordsActivity, CallSingleActivity::class.java)
+                        intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
+                        intent.putExtra(CallSingleActivity.EXTRA_MO, true)
+                        intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
+                        intent.putExtra(CallSingleActivity.EXTRA_TCPMODEL, tcpModel);
+                        intent.putExtra(CallSingleActivity.EXTRA_SHOWNAME, receivedData!!.frameFullName + " " + receivedData!!.customerNamed)
+                        //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        startActivity(intent)
+
+                        countDownTimer.start()
+                    }
+                }
+
+                presenter.loadPage(customerRecords, 1, 30, Constants.deviceId, customerId.toInt(), -1)
+
+                refresh.setOnLoadMoreListener {
+                    page += 1
+                    presenter.loadPage(customerRecords, page, 30, Constants.deviceId, customerId.toInt(), -1)
+                }
+                refresh.setOnRefreshListener {
+                    page = 1
+                    presenter.loadPage(customerRecords, 1, 30, Constants.deviceId, customerId.toInt(), -1)
+                }
+            }
+        }
+
+        initCountDownTimer()
+    }
+
+    override fun bindEvent() {
+        watchCallRecordsItemAdapter.setOnItemClickListener { data, position ->
+
+            //先存储数据然后启动NewEventListActivity显示数据
+            if (Constants.oldEvent) {
+                Constants.eventList.clear()
+            }
+            Constants.oldEvent = true
+            Constants.eventList.add(data)
+
+            var intent = Intent()
+            intent.setClass(this, NewEventListActivity::class.java)
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            startActivity(intent)
+        }
+    }
+
+    override fun destory() {
+        if(countDownTimer != null){
+            countDownTimer.cancel()
+        }
+    }
+
+    override fun renderList(data: ArrayList<InteractionVO>) {
+        if (action.equals(unread)) {
+            EventBus.getDefault().post(MessageEvent(data, Constants.EVENT_UNTREATED_QUANTITY))
+        }
+
+        tv_empty_records.visibility = View.GONE
+        refresh.finishRefresh()
+        Log.i("abc1", " " + data.size);
+        if (page == 1) {
+            refresh.resetNoMoreData()
+            watchCallRecordsItemAdapter.data.clear()
+            if (data.size > 0) {
+                watchCallRecordsItemAdapter.data.addAll(data)
+            } else {
+                tv_empty_records.visibility = View.VISIBLE
+            }
+            watchCallRecordsItemAdapter.notifyDataSetChanged()
+            refresh.finishLoadMore()
+        } else {
+            if (data.size > 0) {
+                watchCallRecordsItemAdapter.data.addAll(data)
+                watchCallRecordsItemAdapter.notifyDataSetChanged()
+                refresh.finishLoadMore()
+            } else {
+                refresh.finishLoadMoreWithNoMoreData()
+            }
+        }
+    }
+
+    override fun onError(message: String, type: Int) {
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+    }
+
+    override fun onResume() {
+        super.onResume()
+        page = 1
+        when(action){
+            all->{
+                presenter.loadPage(all, 1, 30, Constants.deviceId, 0, 0)
+            }
+            unread->{
+                presenter.loadPage(all, 1, 30, Constants.deviceId, 0, 1)
+            }
+            customerRecords->{
+                presenter.loadPage(customerRecords, 1, 30, Constants.deviceId, customerId.toInt(), -1)
+            }
+        }
+    }
+
+    fun initCountDownTimer() {
+        countDownTimer = object : CountDownTimer(CALL_TIMEOUT * 3000L, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+            }
+            override fun onFinish() {
+                call_relyout.isEnabled = true
+            }
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 1 || messageEvent.tag == 2) {
+            val tcpModel = messageEvent.getMessage() as TcpModel
+            Log.i(TAG, tcpModel.toJson())
+            if (tcpModel.type == TcpType.VOICE) {
+                if (tcpModel.action == TcpAction.VoiceAction.SUCCESS) {
+                    //loadingDialog.dismiss()
+                    //界面呈现,逻辑在service中
+                } else if (tcpModel.action == TcpAction.VoiceAction.FAILED) {
+                    //loadingDialog.dismiss()
+                    call_relyout.isEnabled = true
+                    DeviceChannel.calling = false
+                    DeviceChannel.callId = 0
+                    showMessage(R.string.call_failed)
+                }else if (tcpModel.action == TcpAction.VoiceAction.CALLING){
+                    showMessage(R.string.call_busy)
+                    DeviceChannel.calling = false
+                }
+            }
+        }
+    }
+}

+ 157 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchContactsActivity.kt

@@ -0,0 +1,157 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.content.Intent
+import android.util.Log
+import android.view.View
+import com.alibaba.android.vlayout.DelegateAdapter
+import com.alibaba.android.vlayout.VirtualLayoutManager
+import com.enation.javashop.android.jrouter.external.annotation.Router
+import com.enation.javashop.net.engine.model.NetState
+import com.scwang.smartrefresh.layout.footer.ClassicsFooter
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.adapter.WatchContactsItemAdapter
+import com.wdkl.ncs.android.component.home.databinding.WatchContactsLayBinding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.logic.contract.home.WatchActivityContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.WatchActivityPresenter
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactsVO
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import kotlinx.android.synthetic.main.watch_contacts_lay.*
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+@Router(path = "/watch/contacts")
+class WatchContactsActivity : BaseActivity<WatchActivityPresenter, WatchContactsLayBinding>(), WatchActivityContract.View {
+    var TAG = WatchContactsActivity::class.java.getSimpleName()
+
+    private var page: Int = 1
+    private val adapter = WatchContactsItemAdapter(ArrayList(),this)
+
+    private lateinit var virtualLayoutManager: VirtualLayoutManager
+
+    private lateinit var delegateAdapter: DelegateAdapter
+
+
+    override fun getLayId(): Int {
+        return R.layout.watch_contacts_lay
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    override fun onError(message: String, type: Int) {
+        dismissDialog()
+        showMessage(message)
+    }
+
+    override fun complete(message: String, type: Int) {
+        dismissDialog()
+        showMessage(message)
+    }
+
+    override fun start() {
+        showDialog()
+    }
+
+    override fun networkMonitor(state: NetState) {
+
+    }
+
+    override fun destory() {
+
+    }
+
+    override fun init() {
+
+        /**初始化LayoutMannager*/
+        virtualLayoutManager = VirtualLayoutManager(this.activity)
+
+        /**初始化适配器*/
+        delegateAdapter = DelegateAdapter(virtualLayoutManager)
+        delegateAdapter.addAdapter(adapter)
+        mViewBinding.refresh.setRefreshFooter(ClassicsFooter(activity))
+        /**配置到RecycleView*/
+        watch_contacts_list.layoutManager = virtualLayoutManager
+        watch_contacts_list.adapter = delegateAdapter
+    }
+
+    override fun onResume() {
+        super.onResume()
+        presenter.loadData(page, Constants.deviceId)
+    }
+
+    override fun bindEvent() {
+        refresh.setOnLoadMoreListener {
+            page += 1
+            presenter.loadData(page, Constants.deviceId)
+        }
+        refresh.setOnRefreshListener {
+            page = 1
+            presenter.loadData(1, Constants.deviceId)
+        }
+
+        adapter.setOnItemClickListener { data, position ->
+            var customerId = data.customerId
+            if(data.customerId == null){
+                customerId = 0
+            }
+
+            val watchContactsVO :WatchContactsVO = data
+            var intent = Intent(this, WatchCallRecordsActivity::class.java)
+            intent.putExtra("data", watchContactsVO)
+            intent.putExtra("action", "CUSTOMER_RECORDS")
+            intent.putExtra("customer_id", ""+customerId)
+            startActivity(intent)
+
+        }
+    }
+
+    override fun render(data: ArrayList<WatchContactsVO>) {
+
+        Log.i(TAG,"返回的数据 "+data)
+        refresh.finishRefresh()
+        if (page == 1) {
+            refresh.resetNoMoreData()
+            if (data.size > 0) {
+                adapter.data.clear()
+                adapter.data.addAll(data)
+                Log.i("abc1", " " + data.size);
+                adapter.notifyDataSetChanged()
+            } else {
+                tv_empty_contacts.visibility = View.VISIBLE
+            }
+            refresh.finishLoadMore()
+        } else {
+            if (data.size > 0) {
+                adapter.data.addAll(data)
+                adapter.notifyDataSetChanged()
+                refresh.finishLoadMore()
+            } else {
+                refresh.finishLoadMoreWithNoMoreData()
+            }
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 2) {
+            val resTcpModel = messageEvent.getMessage() as TcpModel
+            if (resTcpModel.action  == TcpAction.VoiceAction.FAILED){
+                showMessage(R.string.call_failed)
+            } else if (resTcpModel.action == TcpAction.VoiceAction.CALLING){
+                showMessage(R.string.call_busy)
+            }
+        } else if (messageEvent.tag == 4) {
+            presenter.loadData(page, Constants.deviceId)
+        }
+    }
+}

+ 931 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt

@@ -0,0 +1,931 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.Manifest
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.graphics.Color
+import android.net.Uri
+import android.os.*
+import android.provider.Settings
+import android.telephony.PhoneStateListener
+import android.telephony.SignalStrength
+import android.telephony.TelephonyManager
+import android.text.TextUtils
+import android.util.Log
+import android.view.View
+import com.enation.javashop.net.engine.model.NetState
+import com.enation.javashop.net.engine.plugin.permission.RxPermissions
+import com.enation.javashop.utils.base.tool.CommonTool
+import com.enation.javashop.utils.base.widget.LoadingDialog
+import com.google.common.base.Strings
+import com.wdkl.ncs.android.component.home.BuildConfig
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.broadcast.BatteryBroadcastReceiver
+import com.wdkl.ncs.android.component.home.broadcast.MyMediaButtonReceiver
+import com.wdkl.ncs.android.component.home.databinding.WatchActivityHome2Binding
+import com.wdkl.ncs.android.component.home.launch.HomeLaunch
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.home.util.*
+import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.wdkl.ncs.android.lib.utils.*
+import com.wdkl.ncs.android.lib.utils.Util
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.logic.contract.home.WatchHomeActivityContract
+import com.wdkl.ncs.android.middleware.logic.presenter.home.WatchHomeActivityPresenter
+import com.wdkl.ncs.android.middleware.model.ServerIpInfo
+import com.wdkl.ncs.android.middleware.model.dos.AppVersionDO
+import com.wdkl.ncs.android.middleware.model.dos.ChannelDO
+import com.wdkl.ncs.android.middleware.model.dos.ChannelImDO
+import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
+import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
+import com.wdkl.ncs.android.middleware.model.vo.DeviceVO
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactVO
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.TcpClientHandler
+import com.wdkl.ncs.android.middleware.tcp.enums.CommunicationEnum
+import com.wdkl.ncs.android.middleware.utils.CommonUtils
+import com.wdkl.ncs.android.middleware.utils.ContactHelper
+import com.wdkl.ncs.keepbackground.utils.SpManager
+import com.wdkl.ncs.keepbackground.work.DaemonEnv
+import com.wdkl.rtc.util.JanusConstant
+import io.reactivex.Observable
+import kotlinx.android.synthetic.main.watch_activity_home2.*
+import kotlinx.android.synthetic.main.watch_activity_register.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+import java.util.*
+import kotlin.collections.ArrayList
+
+
+//@Router(path = "/watch/home")
+class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivityHome2Binding>(), WatchHomeActivityContract.View, View.OnClickListener {
+
+    var TAG = WatchHome2Activity::class.java.getSimpleName()
+
+    //监听网络变化
+    lateinit var teleManager: TelephonyManager
+    private var netType: Int = -1
+
+    lateinit var batteryBroadcastReceiver: BatteryBroadcastReceiver
+    lateinit var myMediaButtonReceiver: MyMediaButtonReceiver
+    private var isUnRegister = true
+    lateinit var loadingDialog: LoadingDialog
+    lateinit var netOffLoadingDialog: LoadingDialog
+    lateinit var countDownTimer: CountDownTimer
+
+    private var clickTime: Long = 0
+    private var contactUpdating = false
+
+    private var language = "zh"
+
+    private lateinit var netWorkChangeReceiver: NetWorkChangeReceiver
+
+    private val REQUEST_MANAGER_PERMISSION = 11
+
+    private var tcpInit = false
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        if (!isTaskRoot) {
+            finish()
+        }
+        loadingDialog =
+            CommonTool.createLoadingDialog(this, R.layout.custom_loading, R.id.loadding_image)
+        netOffLoadingDialog =
+            CommonTool.createLoadingDialog(this, R.layout.netoff_loading, R.id.netoff_loading_image)
+
+        super.onCreate(savedInstanceState)
+    }
+
+    override fun getLayId(): Int {
+        return R.layout.watch_activity_home2
+    }
+
+    override fun bindDagger() {
+        HomeLaunch.component.inject(this)
+    }
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        super.onWindowFocusChanged(hasFocus)
+        window.decorView.systemUiVisibility = screen_flags
+    }
+
+    //初始化
+    override fun init() {
+        AnrFcExceptionUtil.getInstance(application).initFCException()
+        //注册广播
+        regReceiver()
+
+        tv_register_version.text = "v" + CommonUtils.getAppVersionName(activity)
+
+        initCountDownTimer()
+
+        //网络强度监听
+        teleManager =
+            (applicationContext.getSystemService(Context.TELEPHONY_SERVICE)) as TelephonyManager
+        netType = NetHelper.getInstance().getNetworkState(applicationContext)
+        Log.i(TAG, "网络类型:" + netType)
+        /*
+-50 to -79 dBm, then it's generally considered great signal (4 to 5 bars).
+-80 to -89 dBm, then it's generally considered good signal (3 to 4 bars).
+-90 to -99 dBm, then it's generally considered average signal (2 to 3 bars).
+-100 to -109 dBm, then it's generally considered poor signal (1 to 2 bars).
+-110 to -120 dBm, then it's generally considered very poor signal (0 to 1 bar)
+         */
+        //if (netType == NetHelper.NETWORK_4G) {
+            if (teleManager != null) {
+                teleManager.listen(object : PhoneStateListener() {
+                    override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
+                        if (NetHelper.getInstance().getNetworkState(applicationContext) == NetHelper.NETWORK_NONE) {
+                            tv_signal_strength.setText(R.string.network_disconnect)
+                        } else {
+                            tv_signal_strength.setText(R.string.network_connect)
+                        }
+
+                        super.onSignalStrengthsChanged(signalStrength)
+                    }
+                }, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+            }
+        //}
+
+        requestPermissions()
+
+        language = LocaleMangerUtils.getApplicationLocale().language
+    }
+
+    private fun getDeviceId() {
+        Thread{
+            if ("Redmi".equals(Build.BRAND)) {
+                Constants.imei = DeviceIdUtils.execRootCmd("getprop ro.ril.miui.imei0")
+                Log.d(TAG, "redmi imei0: " + Constants.imei)
+            } else {
+                Constants.imei = Util.getIMEI(this)
+            }
+
+            runOnUiThread {
+                if (!TextUtils.isEmpty(Constants.imei)) {
+                    permissionGranted()
+                }
+            }
+        }.start()
+    }
+
+    private fun permissionGranted() {
+        /*if ("Redmi".equals(Build.BRAND)) {
+            Constants.imei = DeviceIdUtils.getDeviceId(activity)
+        } else {
+            Constants.imei = Util.getIMEI(this)
+        }*/
+
+        Log.i(TAG, "IMEI " + Constants.imei)
+        Log.i(TAG, "mac " + Constants.mac)
+        tv_feedback_device_info.text = Constants.imei
+
+        //申请悬浮窗权限
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (!Settings.canDrawOverlays(this)) {
+                val intent = Intent(
+                    Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+                    Uri.parse("package:$packageName")
+                )
+                startActivityForResult(intent, 10)
+            }
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            if (!Environment.isExternalStorageManager()) {
+                requestManagerPermission()
+            } else {
+                initTcp()
+            }
+        } else {
+            initTcp()
+        }
+
+
+        if (SettingConfig.getVoiceCallType(BaseApplication.appContext) == SettingConfig.PHONE_CALL) {
+            Constants.phoneType = CommunicationEnum.MOBILE_PHONE.value()
+        }
+
+        tv_server_ip.text = CommonUtils.getUrl(BaseApplication.appContext)
+
+        if (DaemonEnv.app == null && !WdKeepAliveService.instanceCreated) {
+            Log.d(TAG, "开始 WdKeepAliveService")
+            //保活守护进程
+            DaemonEnv.init(this)
+            DaemonEnv.startServiceSafely(this, WdKeepAliveService::class.java)
+        }
+    }
+
+
+    private fun requestManagerPermission() {
+        //当系统在11及以上
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            // 没文件管理权限时申请权限
+            if (!Environment.isExternalStorageManager()) {
+                val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
+                intent.setData(Uri.parse("package:" + getPackageName()))
+                startActivityForResult(intent, REQUEST_MANAGER_PERMISSION)
+            }
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+
+        if (requestCode == REQUEST_MANAGER_PERMISSION && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            //用户拒绝权限,重新申请
+            if (!Environment.isExternalStorageManager()) {
+                //requestManagerPermission()
+                showMessage(R.string.grant_write_permission)
+            } else {
+                initTcp()
+            }
+        }
+    }
+
+
+    private fun regReceiver() {
+
+//        myMediaButtonReceiver = MyMediaButtonReceiver()
+//        val ittFilterButton = IntentFilter(Intent.ACTION_MEDIA_BUTTON) //控制键
+//        registerReceiver(myMediaButtonReceiver, ittFilterButton);
+
+//        val ittFilterBluetooth = IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED)  //蓝牙断开
+//        registerReceiver(myMediaButtonReceiver, ittFilterBluetooth);
+
+        // 动态注册广播接收器
+        // 过滤器
+        val intentFilter = IntentFilter()
+        // 系统的网络被更改的过滤器
+        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE")
+        netWorkChangeReceiver = NetWorkChangeReceiver()
+        registerReceiver(netWorkChangeReceiver, intentFilter)
+
+        //电池
+        batteryBroadcastReceiver = BatteryBroadcastReceiver(electric_quantity_tv)
+        val intentFilter2 = IntentFilter()
+        intentFilter2.addAction(Intent.ACTION_BATTERY_CHANGED)
+        registerReceiver(batteryBroadcastReceiver, intentFilter2)
+    }
+
+    private fun releaseReceiver() {
+        if (batteryBroadcastReceiver != null) {
+            unregisterReceiver(batteryBroadcastReceiver)
+        }
+
+        if (netWorkChangeReceiver != null) {
+            unregisterReceiver(netWorkChangeReceiver)
+        }
+//        if (myMediaButtonReceiver != null ){
+//            unregisterReceiver(myMediaButtonReceiver)
+//        }
+    }
+
+    /**
+     * 返回的tcp信息
+     */
+    override fun setTcpServerHost(tcpSeverDTO: TcpSeverDTO) {
+        /*Log.d(TAG, "获取服务器IP完成")
+        if (tcpSeverDTO.publicIp != null && tcpSeverDTO.tcpPort != null && tcpSeverDTO.readerIdleTime != null) {
+            Constants.tcpServer = tcpSeverDTO.publicIp
+            Constants.tcpPort = tcpSeverDTO.tcpPort
+            Constants.heartBeat = tcpSeverDTO.readerIdleTime
+            tv_server_ip.text = tcpSeverDTO.publicIp
+
+            //成功获取到数据后再启动keepService并连接tcp服务器
+            if (DaemonEnv.app == null && !Strings.isNullOrEmpty(Constants.tcpServer) && !WdKeepAliveService.instanceCreated) {
+                Log.d(TAG, "开始 WdKeepAliveService")
+                //保活守护进程
+                DaemonEnv.init(this)
+                DaemonEnv.startServiceSafely(this, WdKeepAliveService::class.java)
+            }
+
+            presenter.getDeviceVO(Constants.imei)
+        }*/
+    }
+
+    override fun setServerInfo(serverIpInfo: ServerIpInfo) {
+        if (serverIpInfo.tcpPublicIp != null && serverIpInfo.tcpPort != null && serverIpInfo.tcpIdleSeconds != null) {
+            Constants.tcpServer = serverIpInfo.tcpPublicIp
+            Constants.tcpPort = serverIpInfo.tcpPort
+            Constants.heartBeat = serverIpInfo.tcpIdleSeconds
+            tv_server_ip.text = serverIpInfo.tcpPublicIp
+
+            JanusConstant.JANUS_URL = "ws://" + serverIpInfo.rtcPublicIp + ":" + serverIpInfo.rtcPort
+            JanusConstant.STUN_SERVER = arrayOf<String>(serverIpInfo.stunServer)
+            //JanusConstant.TURN_SERVER = data.turnServer
+
+            //成功获取到数据后再启动keepService并连接tcp服务器
+            /*if (DaemonEnv.app == null && !Strings.isNullOrEmpty(Constants.tcpServer) && !WdKeepAliveService.instanceCreated) {
+                Log.d(TAG, "开始 WdKeepAliveService")
+                //保活守护进程
+                DaemonEnv.init(this)
+                DaemonEnv.startServiceSafely(this, WdKeepAliveService::class.java)
+            }*/
+
+            presenter.getDeviceVO(Constants.imei)
+        }
+
+
+        //通过服务端设置语言
+        if (SettingConfig.getLanguageMode(activity) == 0) {
+            var needReboot = false
+            if ("cn".equals(serverIpInfo.lang)) {
+                //中文
+                if (SettingConfig.getLanguageId(activity) != 2) {
+                    needReboot = true
+                }
+                SettingConfig.setLanguageId(activity, 2)
+            } else if ("en".equals(serverIpInfo.lang)) {
+                //英文
+                if (SettingConfig.getLanguageId(activity) != 1) {
+                    needReboot = true
+                }
+                SettingConfig.setLanguageId(activity, 1)
+            } else if ("es".equals(serverIpInfo.lang)) {
+                //西班牙语
+                if (SettingConfig.getLanguageId(activity) != 3) {
+                    needReboot = true
+                }
+                SettingConfig.setLanguageId(activity, 3)
+            }
+
+            if (needReboot) {
+                AppTool.Time.delay(5000) {
+                    AppUtils.restartApp()
+                }
+            }
+        }
+    }
+
+    private fun requestPermissions() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            Observable.just("")
+                .compose(
+                    RxPermissions(this)
+                        .ensure(
+                            Manifest.permission.CAMERA,
+                            Manifest.permission.READ_EXTERNAL_STORAGE,
+                            Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                            Manifest.permission.ACCESS_WIFI_STATE,
+                            Manifest.permission.BLUETOOTH,
+                            Manifest.permission.RECORD_AUDIO,
+                            Manifest.permission.CALL_PHONE,
+                            Manifest.permission.READ_PHONE_STATE,
+                            Manifest.permission.READ_CALL_LOG,
+                            Manifest.permission.ANSWER_PHONE_CALLS,
+                            Manifest.permission.WRITE_CALL_LOG,
+                            Manifest.permission.READ_CONTACTS,
+                            Manifest.permission.WRITE_CONTACTS,
+                            Manifest.permission.SEND_SMS,
+                            Manifest.permission.READ_SMS,
+                            Manifest.permission.RECEIVE_SMS
+                        )
+                )
+                .subscribe {
+                    if (!it) {
+                        showMessage(R.string.permission_tips)
+                        requestPermissions()
+                    } else {
+                        getDeviceId()
+                    }
+                }.joinManager(disposableManager)
+        } else {
+            Observable.just("")
+                .compose(
+                    RxPermissions(this)
+                        .ensure(
+                            Manifest.permission.CAMERA,
+                            Manifest.permission.READ_EXTERNAL_STORAGE,
+                            Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                            Manifest.permission.ACCESS_WIFI_STATE,
+                            Manifest.permission.BLUETOOTH,
+                            Manifest.permission.RECORD_AUDIO,
+                            Manifest.permission.CALL_PHONE,
+                            Manifest.permission.READ_PHONE_STATE,
+                            Manifest.permission.READ_CALL_LOG,
+                            Manifest.permission.WRITE_CALL_LOG,
+                            Manifest.permission.READ_CONTACTS,
+                            Manifest.permission.WRITE_CONTACTS,
+                            Manifest.permission.SEND_SMS,
+                            Manifest.permission.READ_SMS,
+                            Manifest.permission.RECEIVE_SMS
+                        )
+                )
+                .subscribe {
+                    if (!it) {
+                        showMessage(R.string.permission_tips)
+                        requestPermissions()
+                    } else {
+                        getDeviceId()
+                    }
+                }.joinManager(disposableManager)
+        }
+    }
+
+    override fun handleAppVersion(appInfo: AppVersionDO) {
+        val versionCode = CommonUtils.getAppVersionCode(activity)
+        if (versionCode > 0 && versionCode < appInfo.versionNo && !AppUpdateActivity.opened) {
+            showMessage(R.string.new_version_tips)
+            val intent = Intent()
+            intent.setClass(this, AppUpdateActivity::class.java)
+            startActivity(intent)
+        }
+    }
+
+    /**
+     * 返回的设备信息
+     */
+    override fun setDeviceDo(data: DeviceVO) {
+        if (data.id == null || data.id <= 0) {
+            loadingDialog.dismiss()
+            showMessage(R.string.device_not_register)
+            tv_register_status.setText(R.string.device_not_register_tips)
+            if (DaemonEnv.app != null) {
+                SpManager.getInstance().putBoolean(Constants.SYSTEM_REGISTERED, false)
+            }
+            tv_register_ok.visibility = View.VISIBLE
+            watch_activity_register_layout.visibility = View.VISIBLE
+            watch_activity_home_linyout.visibility = View.GONE
+            return
+        }
+
+        Constants.partId = data.partId
+        Constants.deviceId = data.id
+        Constants.sipId = data.sipId
+
+        loadingDialog.dismiss()
+        watch_activity_register_layout.visibility = View.GONE
+        watch_activity_home_linyout.visibility = View.VISIBLE
+        tv_register_ok.visibility = View.GONE
+
+        tv_register_status.text = "Loading..."
+        tv_register_status.setTextColor(Color.GREEN)
+
+        isUnRegister = false
+
+        Log.i(TAG, "收到返回的设备信息 ")
+        Constants.ethIp = data.ethIp
+        Constants.sipPassword = data.sipPassword
+        Constants.memberId = data.memberId
+        Constants.userName = data.memberName
+        Constants.userRoleName = data.roleName
+
+        presenter.getAppVersion(Constants.partId, 7)
+        presenter.getWatchContacts(Constants.deviceId)
+
+        if (Constants.deviceId <= 0 || TextUtils.isEmpty(Constants.sipId)) {
+            showMessage(R.string.init_data_error)
+            tv_feedback_device_info.setText(R.string.init_data_error)
+            return
+        } else if (TextUtils.isEmpty(Constants.tcpServer)) {
+            showMessage(R.string.init_server_data_error)
+            tv_feedback_device_info.setText(R.string.init_server_data_error)
+            return
+        }
+
+        if (DaemonEnv.app != null) {
+            SpManager.getInstance().putBoolean(Constants.SYSTEM_REGISTERED, true)
+        }
+
+        presenter.getDeviceSettingData(Constants.partId)
+
+        //onTcpConnectSuccess()
+
+        if (Strings.isNullOrEmpty(Constants.userName)) {
+            watch_name_tv.setText(R.string.str_reload)
+            watch_name_tv.setTextColor(Color.BLACK)
+            watch_name_tv.textSize = 13f
+            watch_name_tv.setBackgroundResource(R.drawable.javashop_btn_balck_line_bg)
+            watch_name_tv.setOnClickListener {
+                loadingDialog.show()
+                presenter.getDeviceVO(Constants.imei)
+                return@setOnClickListener
+            }
+
+            watch_role_name_tv.setText(R.string.device_user_empty)
+            watch_role_name_tv.textSize = 12f
+        } else {
+            watch_name_tv.text = Constants.userName
+            if (Constants.userName != null) {
+                if (Constants.userRoleName!!.contains("腕表")) {
+                    watch_role_name_tv.text = Constants.userRoleName!!.substring(0, (Constants.userRoleName)!!.indexOf("腕表"))
+                } else {
+                    watch_role_name_tv.text = Constants.userRoleName
+                }
+                watch_user_phone_tv.text = data.phoneNumber
+            }
+
+            if (Locale.CHINESE.getLanguage().equals(language)) {
+                watch_role_name_tv.visibility = View.VISIBLE
+            }
+        }
+        Constants.phoneNumber = data.phoneNumber
+
+        //获取频道列表
+        if (data.memberId != null) {
+            presenter.getDeviceVoiceChannel(data.memberId)
+        }
+
+        ll_clerk_list.setOnClickListener {
+            var intent = Intent(this, TakeoverActivity().javaClass)
+            startActivity(intent)
+        }
+
+        //初始化语音留言文件保存路径
+        RecordHelper.getInstance().init()
+
+        //加载电话号码白名单
+        presenter.getPhoneNumberWhiteList(Constants.deviceId)
+
+        showMessage("Load complete")
+    }
+
+    override fun setDeviceVoiceChannel(channelList: ArrayList<ChannelDO>) {
+        //取第一个频道
+        if (channelList != null && channelList.size > 0) {
+            Constants.channelId = channelList.get(0).id
+        }
+    }
+
+    override fun setPhoneNumberWhiteList(phoneList: ArrayList<String>) {
+        //保存手机号码白名单
+        Constants.phoneWhiteList.clear()
+        Constants.phoneWhiteList.addAll(phoneList)
+    }
+
+    //写手机通讯录
+    override fun setContact(contactsVOs: List<WatchContactVO>) {
+        if (contactsVOs != null && contactsVOs.size > 0) {
+            if (!contactUpdating) {
+                contactUpdating = true
+                Thread {
+                    try {
+                        Log.e(TAG, "111 start update contacts...")
+                        /*for (contactVO in contactVOs) {
+                            if (!Strings.isNullOrEmpty(contactVO.name) && !Strings.isNullOrEmpty(contactVO.phoneNumber)) {
+                                ContactHelper.setContact(BaseApplication.appContext, contactVO.name, contactVO.phoneNumber)
+                            }
+                        }*/
+                        ContactHelper.setContact2(BaseApplication.appContext, contactsVOs)
+                        Log.e(TAG, "111 update contacts end...")
+                        contactUpdating = false
+                    } catch (ex: Exception) {
+                        contactUpdating = false
+                        ex.printStackTrace()
+                    }
+                }.start()
+
+            } else {
+                showMessage(R.string.contact_update_porgress)
+            }
+        }
+    }
+
+    /**
+     * 设置设备数据
+     */
+    override fun setDeviceSettingData(partSettingDO: PartSettingDO) {
+        Log.i(TAG, "收到设置设备数据 transferDuration" + partSettingDO.transferDuration)
+        Log.i(TAG, "收到设置设备数据 transferDurationLeader" + partSettingDO.transferDurationLeader)
+        if (partSettingDO != null && partSettingDO.transferDuration != null && partSettingDO.transferDurationLeader != null) {
+            if (Constants.userRoleName != null) {
+                if (!Constants.userRoleName!!.contains("护士组长") && Constants.userRoleName!!.contains("护士")) {
+                    SettingConfig.setCountdownTime(application, partSettingDO.transferDuration)
+                } else if (Constants.userRoleName!!.contains("护士组长")) {
+                    SettingConfig.setCountdownTime(application, partSettingDO.transferDurationLeader)
+                }
+            }
+        }
+
+        //设置通话方式
+        if (partSettingDO.communicationModeMobile != null) {
+            Constants.phoneType = partSettingDO.communicationModeMobile
+            if (Constants.phoneType == CommunicationEnum.MOBILE_PHONE.value()) {
+                SettingConfig.setVoiceCallType(BaseApplication.appContext, SettingConfig.PHONE_CALL)
+            } else {
+                SettingConfig.setVoiceCallType(BaseApplication.appContext, SettingConfig.SIP_CALL)
+            }
+        }
+        if (partSettingDO.communicationModeBed != null) {
+            Constants.bedPhoneType = partSettingDO.communicationModeBed
+        }
+        if (partSettingDO.communicationModeNurse != null) {
+            Constants.nursePhoneType = partSettingDO.communicationModeNurse
+        }
+    }
+
+    fun onTcpConnectSuccess() {
+        Log.i(TAG, "连接成功")
+
+        runOnUiThread(Runnable {
+            Log.i(TAG, "连接成功设置UI")
+            tv_net_reconnect_text.visibility = View.GONE
+            tv_signal_strength.setText(R.string.tcp_connect_success)
+            netOffLoadingDialog.dismiss()
+            sip_state_tv.setBackgroundColor(Color.parseColor("#00FFFF"))
+        })
+    }
+
+    fun onTcpConnectFailed() {
+        Log.w(TAG, "网络断开")
+
+        runOnUiThread(Runnable {
+            tv_signal_strength.setText(R.string.tcp_connect_failed)
+            tv_net_reconnect_text.visibility = View.VISIBLE
+            netOffLoadingDialog.show()
+            sip_state_tv.setBackgroundColor(Color.parseColor("#FF0000"))
+        })
+
+        //等30秒
+        var waitSeconds = 0
+        Thread {
+            while (!NettyClient.instance.isConnect()) {
+                Log.w(TAG, "无网络" + waitSeconds)
+                waitSeconds++
+                if (waitSeconds > 30) {
+                    break
+                }
+                Thread.sleep(5000)
+            }
+
+//            if (NettyClient.instance.isConnect()) {
+//                onTcpConnectSuccess()
+//            }
+        }.start()
+    }
+
+    override fun bindEvent() {
+        customer_list_linlyout.setOnClickListener(this)
+        call_records_linlyout.setOnClickListener(this)
+        user_name_linlyout.setOnClickListener(this)
+        state_linlyout.setOnClickListener(this)
+        other_linyout.setOnClickListener(this)
+        channel_im_layout.setOnClickListener(this)
+        tv_register_ok.setOnClickListener {
+            Log.d(TAG, "注册完成")
+            initTcp()
+
+            //presenter.getDeviceVO(Constants.imei)
+            countDownTimer.start()
+        }
+
+        tv_restart_app.setOnClickListener {
+            AppUtils.restartApp()
+        }
+
+        tv_config_server.setOnClickListener {
+            ServerConfigDialogHelper.showPasswordDialog(activity)
+        }
+
+        tv_language_settings.setOnClickListener {
+            LanguageSetDialogHelper.showDialog(activity)
+        }
+    }
+
+    override fun enableHeadsetVoiceMsg(): Boolean {
+        return false
+    }
+
+    private fun initTcp() {
+        if (!Strings.isNullOrEmpty(Constants.tcpServer)) {    //有网且得到了服务器IP
+            //presenter.getTcpServerHost()
+            presenter.getServerInfo()
+            return
+        }
+
+        if (tcpInit) {
+            return
+        }
+        tcpInit = true
+
+        var count = 30
+        Thread {
+            while (isUnRegister && count > 0 && Strings.isNullOrEmpty(Constants.tcpServer)) {
+                //DaemonEnv.sendStopWorkBroadcast(this)
+                Log.i(TAG, "获取TCP服务器IP和端口")
+                //presenter.getTcpServerHost()
+                presenter.getServerInfo()
+
+                try {
+                    Thread.sleep(15000)
+                } catch (e: Exception) {
+                }
+                count--
+            }
+
+            if (NetHelper.getInstance().getNetworkState(this) == NetHelper.NETWORK_NONE) {
+                runOnUiThread(Runnable {
+                    showMessage(R.string.net_disconnect)
+                })
+            }
+        }.start()
+    }
+
+    fun initCountDownTimer() {
+        countDownTimer = object : CountDownTimer(1 * 1000L, 1000) {
+            override fun onTick(millisUntilFinished: Long) {
+            }
+
+            override fun onFinish() {
+                tv_register_ok.isEnabled = true
+                //loadingDialog.dismiss()
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        window.decorView.systemUiVisibility = screen_flags
+
+        //主界面恢复时检查tcp连接状态
+        Log.d(TAG, "Watch home activity resumed...")
+        if (TcpClientHandler.getConnected()) {
+            onTcpConnectSuccess()
+        } else {
+            onTcpConnectFailed()
+        }
+
+        updatePower()
+
+        Constants.allowVoiceMsg = true
+        tv_im_count.text = "" + WdKeepAliveService.channelImList.size
+
+        EventBus.getDefault().post(MessageEvent("im_done", Constants.EVENT_IM_PLAY_DONE))
+
+        //返回主界面时重置呼叫状态
+        /*if (!Constants.showFloatWindow) {
+            DeviceChannel.calling = false
+        }*/
+
+        //检查当前是否还有未处理事件,有则显示
+        if (Constants.eventList.size > 0) {
+            val intent = Intent()
+            intent.setClass(this, NewEventListActivity::class.java)
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            startActivity(intent)
+        }
+    }
+
+    override fun onError(message: String, type: Int) {
+    }
+
+    override fun complete(message: String, type: Int) {
+    }
+
+    override fun start() {
+    }
+
+    override fun networkMonitor(state: NetState) {
+    }
+
+    override fun destory() {
+        releaseReceiver()
+        //停止服务
+        //DaemonEnv.sendStopWorkBroadcast(this)
+
+        if (countDownTimer != null) {
+            countDownTimer.cancel()
+        }
+    }
+
+    /**
+     * 监听
+     */
+    override fun onClick(p0: View) {
+        when (p0.id) {
+            R.id.customer_list_linlyout -> {
+                push("/watch/contacts")
+            }
+            R.id.call_records_linlyout -> {
+                var intent = Intent(this, WatchCallRecordsActivity::class.java)
+                intent.putExtra("action", "ALL")
+                intent.putExtra("customer_id", "")
+                startActivity(intent)
+            }
+            R.id.user_name_linlyout -> {
+                if (Constants.userName == null) {
+                    var intent = Intent(this, TakeoverActivity().javaClass)
+                    startActivity(intent)
+                } else {
+                    var intent = Intent(this, WatchCallRecordsActivity::class.java)
+                    intent.putExtra("action", "UNREAD")
+                    intent.putExtra("customer_id", "")
+                    startActivity(intent)
+                }
+            }
+            R.id.channel_im_layout -> {
+                //群留言
+                if (Constants.channelId == -1) {
+                    showMessage(R.string.str_im_no_channel)
+                } else if (TextUtils.isEmpty(Constants.userName)) {
+                    showMessage(R.string.device_user_not_register)
+                } else {
+                    var intent = Intent(this, ChannelImActivity::class.java)
+                    startActivity(intent)
+                }
+            }
+            R.id.state_linlyout -> {
+//                Log.d(TAG,"点击了状态,测试崩溃")
+//                netOffLoadingDialog.show()
+//                Thread.sleep(10000)
+//                netOffLoadingDialog.dismiss()
+            }
+            R.id.other_linyout -> {
+                if (System.currentTimeMillis() - clickTime > 2000) {
+                    showMessage(R.string.click_twice_start_settings)
+                } else {
+                    val intent = Intent(this, WatchUserSettingActivity::class.java)
+                    startActivity(intent)
+                }
+                clickTime = System.currentTimeMillis()
+            }
+        }
+    }
+
+
+    override fun onBackPressed() {
+        //禁用返回
+        //super.onBackPressed()
+    }
+
+    private fun getPower(): Int {
+        val mBatteryManager = getSystemService(BATTERY_SERVICE) as BatteryManager
+        val power = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
+        Log.d(TAG, "power energy: power percent: $power")
+
+        return power
+    }
+
+    private fun updatePower() {
+        val power = getPower()
+        if (10 < power && power < 20) {
+            electric_quantity_tv.text = "" + power
+            battery_warning_tv.setTextColor(Color.parseColor("#FF3030"))
+            battery_warning_tv.visibility = View.VISIBLE
+            battery_warning_tv.setText(R.string.low_power)
+        } else if (10 > power) {
+            electric_quantity_tv.text = "" + power
+            battery_warning_tv.setTextColor(Color.parseColor("#8B2323"))
+            battery_warning_tv.visibility = View.VISIBLE
+            battery_warning_tv.setText(R.string.low_power)
+        } else {
+            battery_warning_tv.visibility = View.GONE
+            electric_quantity_tv.text = "" + power
+        }
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        if (loadingDialog != null) {
+            loadingDialog.dismiss()
+        }
+        if (netOffLoadingDialog != null) {
+            netOffLoadingDialog.dismiss()
+        }
+    }
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == Constants.EVENT_UNTREATED_QUANTITY) {   //未读
+            var message = messageEvent.getMessage() as ArrayList<InteractionVO>
+            list_item_img_icon_num.text = "" + message.size
+        } else if (messageEvent.tag == Constants.EVENT_BATTERY_PERCENT) {
+            var message = messageEvent.getMessage() as Int
+            updatePower()
+
+            /*if (10 < message && message < 20) {
+                electric_quantity_tv.text = "" + message
+                battery_warning_tv.setTextColor(Color.parseColor("#FF3030"))
+                battery_warning_tv.visibility = View.VISIBLE
+                battery_warning_tv.text = "低电量,请注意充电"
+            } else if (10 > message) {
+                electric_quantity_tv.text = "" + message
+                battery_warning_tv.setTextColor(Color.parseColor("#8B2323"))
+                battery_warning_tv.visibility = View.VISIBLE
+                battery_warning_tv.text = "电量过低,请充电"
+            } else {
+                battery_warning_tv.visibility = View.GONE
+                electric_quantity_tv.text = "" + message
+            }*/
+        } else if (messageEvent.tag == Constants.EVENT_RTC_STATE) {
+            runOnUiThread(Runnable {
+                sip_state_tv.setBackgroundColor(Color.parseColor("#00FFFF"))
+                netOffLoadingDialog.dismiss()
+                //tv_signal_strength.text = "网络恢复"
+                tv_net_reconnect_text.visibility = View.GONE
+            })
+        } else if (messageEvent.tag == Constants.EVENT_TCP_BREAK) {
+            onTcpConnectFailed()
+        } else if (messageEvent.tag == Constants.EVENT_TCP_CONNECTED) {
+            onTcpConnectSuccess()
+        } else if (messageEvent.tag == Constants.EVENT_IM_PLAY_DONE || messageEvent.tag == Constants.EVENT_CLEAR_IM || messageEvent.tag == 3) {
+            tv_im_count.text = "" + WdKeepAliveService.channelImList.size
+        }
+    }
+}

+ 267 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java

@@ -0,0 +1,267 @@
+package com.wdkl.ncs.android.component.home.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.google.common.base.Strings;
+import com.wdkl.ncs.android.component.home.BuildConfig;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.util.AppUtils;
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils;
+import com.wdkl.ncs.android.component.home.util.NetHelper;
+import com.wdkl.ncs.android.component.home.util.PasswordDialogHelper;
+import com.wdkl.ncs.android.component.home.util.PhoneNumberDialogHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.lib.utils.ExtendMethodsKt;
+import com.wdkl.ncs.android.middleware.tcp.NettyClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.utils.CommonUtils;
+
+public class WatchUserSettingActivity extends Activity {
+    private static final String TAG = WatchUserSettingActivity.class.getSimpleName();
+
+    private TextView tvAppVersion,tvDeviceId,tvDeviceImei,tvDeviceIp,tvServerIp,tvTtsStatus,tvPhoneType;
+
+    private Button btnChange,btnAppRestart,btnCheckUpdate,btnSystemSetting;
+
+    private RadioGroup callGroup;
+    private RadioButton callYes, callNo;
+    private RadioGroup imChannelGroup;
+    private RadioButton imChannelYes, imChannelNo;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        //切换语言
+        int languageId = SettingConfig.getLanguageId(this);
+        LocaleMangerUtils.setApplicationLanguageByIndex(this, languageId);
+
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.user_setting_layout);
+
+        tvAppVersion = findViewById(R.id.tv_app_version);
+        tvDeviceId = findViewById(R.id.tv_device_id);
+        tvDeviceImei = findViewById(R.id.tv_device_imei);
+        tvDeviceIp = findViewById(R.id.tv_device_ip);
+        tvServerIp = findViewById(R.id.tv_server_ip);
+        tvTtsStatus = findViewById(R.id.tv_tts_status);
+        tvPhoneType = findViewById(R.id.tv_phone_type);
+
+        btnChange = findViewById(R.id.btn_user_change);
+        btnAppRestart = findViewById(R.id.btn_app_restart);
+        btnCheckUpdate = findViewById(R.id.btn_check_update);
+        btnSystemSetting = findViewById(R.id.btn_system_setting);
+
+        callGroup = findViewById(R.id.group_call);
+        callYes = findViewById(R.id.rb_call_yes);
+        callNo = findViewById(R.id.rb_call_no);
+
+        imChannelGroup = findViewById(R.id.group_im_channel);
+        imChannelYes = findViewById(R.id.rb_im_channel_yes);
+        imChannelNo = findViewById(R.id.rb_im_channel_no);
+
+        tvAppVersion.setText(CommonUtils.getAppVersionName(BaseApplication.appContext) + "_" + BuildConfig.BUILD_TIME);
+        tvDeviceId.setText("" + Constants.Companion.getDeviceId());
+        tvDeviceImei.setText(Constants.Companion.getImei());
+        tvDeviceIp.setText(NetHelper.getInstance().getLocalIP());
+        tvServerIp.setText(Constants.Companion.getTcpServer());
+        String type = getString(R.string.phone_type, Constants.Companion.getPhoneType(), Constants.Companion.getBedPhoneType(), Constants.Companion.getNursePhoneType());
+        tvPhoneType.setText(type);
+
+        if (SettingConfig.getVoiceCallType(BaseApplication.appContext) == SettingConfig.PHONE_CALL) {
+            callYes.setChecked(true);
+        } else {
+            callNo.setChecked(true);
+        }
+
+        if (SettingConfig.getImChannel(BaseApplication.appContext) == SettingConfig.IM_CHANNEL_ON) {
+            imChannelYes.setChecked(true);
+        } else {
+            imChannelNo.setChecked(true);
+        }
+
+        int mode = SettingConfig.getLanguageMode(BaseApplication.appContext);
+        RadioGroup languageMode = findViewById(R.id.group_language_mode);
+        RadioButton languageYes = findViewById(R.id.rb_language_yes);
+        RadioButton languageNo = findViewById(R.id.rb_language_no);
+        if (mode == 0) {
+            languageYes.setChecked(true);
+        } else {
+            languageNo.setChecked(true);
+        }
+        languageMode.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                if (checkedId == R.id.rb_language_yes) {
+                    SettingConfig.setLanguageMode(BaseApplication.appContext, 0);
+                } else {
+                    SettingConfig.setLanguageMode(BaseApplication.appContext, 1);
+                }
+            }
+        });
+
+        int originIndex = LocaleMangerUtils.getCurrentLocaleIndex();
+        Spinner spinner = findViewById(R.id.spinner_language_select);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
+                R.array.language_list, R.layout.spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spinner.setAdapter(adapter);
+        spinner.setSelection(originIndex);
+        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.d("languageId", "pos: " + position + ", originIndex: " + originIndex);
+                SettingConfig.setLanguageId(BaseApplication.appContext, position);
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+
+            }
+        });
+
+        findViewById(R.id.tv_tts_title).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                //tts test
+                String text = getString(R.string.speech_sample_text);
+                SpeechUtil.getInstance().startSpeak(text);
+            }
+        });
+
+        btnChange.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                TcpModel userChangeModel = DeviceUtil.userChange(Constants.Companion.getDeviceId());
+                NettyClient.Companion.getInstance().sendMsg(userChangeModel.toJson()).subscribe(it->{
+                    if (it) {
+                        Log.d(TAG, "TCP.发送消息完成");
+                        ExtendMethodsKt.showMessage(R.string.user_change_success);
+                        WatchUserSettingActivity.this.finish();
+                    } else {
+                        Log.e(TAG, "TCP.发送消息失败");
+                        ExtendMethodsKt.showMessage(R.string.user_change_failed);
+                    }
+                });
+            }
+        });
+
+        final Activity _this = this;
+        btnAppRestart.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                AppUtils.restartApp();
+            }
+        });
+        btnCheckUpdate.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                if ((System.currentTimeMillis() / 1000) - WdKeepAliveService.instance.getUpdateLastTime() > 10) {
+                    WdKeepAliveService.instance.setUpdateLastTime(System.currentTimeMillis() / 1000);
+                    Intent intent = new Intent();
+                    intent.setClass(_this, AppUpdateActivity.class);
+                    startActivity(intent);
+                } else {
+                    ExtendMethodsKt.showMessage(R.string.retry_tips);
+                }
+            }
+        });
+        btnSystemSetting.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                Intent intent = new Intent(Settings.ACTION_SETTINGS);
+                startActivity(intent);
+            }
+        });
+
+        callGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                if (checkedId == R.id.rb_call_yes) {
+                    //普通电话
+                    SettingConfig.setVoiceCallType(BaseApplication.appContext, SettingConfig.PHONE_CALL);
+                } else {
+                    //网络电话
+                    SettingConfig.setVoiceCallType(BaseApplication.appContext, SettingConfig.SIP_CALL);
+                }
+            }
+        });
+
+        imChannelGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                if (checkedId == R.id.rb_im_channel_yes) {
+                    SettingConfig.setImChannel(BaseApplication.appContext, SettingConfig.IM_CHANNEL_ON);
+                } else {
+                    SettingConfig.setImChannel(BaseApplication.appContext, SettingConfig.IM_CHANNEL_OFF);
+                }
+            }
+        });
+
+
+        Button update = findViewById(R.id.btn_update_contact);
+        update.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (Strings.isNullOrEmpty(Constants.Companion.getUserName())) {
+                    Toast.makeText(_this, R.string.device_user_not_register, Toast.LENGTH_SHORT).show();
+                } else {
+                    Intent intent = new Intent(getApplicationContext(), ContactUpdateActivity.class);
+                    startActivity(intent);
+                }
+            }
+        });
+
+        Button phoneNum = findViewById(R.id.btn_phone_number);
+        phoneNum.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                PasswordDialogHelper.showPasswordDialog(_this, new PasswordDialogHelper.ClickListener() {
+                    @Override
+                    public void onConfirm() {
+                        PhoneNumberDialogHelper.showDialog(_this);
+                    }
+                });
+            }
+        });
+
+        //test code
+        /*phoneNum.setOnClickListener(v -> {
+            Intent smsIntent = new Intent(_this, SmsReceivedActivity.class);
+            smsIntent.putExtra("address", "10086");
+            smsIntent.putExtra("sms_data", "尊敬的客户,您的手机号码为13611200010,品牌为全球通,入网时间为2011-08-01 14:00:18,业务区为长沙市,入网证件类型为本地身份证,付费方式为现金。");
+            smsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(smsIntent);
+        });*/
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        Log.d(TAG, "tts state: " + Constants.Companion.getTtsState());
+        if (Constants.Companion.getTtsState() == 2) {
+            tvTtsStatus.setText(R.string.tts_init_success);
+        } else {
+            tvTtsStatus.setText(R.string.tts_init_failed);
+        }
+    }
+}

+ 126 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/ChannelImItemAdapter.kt

@@ -0,0 +1,126 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+import android.app.Activity
+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.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.databinding.AdapterChannelImRecordsItemBinding
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect
+import com.wdkl.ncs.android.component.home.util.ImPlayDialogHelper
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper
+import com.wdkl.ncs.android.component.home.util.TimeTransition
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.api.ApiManager
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.model.dos.ChannelImDO
+import com.wdkl.ncs.android.middleware.model.vo.ChannelIMVO
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.channel.ChannelImUtil
+import org.greenrobot.eventbus.EventBus
+
+
+/**
+ * 呼叫记录适配器
+ */
+class ChannelImItemAdapter(val data: ArrayList<ChannelIMVO>, val imActivity: Activity) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding>, ChannelIMVO>() {
+    var TAG = ChannelImItemAdapter::class.java.getSimpleName()
+    var imCallBack: CallBack? = null
+
+    /**
+     * 数据提供者
+     */
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    /**
+     * Item坐标
+     */
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    /**
+     * 获取Item总数
+     */
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    /**
+     * 创建LayoutHelper
+     */
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        return LinearLayoutHelper(0, data.size)
+    }
+
+    /**
+     * 创建ViewHolder
+     */
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_channel_im_records_item)
+    }
+
+    /**
+     * 绑定数据
+     */
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterChannelImRecordsItemBinding>, position: Int) {
+        holder.bind { binding ->
+            val itemData = getItem(position)
+
+            binding.imCallTimeTv.text = TimeTransition().stampToTime(itemData.sendTime*1000)
+            binding.imSickbedTv.text = itemData.senderMemberName
+            if (itemData.is_self) {
+                binding.imTreatmentStateImagev.visibility = View.GONE
+            } else {
+                binding.imTreatmentStateImagev.visibility = View.VISIBLE
+            }
+
+            if (itemData.readed) {
+                binding.imTreatmentStateImagev.setImageResource(R.drawable.checked_100)
+            } else {
+                binding.imTreatmentStateImagev.setImageResource(R.drawable.unchecked_100)
+            }
+
+
+            binding.imPlayTv.setOnClickListener {
+                showMessage("play:" + itemData.audioPath)
+                EventBus.getDefault().post(MessageEvent(false, Constants.EVENT_CLEAR_IM))
+                MediaPlayHelper.getInstance().stopMusic(false)
+                MediaPlayHelper.getInstance().playUrlMusic(ApiManager.urlManager.device_url + itemData.audioPath, 1f, false)
+                ImPlayDialogHelper.showImPlayDialog(imActivity, itemData.senderMemberName, TimeTransition().stampToTime(itemData.sendTime*1000))
+
+                //如果是未读留言则回复已读tcp
+                if (!itemData.readed) {
+                    val imTcpModel = ChannelImUtil.channelImRead(Constants.deviceId, itemData as ChannelImDO)
+                    NettyClient.instance.sendMsg(imTcpModel.toJson()).subscribe {
+                        if (it) {
+                            Log.d(TAG, "TCP.发送消息完成")
+                            //EventBus.getDefault().post(MessageEvent("im_update", Constants.EVENT_CHANNEL_IM_UPDATE))
+                            if (imCallBack != null) {
+                                imCallBack!!.onChannelImRead(itemData, position)
+                            }
+                        } else {
+                            Log.e(TAG, "TCP.发送消息失败")
+                            HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    fun setCallBack(callback: CallBack) {
+        imCallBack = callback
+    }
+
+    interface CallBack {
+        fun onChannelImRead(item: ChannelIMVO, pos: Int)
+    }
+}

+ 45 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/ContactUpdateAdapter.kt

@@ -0,0 +1,45 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+import android.content.Context
+import android.view.ViewGroup
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.layout.LinearLayoutHelper
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.databinding.UpdateContactItemBinding
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+
+class ContactUpdateAdapter(val data: ArrayList<String>, val context: Context):
+    BaseDelegateAdapter<BaseRecyclerViewHolder<UpdateContactItemBinding>, String>() {
+
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    override fun onCreateViewHolder(
+        p0: ViewGroup,
+        p1: Int
+    ): BaseRecyclerViewHolder<UpdateContactItemBinding> {
+        return BaseRecyclerViewHolder.build(p0, R.layout.update_contact_item)
+    }
+
+    override fun onBindViewHolder(p0: BaseRecyclerViewHolder<UpdateContactItemBinding>, p1: Int) {
+        p0.bind {
+            binding ->
+            binding.tvContactItem.text = data[p1]
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        return LinearLayoutHelper(0, data.size)
+    }
+
+}

+ 230 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/NewEventItemAdapter.kt

@@ -0,0 +1,230 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+import android.app.Activity
+import android.content.Intent
+import android.graphics.Color
+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.enation.javashop.utils.base.tool.CommonTool
+import com.enation.javashop.utils.base.widget.LoadingDialog
+import com.google.gson.Gson
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.activity.VoiceMsgActivity
+import com.wdkl.ncs.android.component.home.databinding.EventListItemBinding
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect
+import com.wdkl.ncs.android.component.home.util.ImPlayDialogHelper
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.component.home.util.TimeTransition
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.api.ApiManager
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.tcp.channel.*
+import org.greenrobot.eventbus.EventBus
+
+
+class NewEventItemAdapter(var data:ArrayList<InteractionVO>, val activity: Activity) : BaseDelegateAdapter<BaseRecyclerViewHolder<EventListItemBinding>, InteractionVO>() {
+    val TAG = NewEventItemAdapter::class.simpleName
+
+    private lateinit var loadingDialog: LoadingDialog
+
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<EventListItemBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.event_list_item)
+    }
+
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        loadingDialog = CommonTool.createLoadingDialog(activity, R.layout.custom_loading, R.id.loadding_image)
+        return LinearLayoutHelper(0,data.size)
+    }
+
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<EventListItemBinding>, position: Int) {
+        holder?.bind { binding ->
+            val itemData = getItem(position)
+
+            var toDeviceId:Int?
+            binding.eliEventTime.text = TimeTransition().stampToDate(itemData.createDate*1000)
+            //binding.eliRoomName.text = itemData.fromFrameFullName
+            var showName: String? = ""
+            if (itemData.fromDeviceMemberId == Constants.memberId){
+                binding.eliRoomName.text = itemData.toFrameFullName
+                showName = itemData.toMemberName
+                toDeviceId = itemData.toDeviceId
+            } else {
+                binding.eliRoomName.text = itemData.fromFrameFullName
+                showName = itemData.fromMemberName
+                toDeviceId = itemData.fromDeviceId
+            }
+
+            if (TextUtils.isEmpty(showName)) {
+                binding.eliMemberName.setText(R.string.str_null)
+            } else {
+                binding.eliMemberName.text = showName
+            }
+
+            if (toDeviceId != null) {
+                binding.eliVoiceRecorder.visibility = View.VISIBLE
+            } else {
+                binding.eliVoiceRecorder.visibility = View.GONE
+            }
+            binding.eliVoiceRecorder.setOnClickListener{
+                var intent = Intent(activity,VoiceMsgActivity::class.java)
+                intent.putExtra(VoiceMsgActivity.TO_DEVICE_ID,toDeviceId)
+                activity.startActivity(intent)
+            }
+
+            when (itemData.actionType){
+                TcpType.EVENT.name->{
+                    binding.eliImRead.visibility = View.GONE
+                    binding.eliEventResponse.visibility = View.VISIBLE
+                    binding.eliEventName.text = itemData.data
+                    if (itemData.actionEnd!=null){
+                        binding.eliEventResponse.visibility = View.GONE
+                        binding.eliEventName.setTextColor(Color.GREEN)
+                    } else {
+                        binding.eliEventName.setTextColor(activity.resources.getColor(R.color.warn_orange))
+                        binding.eliEventResponse.setOnClickListener {
+                            loadingDialog.show()
+                            val tcpModel = EventUtil.eventResponse(Constants.deviceId, itemData.fromDeviceId, itemData.id)
+                            NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe {
+                                if (it) {
+                                    Log.d(TAG, "TCP.发送消息完成")
+                                } else {
+                                    Log.e(TAG, "TCP.发送消息失败")
+                                    HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                }
+                                loadingDialog.dismiss()
+                            }
+                        }
+                    }
+                }
+                TcpType.IM.name->{
+                    binding.eliEventName.setText(R.string.event_voice_msg)
+                    binding.eliEventResponse.visibility = View.GONE
+                    if (itemData.actionEnd!=null){
+                        binding.eliEventResponse.visibility = View.GONE
+                        binding.eliImRead.visibility = View.VISIBLE
+                        binding.eliEventName.setTextColor(Color.GREEN)
+
+                        binding.eliImRead.setOnClickListener {
+                            showMessage("play:" + itemData.data)
+                            MediaPlayHelper.getInstance().stopMusic(false)
+                            MediaPlayHelper.getInstance().playUrlMusic(ApiManager.urlManager.device_url + itemData.data, 1f, false)
+                            ImPlayDialogHelper.showImPlayDialog(activity, itemData.fromFrameFullName, TimeTransition().stampToTime(itemData.createDate*1000))
+                        }
+                    } else {
+                        binding.eliEventName.setTextColor(activity.resources.getColor(R.color.warn_orange))
+                        binding.eliImRead.visibility = View.VISIBLE
+
+                        binding.eliImRead.setOnClickListener {
+                            showMessage("play:" + itemData.data)
+                            MediaPlayHelper.getInstance().stopMusic(false)
+                            MediaPlayHelper.getInstance().playUrlMusic(ApiManager.urlManager.device_url + itemData.data, 1f, false)
+                            ImPlayDialogHelper.showImPlayDialog(activity, itemData.fromFrameFullName, TimeTransition().stampToTime(itemData.createDate*1000))
+                            //如果是自己发的留言则不发送已读tcp
+                            if (Constants.memberId != itemData.fromDeviceMemberId) {
+                                val tcpModel = ImUtil.imRead(Constants.deviceId, itemData.fromDeviceId, itemData.id)
+                                NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe {
+                                    if (it) {
+                                        Log.d(TAG, "TCP.发送消息完成")
+                                    } else {
+                                        Log.e(TAG, "TCP.发送消息失败")
+                                        HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                TcpType.SOS.name->{
+                    binding.eliEventName.setTextColor(activity.resources.getColor(R.color.warn_orange))
+                    binding.eliImRead.visibility = View.GONE
+                    binding.eliEventResponse.visibility = View.VISIBLE
+                    binding.eliEventName.text = "SOS"
+//                    binding.eliEventName.setBackgroundResource(R.drawable.sos_96)
+                    if (itemData.actionEnd!=null){
+                        binding.eliEventResponse.visibility = View.GONE
+                        binding.eliEventName.setTextColor(Color.GREEN)
+                    } else {
+                        binding.eliEventResponse.setOnClickListener {
+                            loadingDialog.show()
+                            val tcpModel = OtherUtil.SOSCancel(Constants.deviceId, itemData.fromDeviceId, itemData.id)
+                            NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe {
+                                if (it) {
+                                    Log.d(TAG, "TCP.发送消息完成")
+                                    tcpModel.data = Gson().toJson(itemData)
+                                    EventBus.getDefault().post(MessageEvent(tcpModel, 999))
+                                } else {
+                                    Log.e(TAG, "TCP.发送消息失败")
+
+                                    HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                }
+                                loadingDialog.dismiss()
+                            }
+                        }
+                    }
+                }
+                TcpType.VOICE.name->{
+                    binding.eliEventName.setText(R.string.event_voice_call)
+                    binding.eliEventResponse.visibility = View.GONE
+                }
+                TcpType.PHONE.name->{
+                    binding.eliEventName.setText(R.string.event_voice_call)
+                    binding.eliEventResponse.visibility = View.GONE
+                }
+            }
+
+            binding.eliCallout.setOnClickListener {
+                if (itemData.fromDeviceMemberId == Constants.memberId){
+                    DeviceChannel.calling = true
+
+                    val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.toDeviceId)
+                    val intent = Intent(activity, CallSingleActivity::class.java)
+                    intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
+                    intent.putExtra(CallSingleActivity.EXTRA_MO, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_TCPMODEL, tcpModel);
+                    intent.putExtra(CallSingleActivity.EXTRA_SHOWNAME, itemData.fromFrameFullName + " "+showName)
+                    //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    activity.startActivity(intent)
+
+                } else {
+                    DeviceChannel.calling = true
+                    val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.fromDeviceId)
+
+                    val intent = Intent(activity, CallSingleActivity::class.java)
+                    intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
+                    intent.putExtra(CallSingleActivity.EXTRA_MO, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_TCPMODEL, tcpModel);
+                    intent.putExtra(CallSingleActivity.EXTRA_SHOWNAME, itemData.fromFrameFullName + " "+showName)
+                    //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    activity.startActivity(intent)
+
+                }
+            }
+        }
+    }
+
+}

+ 56 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/NumAdapter.java

@@ -0,0 +1,56 @@
+package com.wdkl.ncs.android.component.home.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import com.wdkl.ncs.android.component.home.R;
+
+public class NumAdapter extends BaseAdapter {
+    private String[] num;
+    private Context context;
+
+    public NumAdapter(String[] numbers, Context context) {
+        this.num = numbers;
+        this.context = context;
+    }
+
+
+    @Override
+    public int getCount() {
+        return num.length;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return num[position];
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewHolder viewHolder;
+        if (convertView == null) {
+            convertView = LayoutInflater.from(context).inflate(R.layout.digital_item, null);
+            viewHolder = new ViewHolder();
+            viewHolder.numTv = convertView.findViewById(R.id.tv_number);
+            convertView.setTag(viewHolder);
+        } else {
+            viewHolder = (ViewHolder) convertView.getTag();
+        }
+        viewHolder.numTv.setText(num[position]);
+
+        return convertView;
+    }
+
+    static class ViewHolder {
+        TextView numTv;
+    }
+}

+ 155 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemAdapter.kt

@@ -0,0 +1,155 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+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.enation.javashop.utils.base.tool.CommonTool
+import com.enation.javashop.utils.base.widget.LoadingDialog
+import com.google.common.base.Strings
+import com.google.gson.JsonObject
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.activity.VoiceMsgActivity
+import com.wdkl.ncs.android.component.home.databinding.TakeoverItemBinding
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.base.BaseApplication
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.PhoneUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.enums.CommunicationEnum
+import com.wdkl.ncs.android.middleware.utils.StringUtil
+
+class TakeoverItemAdapter(var data:ArrayList<JsonObject>, val context: Context) : BaseDelegateAdapter<BaseRecyclerViewHolder<TakeoverItemBinding>, JsonObject>(){
+    val TAG = "TakeoverItemAdapter"
+
+    private lateinit var loadingDialog: LoadingDialog
+
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        Log.i(TAG,data.toString())
+        loadingDialog = CommonTool.createLoadingDialog(context, R.layout.custom_loading,R.id.loadding_image)
+        return LinearLayoutHelper(0,data.size)
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<TakeoverItemBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.takeover_item)
+    }
+
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<TakeoverItemBinding>, position: Int) {
+        holder.bind {
+            binding ->
+            val itemData = getItem(position)
+            binding.clerkName.setText(R.string.str_null)
+            binding.tvPhoneNumber.setText(R.string.str_empty)
+            try {
+                binding.clerkName.text = itemData.get("clerk_name").asString
+                binding.tvPhoneNumber.text = itemData.get("phone_number").asString
+            } catch (e: Exception) {
+                //
+            }
+
+            if (Strings.isNullOrEmpty(Constants.userName)){
+                binding.btnCallOut.visibility = View.GONE
+                binding.btnUserChange.setOnClickListener {
+                    CommonTool.createVerifyDialog(
+                        StringUtil.getResString(R.string.device_change_check),
+                        StringUtil.getResString(R.string.str_cancel),
+                        StringUtil.getResString(R.string.str_confirm),
+                        context,object : CommonTool.DialogInterface{
+                            override fun yes() {
+                                showMessage(R.string.device_change_tips)
+                                var tcpModel = DeviceUtil.deviceChange(Constants.deviceId, itemData.get("id").asInt)
+                                NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe {
+                                    if (it) {
+                                        Log.d(TAG, "TCP.发送消息完成")
+                                    } else {
+                                        Log.e(TAG, "TCP.发送消息失败")
+                                        HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                    }
+                                }
+                            }
+                            override fun no() {
+                            }
+                        }).show()
+                }
+            } else {
+                binding.btnUserChange.visibility = View.GONE
+                binding.btnCallOut.setOnClickListener {
+                    //DeviceChannel.calling = true
+
+                    if (Constants.phoneType == CommunicationEnum.MOBILE_PHONE.value()) {
+                        try {
+                            val number = itemData.get("phone_number")
+                            val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:" + number.asString))
+                            context.startActivity(intent)
+                            //Constants.phoneState = Constants.PHONE_OUTGOING
+
+                            if (!Constants.uploadCalllog) {
+                                //发送tcp
+                                val tcpModel = PhoneUtil.phoneCall(Constants.deviceId, itemData.get("id").asInt)
+                                NettyClient.instance.sendMsg(tcpModel.toJson())
+                                    .subscribe { res: Boolean ->
+                                        if (res) {
+                                            Log.d(TAG, "TCP.发送消息完成")
+                                        } else {
+                                            Log.e(TAG, "TCP.发送消息失败")
+                                            HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                        }
+                                    }
+                            }
+                        } catch (e: Exception) {
+                            showMessage(R.string.call_phone_failed)
+                        }
+
+                        //直接呼叫
+                        //val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:10010"))
+                        //先启动dialer然后手动呼叫
+                        //val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:10010"))
+                        //context.startActivity(intent)
+                    } else {
+                        //网络电话
+                        val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.get("id").asInt)
+                        val intent = Intent(context, CallSingleActivity::class.java)
+                        intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
+                        intent.putExtra(CallSingleActivity.EXTRA_MO, true)
+                        intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
+                        intent.putExtra(CallSingleActivity.EXTRA_TCPMODEL, tcpModel);
+                        intent.putExtra(CallSingleActivity.EXTRA_SHOWNAME, itemData.get("clerk_name").asString)
+                        //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        context.startActivity(intent)
+                    }
+                }
+
+                binding.tiVoiceRecorder.visibility = View.VISIBLE
+                binding.tiVoiceRecorder.setOnClickListener{
+                    var intent = Intent(context, VoiceMsgActivity::class.java)
+                    intent.putExtra(VoiceMsgActivity.TO_DEVICE_ID,itemData.get("id").asInt)
+                    context.startActivity(intent)
+                }
+            }
+        }
+    }
+}

+ 152 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/TakeoverItemSearchAdapter.kt

@@ -0,0 +1,152 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.enation.javashop.utils.base.tool.CommonTool
+import com.google.common.base.Strings
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.activity.VoiceMsgActivity
+import com.wdkl.ncs.android.component.home.entity.ContactItemEntity
+import com.wdkl.ncs.android.component.home.search.FuzzySearchBaseAdapter
+import com.wdkl.ncs.android.component.home.search.IFuzzySearchRule
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.PhoneUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.enums.CommunicationEnum
+import com.wdkl.ncs.android.middleware.utils.StringUtil
+
+class TakeoverItemSearchAdapter : FuzzySearchBaseAdapter<ContactItemEntity, TakeoverItemSearchAdapter.ItemHolder> {
+    val TAG = "TakeoverItem"
+
+    lateinit var context: Context
+
+    constructor() : super(null)
+
+    constructor(rule: IFuzzySearchRule) : super(rule)
+
+    constructor(data: ArrayList<ContactItemEntity>) : super(null, data)
+
+    constructor(rule: IFuzzySearchRule?, data: ArrayList<ContactItemEntity>, context: Context) : super(rule, data) {
+        this.context = context
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
+        val view = LayoutInflater.from(parent.getContext()).inflate(R.layout.takeover_item, parent, false)
+        return ItemHolder(view)
+    }
+
+    override fun onBindViewHolder(p0: ItemHolder, p1: Int) {
+        val itemData = mDataList[p1]
+        p0.clerkName.setText(R.string.str_null)
+        p0.phoneNumber.setText(R.string.str_empty)
+        try {
+            p0.clerkName.text = itemData.value
+            p0.phoneNumber.text = itemData.number
+        } catch (e: Exception) {
+            //
+        }
+
+        if (Strings.isNullOrEmpty(Constants.userName)){
+            p0.btnCallOut.visibility = View.GONE
+            p0.btnUserChange.setOnClickListener {
+                CommonTool.createVerifyDialog(
+                    StringUtil.getResString(R.string.device_change_check),
+                    StringUtil.getResString(R.string.str_cancel),
+                    StringUtil.getResString(R.string.str_confirm),
+                    context,object : CommonTool.DialogInterface{
+                        override fun yes() {
+                            showMessage(R.string.device_change_tips)
+                            val tcpModel = DeviceUtil.deviceChange(Constants.deviceId, itemData.id)
+                            NettyClient.instance.sendMsg(tcpModel.toJson()).subscribe {
+                                if (it) {
+                                    Log.d(TAG, "TCP.发送消息完成")
+                                } else {
+                                    Log.e(TAG, "TCP.发送消息失败")
+                                    HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                }
+                            }
+                        }
+                        override fun no() {
+                        }
+                    }).show()
+            }
+        } else {
+            p0.btnUserChange.visibility = View.GONE
+            p0.btnCallOut.setOnClickListener {
+                //DeviceChannel.calling = true
+
+                if (Constants.phoneType == CommunicationEnum.MOBILE_PHONE.value()) {
+                    try {
+                        val number = itemData.number
+                        val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:$number"))
+                        context.startActivity(intent)
+                        //Constants.phoneState = Constants.PHONE_OUTGOING
+
+                        if (!Constants.uploadCalllog) {
+                            //发送tcp
+                            val tcpModel = PhoneUtil.phoneCall(Constants.deviceId, itemData.id)
+                            NettyClient.instance.sendMsg(tcpModel.toJson())
+                                .subscribe { res: Boolean ->
+                                    if (res) {
+                                        Log.d(TAG, "TCP.发送消息完成")
+                                    } else {
+                                        Log.e(TAG, "TCP.发送消息失败")
+                                        HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                    }
+                                }
+                        }
+                    } catch (e: Exception) {
+                        showMessage(R.string.call_phone_failed)
+                    }
+
+                    //直接呼叫
+                    //val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:10010"))
+                    //先启动dialer然后手动呼叫
+                    //val intent = Intent(Intent.ACTION_DIAL, Uri.parse("tel:10010"))
+                    //context.startActivity(intent)
+                } else {
+                    //网络电话
+                    val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.id)
+                    val intent = Intent(context, CallSingleActivity::class.java)
+                    intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
+                    intent.putExtra(CallSingleActivity.EXTRA_MO, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_TCPMODEL, tcpModel);
+                    intent.putExtra(CallSingleActivity.EXTRA_SHOWNAME, itemData.value)
+                    //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    context.startActivity(intent)
+                }
+            }
+
+            p0.tiVoiceRecorder.visibility = View.VISIBLE
+            p0.tiVoiceRecorder.setOnClickListener{
+                val intent = Intent(context, VoiceMsgActivity::class.java)
+                intent.putExtra(VoiceMsgActivity.TO_DEVICE_ID,itemData.id)
+                context.startActivity(intent)
+            }
+        }
+    }
+
+
+    class ItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+        var clerkName: TextView = itemView.findViewById(R.id.clerk_name)
+        var phoneNumber: TextView = itemView.findViewById(R.id.tv_phone_number)
+        var btnUserChange: Button = itemView.findViewById(R.id.btn_user_change)
+        var btnCallOut: Button = itemView.findViewById(R.id.btn_call_out)
+        var tiVoiceRecorder: ImageView = itemView.findViewById(R.id.ti_voice_recorder)
+    }
+}

+ 227 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/WatchCallRecordsItemAdapter.kt

@@ -0,0 +1,227 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+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.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.databinding.AdapterWatchCallRecordsItemBinding
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.component.home.util.TimeTransition
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.ncs.android.middleware.utils.StringUtil
+
+/**
+ * 呼叫记录适配器
+ */
+class WatchCallRecordsItemAdapter(val data: ArrayList<InteractionVO>) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding>, InteractionVO>() {
+    var TAG = WatchCallRecordsItemAdapter::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
+    }
+
+    /**
+     * 创建LayoutHelper
+     */
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        Log.i("abc", data.toString());
+        return LinearLayoutHelper(0, data.size)
+    }
+
+    /**
+     * 创建ViewHolder
+     */
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_watch_call_records_item)
+    }
+
+    /**
+     * 绑定数据
+     */
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterWatchCallRecordsItemBinding>, position: Int) {
+        holder.bind { binding ->
+            val itemData = getItem(position)
+
+            if (itemData.createDate != null) {
+                binding.callTimeTv.text = TimeTransition().stampToDate(itemData.createDate*1000)
+            }
+
+            //是否已播放 已响应
+            if (itemData.actionEnd != null) {
+                binding.treatmentStateImagev.setImageResource(R.drawable.checked_100)
+                binding.treatmentStateImagev.visibility = View.VISIBLE
+                binding.processingTimeTv.visibility = View.VISIBLE
+
+                //判断是呼入还是呼出 1 分机到主机 2主机到分机
+                if (itemData.fromDeviceMemberId == Constants.memberId){
+                    binding.sickbedTv.text = itemData.toFrameFullName
+                    if (TextUtils.isEmpty(itemData.toMemberName)) {
+                        binding.nameTv.text = "To: " + StringUtil.getResString(R.string.str_null)
+                    } else {
+                        binding.nameTv.text = "To: " + itemData.toMemberName
+                    }
+                    binding.callStatusImagev.setImageResource(R.drawable.hu_chu_yi_jie)
+                } else {
+                    binding.sickbedTv.text = itemData.fromFrameFullName
+                    if (TextUtils.isEmpty(itemData.fromMemberName)) {
+                        binding.nameTv.text = "From: " + StringUtil.getResString(R.string.str_null)
+                    } else {
+                        binding.nameTv.text = "From: " + itemData.fromMemberName
+                    }
+                    binding.callStatusImagev.setImageResource(R.drawable.hu_ru_yi_jie)
+                }
+
+                if(itemData.actionType == TcpType.SOS.name){ //sos紧急呼叫 已处理
+                    binding.playTv.visibility = View.GONE
+                    binding.projectTv.visibility = View.VISIBLE
+                    binding.conductorNameTv.visibility = View.VISIBLE
+                    binding.projectTv.text = "SOS"
+                    binding.conductorNameTv.text = itemData.toMemberName
+                    binding.processingTimeTv.text = TimeTransition().stampToDate(itemData.actionEnd*1000)
+                    binding.projectTv.setBackgroundResource(R.drawable.sp_event_do)
+                    binding.callStatusImagev.setImageResource(R.drawable.sos_96)
+                }else 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 = TimeTransition().stampToDate(itemData.actionEnd*1000)
+                    binding.projectTv.setBackgroundResource(R.drawable.sp_event_do)
+                    binding.callStatusImagev.setImageResource(R.drawable.event_do)
+                }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.drawable.yi_bo_fang)
+                    //检查是否自己发的留言
+                    /*if (itemData.fromDeviceMemberId == Constants.memberId) {
+                        binding.processingTimeTv.text = "对方已播放"
+                    } else {
+                        binding.processingTimeTv.text = "已播放"
+                    }*/
+                    binding.processingTimeTv.visibility = View.GONE
+
+                }else if(itemData.actionType == TcpType.VOICE.name || itemData.actionType == TcpType.PHONE.name){ //语音呼叫已接听
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.GONE
+                    binding.treatmentStateImagev.visibility = View.GONE
+                    binding.conductorNameTv.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.conductorNameTv.visibility = View.GONE
+                    binding.processingTimeTv.visibility = View.GONE
+                }
+
+            } else {
+                binding.treatmentStateImagev.setImageResource(R.drawable.unchecked_100)
+                binding.treatmentStateImagev.visibility = View.VISIBLE
+                binding.conductorNameTv.visibility = View.GONE
+                binding.processingTimeTv.visibility = View.VISIBLE
+
+                //判断是呼入还是呼出 1 分机到主机 2主机到分机
+                if(Constants.memberId != itemData.fromDeviceMemberId){
+                    binding.sickbedTv.text = itemData.fromFrameFullName
+                    if (TextUtils.isEmpty(itemData.fromMemberName)) {
+                        binding.nameTv.text = "From: " + StringUtil.getResString(R.string.str_null)
+                    } else {
+                        binding.nameTv.text = "From: " + itemData.fromMemberName
+                    }
+                    binding.callStatusImagev.setImageResource(R.drawable.hu_ru_wei_jie)
+                }else{
+                    binding.sickbedTv.text = itemData.toFrameFullName
+                    if (TextUtils.isEmpty(itemData.toMemberName)) {
+                        binding.nameTv.text = "To: " + StringUtil.getResString(R.string.str_null)
+                    } else {
+                        binding.nameTv.text = "To: " + itemData.toMemberName
+                    }
+                    binding.callStatusImagev.setImageResource(R.drawable.hu_chu_wei_jie)
+                }
+
+                if(itemData.actionType == TcpType.SOS.name){ //sos紧急呼叫 未处理
+                    binding.playTv.visibility = View.GONE
+                    binding.projectTv.visibility = View.VISIBLE
+                    binding.projectTv.text = "SOS"
+                    binding.projectTv.setBackgroundResource(R.drawable.sp_event_undo)
+                    binding.processingTimeTv.setText(R.string.event_undo)
+                    binding.callStatusImagev.setImageResource(R.drawable.sos_96)
+                }else 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.drawable.sp_event_undo)
+                    binding.callStatusImagev.setImageResource(R.drawable.event_undo)
+                    binding.processingTimeTv.setText(R.string.event_undo)
+                }else if (itemData.actionType == TcpType.IM.name){//语音未播放
+                    binding.projectTv.visibility = View.GONE
+                    binding.playTv.visibility = View.VISIBLE
+                    binding.playTv.setBackgroundResource(R.drawable.wei_bo_fang)
+                    //检查是否自己发的留言
+                    /*if (itemData.fromDeviceMemberId == Constants.memberId) {
+                        binding.processingTimeTv.text = "对方未播放"
+                    } else {
+                        binding.processingTimeTv.text = "未播放"
+                    }*/
+                    binding.processingTimeTv.visibility = View.GONE
+                }else if(itemData.actionType == TcpType.VOICE.name || itemData.actionType == TcpType.PHONE.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
+
+                }
+            }
+
+            if(itemData.actionType == TcpType.VOICE.name || itemData.actionType == TcpType.PHONE.name) {
+                if (itemData.actionAccept != null) {
+                    if (itemData.fromDeviceMemberId == Constants.memberId){
+                        binding.callStatusImagev.setImageResource(R.drawable.hu_chu_yi_jie)
+                    } else {
+                        binding.callStatusImagev.setImageResource(R.drawable.hu_ru_yi_jie)
+                    }
+                } else {
+                    if(Constants.memberId != itemData.fromDeviceMemberId){
+                        binding.callStatusImagev.setImageResource(R.drawable.hu_ru_wei_jie)
+                    }else{
+                        binding.callStatusImagev.setImageResource(R.drawable.hu_chu_wei_jie)
+                    }
+                }
+            }
+        }
+    }
+
+}

+ 121 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/adapter/WatchContactsItemAdapter.kt

@@ -0,0 +1,121 @@
+package com.wdkl.ncs.android.component.home.adapter
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.util.Log
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.alibaba.android.vlayout.LayoutHelper
+import com.alibaba.android.vlayout.layout.LinearLayoutHelper
+import com.enation.javashop.utils.base.tool.CommonTool
+import com.enation.javashop.utils.base.widget.LoadingDialog
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.activity.VoiceMsgActivity
+import com.wdkl.ncs.android.component.home.databinding.AdapterWatchContactsItemBinding
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.adapter.BaseDelegateAdapter
+import com.wdkl.ncs.android.lib.utils.BaseRecyclerViewHolder
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactsVO
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+import com.wdkl.ncs.android.middleware.tcp.channel.PhoneUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.enums.CommunicationEnum
+
+class WatchContactsItemAdapter(val data:ArrayList<WatchContactsVO>, val context: Context) : BaseDelegateAdapter<BaseRecyclerViewHolder<AdapterWatchContactsItemBinding>, WatchContactsVO>(){
+
+    val TAG = WatchContactsItemAdapter::class.java.simpleName
+
+    private var thisPosition: Int = -1
+    private lateinit var loadingDialog: LoadingDialog
+
+    override fun dataProvider(): Any {
+        return data
+    }
+
+    override fun itemFilter(position: Int): Boolean {
+        return true
+    }
+    fun setThisPosition(thisPosition:Int) {
+        this.thisPosition = thisPosition
+    }
+    override fun getItemCount(): Int {
+        return data.size
+    }
+
+    override fun onCreateLayoutHelper(): LayoutHelper {
+        Log.i("abc",data.toString())
+        loadingDialog = CommonTool.createLoadingDialog(context, R.layout.custom_loading,R.id.loadding_image)
+        return LinearLayoutHelper(0,data.size)
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseRecyclerViewHolder<AdapterWatchContactsItemBinding> {
+        return BaseRecyclerViewHolder.build(parent, R.layout.adapter_watch_contacts_item)
+    }
+
+    override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
+        super.onDetachedFromRecyclerView(recyclerView)
+        loadingDialog.dismiss()
+    }
+
+    override fun onBindViewHolder(holder: BaseRecyclerViewHolder<AdapterWatchContactsItemBinding>, position: Int) {
+        holder.bind {
+            binding ->
+            val itemData = getItem(position)
+            binding.roomNumberTv.text = itemData.frameFullName
+            binding.nameTv.text = itemData.customerNamed
+
+            binding.awciVoiceRecorder.setOnClickListener{
+                var intent = Intent(context, VoiceMsgActivity::class.java)
+                intent.putExtra(VoiceMsgActivity.TO_DEVICE_ID,itemData.deviceId)
+                context.startActivity(intent)
+            }
+
+            binding.awciBtnCallOut.setOnClickListener{
+                if (Constants.bedPhoneType == CommunicationEnum.MOBILE_PHONE.value()) {
+                    try {
+                        if (itemData.phoneNumber != null) {
+                            val intent = Intent(Intent.ACTION_CALL, Uri.parse("tel:" + itemData.phoneNumber))
+                            context.startActivity(intent)
+
+                            if (!Constants.uploadCalllog) {
+                                //发送tcp
+                                val tcpModel = PhoneUtil.phoneCall(Constants.deviceId, itemData.deviceId)
+                                NettyClient.instance.sendMsg(tcpModel.toJson())
+                                    .subscribe { res: Boolean ->
+                                        if (res) {
+                                            Log.d(TAG, "TCP.发送消息完成")
+                                        } else {
+                                            Log.e(TAG, "TCP.发送消息失败")
+                                            HandleTcpConnect.instance.tcpReConnectWithMsgShow()
+                                        }
+                                    }
+                            }
+                        } else {
+                            showMessage(R.string.call_phone_failed)
+                        }
+                    } catch (e: Exception) {
+                        showMessage(R.string.call_phone_failed)
+                    }
+                } else {
+                    DeviceChannel.calling = true
+                    //通话
+                    val tcpModel = VoiceUtil.voiceCall(Constants.deviceId, itemData.deviceId)
+                    val intent = Intent(context, CallSingleActivity::class.java)
+                    intent.putExtra(CallSingleActivity.EXTRA_ROOM_ID, Constants.sipId)
+                    intent.putExtra(CallSingleActivity.EXTRA_MO, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_AUDIO_ONLY, true)
+                    intent.putExtra(CallSingleActivity.EXTRA_TCPMODEL, tcpModel)
+                    intent.putExtra(CallSingleActivity.EXTRA_SHOWNAME, itemData.frameFullName + " " + itemData.customerNamed)
+                    //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    context.startActivity(intent)
+                }
+
+            }
+        }
+    }
+}

+ 36 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/binding/HomeFragmentBindHelper.kt

@@ -0,0 +1,36 @@
+package com.wdkl.ncs.android.component.home.binding
+
+import androidx.databinding.ObservableField
+
+
+/**
+ * @author LDD
+ * @From   com.wdkl.ncs.android.component.home.binding
+ * @Date   2018/1/30 下午3:03
+ * @Note   首页UIVM
+ */
+class HomeFragmentBindHelper {
+
+    /**
+     * @Name  scrollY
+     * @Type  ObservableField<Int>
+     * @Note  滑动距离
+     */
+    val scrollY = ObservableField(0)
+
+    /**
+     * @Name  image
+     * @Type  String
+     * @Note  测试图片
+     */
+    var image = "http://onghqryqs.bkt.clouddn.com/ChMkJlauzbOIb6JqABF4o12gc_AAAH9HgF1sh0AEXi7441.jpeg"
+
+    /**
+     * @Name  isHide
+     * @Type  ObservableField<Boolean>
+     * @Note  是否隐藏
+     */
+    val isHide  = ObservableField(true)
+
+
+}

+ 53 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/broadcast/BatteryBroadcastReceiver.java

@@ -0,0 +1,53 @@
+package com.wdkl.ncs.android.component.home.broadcast;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.TextView;
+
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.lib.vo.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.util.Locale;
+
+
+public class BatteryBroadcastReceiver extends BroadcastReceiver {
+    private String TAG = BatteryBroadcastReceiver.class.getSimpleName();
+
+    private TextView mBatteryView;
+
+    public BatteryBroadcastReceiver(TextView mBatteryView) {
+        this.mBatteryView = mBatteryView;
+    }
+
+    @SuppressLint("LongLogTag")
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+            int level = intent.getIntExtra("level", 0);
+            int scale = intent.getIntExtra("scale", 100);
+            int power = level * 100 / scale;
+            if (power<15){
+                if (Constants.Companion.getEVENT_BATTERY_ALARM()) {
+                    String language = LocaleMangerUtils.getApplicationLocale().getLanguage();
+                    if (Locale.CHINESE.getLanguage().equals(language)) {
+                        SpeechUtil.getInstance().startSpeak("电池电量低,请注意!电池电量低,请注意!电池电量低,请注意!");
+                    } else {
+                        RingPlayHelper.playRingTone(context, R.raw.low_battery, false);
+                    }
+                    Constants.Companion.setEVENT_BATTERY_ALARM(false);
+                }
+            } else {
+                Constants.Companion.setEVENT_BATTERY_ALARM(true);
+            }
+            EventBus.getDefault().post(new MessageEvent(power, Constants.EVENT_BATTERY_PERCENT));
+        }
+    }
+}

+ 34 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/broadcast/MyMediaButtonReceiver.kt

@@ -0,0 +1,34 @@
+package com.wdkl.ncs.android.component.home.broadcast
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.util.Log
+import android.view.KeyEvent
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import org.greenrobot.eventbus.EventBus
+
+class MyMediaButtonReceiver: BroadcastReceiver() {
+    var TAG = "MyMediaButtonReceiver"
+    override fun onReceive(context: Context?, intent: Intent?) {
+        val action = intent!!.action
+        if (action!=null&&action.equals(Intent.ACTION_MEDIA_BUTTON)){
+            var keyEvent = intent!!.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
+            if (keyEvent!=null && keyEvent.action == KeyEvent.ACTION_DOWN){
+                val keyCode = keyEvent.keyCode
+                Log.i(TAG, "keyCode " + keyCode)
+                when (keyCode){
+                    KeyEvent.KEYCODE_MEDIA_PLAY -> {
+                        EventBus.getDefault().post(MessageEvent(1, Constants.EVENT_BLUETOOTH_ACCEPT_CALL))
+                        return
+                    }
+                    KeyEvent.KEYCODE_MEDIA_PAUSE -> {
+                        EventBus.getDefault().post(MessageEvent(1, Constants.EVENT_BLUETOOTH_ACCEPT_CALL))
+                        return
+                    }
+                }
+            }
+        }
+    }
+}

+ 22 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/broadcast/NetworkBroadcastReceiver.kt

@@ -0,0 +1,22 @@
+package com.wdkl.ncs.android.component.home.broadcast
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import com.wdkl.ncs.android.component.home.util.NetHelper
+import com.wdkl.ncs.android.component.home.util.Util.getCpuWakeLock
+import com.wdkl.ncs.android.component.home.util.Util.wakeUpAndUnlock
+import com.wdkl.ncs.android.lib.utils.AppTool
+
+class NetworkBroadcastReceiver: BroadcastReceiver() {
+    override fun onReceive(context: Context?, intent: Intent?) {
+        if (ConnectivityManager.CONNECTIVITY_ACTION == intent!!.action) {
+            val netState = NetHelper.getInstance().getNetworkState(context)
+            if (netState == NetHelper.NETWORK_NONE) {
+                wakeUpAndUnlock()
+            }
+        }
+    }
+
+}

+ 36 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/di/HomeComponent.kt

@@ -0,0 +1,36 @@
+package com.wdkl.ncs.android.component.home.di
+
+import com.wdkl.ncs.android.component.home.activity.*
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService
+import com.wdkl.ncs.android.middleware.di.ApplicationComponent
+import dagger.Component
+
+/**
+ * @author LDD
+ * @Date   2018/1/22 下午2:43
+ * @From   com.wdkl.ncs.android.component.home.di
+ * @Note   依赖注入入口
+ */
+@Component(dependencies = arrayOf(ApplicationComponent::class))
+interface HomeComponent {
+
+    fun inject(activity: WatchHome2Activity)
+
+    fun inject(activity: AppUpdateActivity)
+
+    fun inject(activity: WatchContactsActivity)
+
+    fun inject(activity: WatchCallRecordsActivity)
+
+    fun inject(activity: TakeoverActivity)
+
+    fun inject(service: WdKeepAliveService)
+
+    fun inject(activity: NewEventListActivity)
+
+    fun inject(activity: VoiceMsgActivity)
+
+    fun inject(activity: ChannelImActivity)
+
+    fun inject(activity: ContactUpdateActivity)
+}

+ 50 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/entity/ContactItemEntity.java

@@ -0,0 +1,50 @@
+package com.wdkl.ncs.android.component.home.entity;
+
+import com.wdkl.ncs.android.component.home.search.IAZItem;
+import com.wdkl.ncs.android.component.home.search.IFuzzySearchItem;
+
+import java.util.List;
+
+public class ContactItemEntity implements IAZItem, IFuzzySearchItem {
+
+    private String       mValue;
+    private String       mNumber;
+    private int          mId;
+    private String       mSortLetters;
+    private List<String> mFuzzySearchKey;
+
+    public ContactItemEntity(String value, String number, int id, String sortLetters, List<String> fuzzySearchKey) {
+        mValue = value;
+        mNumber = number;
+        mId = id;
+        mSortLetters = sortLetters;
+        mFuzzySearchKey = fuzzySearchKey;
+    }
+
+    public String getValue() {
+        return mValue;
+    }
+
+    public String getNumber() {
+        return mNumber;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    @Override
+    public String getSortLetters() {
+        return mSortLetters;
+    }
+
+    @Override
+    public String getSourceKey() {
+        return mValue;
+    }
+
+    @Override
+    public List<String> getFuzzyKey() {
+        return mFuzzySearchKey;
+    }
+}

+ 244 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/helper/AppUpdateHelper.java

@@ -0,0 +1,244 @@
+package com.wdkl.ncs.android.component.home.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.util.Log;
+
+import androidx.core.content.FileProvider;
+
+import com.wdkl.ncs.android.middleware.utils.CommonUtils;
+
+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";
+    public static final String FILE_APK_PATH = CommonUtils.getRootFilePath() + "/CallingBed2";
+    /**
+     * 下载的APK文件的文件名
+     */
+    public static final String FILE_APK_NAME = "CallingBed2APK.apk";
+
+    public static Context mContext;
+
+    public static void updateApp(Context context, UpdateCallBack callBack) {
+        mContext = context;
+        if (checkApkExit(context)) {
+            Log.d(TAG, "文件存在");
+        } else {
+            Log.d(TAG, "文件不存在");
+            if (callBack != null) {
+                callBack.onFailed();
+            }
+            return;
+        }
+
+        String path = FILE_APK_PATH + "/" + FILE_APK_NAME;
+
+//        if (!checkApkAvailable(context, path)) {
+//            Log.d(TAG, "安装失败");
+//            return;
+//        }
+
+
+//        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();
+//            }
+//        }
+
+        if (installAPP(path)) {
+            Log.d(TAG, "安装成功");
+            if (callBack != null) {
+                callBack.onSuccess();
+            }
+        } else {
+            callBack.onFailed();
+        }
+    }
+
+    public static boolean installAPP(String path) {
+
+//        if (ShellUtils.checkRootPermission()){
+//            // 安装App命令:apkFilePath为待安装的软件地址
+//            String installCommand = "pm install -r " + path;
+//            // 重启密令: getPackageName:软件包名,WatchHome2Activity.class.getCanonicalName():重启APP的首页;
+//            String restartCommand = "am start -n "
+//                    + mContext.getPackageName() + "/" + WatchHome2Activity.class.getCanonicalName();
+//            String[] commands = {installCommand, restartCommand};
+//            ShellUtils.CommandResult commandResult = ShellUtils.execCommand(commands, true, true);
+//            return Strings.isNullOrEmpty(commandResult.errorMsg);
+//        } else {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            File apkFile = new File(path);
+            // 由于没有在Activity环境下启动Activity,设置下面的标签
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            //Android 7.0以上要使用FileProvider
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
+                Uri uri = FileProvider.getUriForFile(mContext, mContext.getPackageName() + ".provider", apkFile);
+                //添加这一句表示对目标应用临时授权该Uri所代表的文件
+//                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+                intent.setDataAndType(uri, "application/vnd.android.package-archive");
+            } else {
+                intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
+            }
+            mContext.startActivity(intent);
+            return true;
+//        }
+    }
+
+
+    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.i(TAG, "install package: " + packageName + ", apkPath: " + apkPath);
+        try {
+            process = new ProcessBuilder("pm", "install", "-i", packageName, "-r", apkPath).start();
+            successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
+            errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
+            String s;
+            while ((s = successResult.readLine()) != null) {
+                successMsg.append(s);
+            }
+            while ((s = errorResult.readLine()) != null) {
+                errorMsg.append(s);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (successResult != null) {
+                    successResult.close();
+                }
+                if (errorResult != null) {
+                    errorResult.close();
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            if (process != null) {
+                process.destroy();
+            }
+        }
+        Log.e(TAG, "" + errorMsg.toString());
+        //如果含有“success”认为安装成功
+        return successMsg.toString().equalsIgnoreCase("success");
+    }
+
+    public static boolean rootSilenceInstall(String path) {
+        Process process;
+        PrintWriter printWriter;
+        try {
+            process = Runtime.getRuntime().exec("su");
+            printWriter = new PrintWriter(process.getOutputStream());
+            printWriter.println("pm install -r " + path);
+            printWriter.flush();
+            printWriter.close();
+            int res = process.waitFor();
+            Log.e(TAG, "silent install res: " + res);
+            if (res == 0) {
+                return true;
+            } else {
+                return false;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "rootSilenceInstall e:" + e.getMessage());
+            return false;
+        }
+    }
+
+    public static void reboot(Context context) {
+        Intent intent = new Intent(Intent.ACTION_REBOOT);
+        intent.putExtra("nowait", 1);
+        intent.putExtra("interval", 1);
+        intent.putExtra("window", 0);
+        context.sendBroadcast(intent);
+    }
+
+    public interface UpdateCallBack {
+        void onFailed();
+
+        void onSuccess();
+    }
+}

+ 203 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/helper/HttpHelper.java

@@ -0,0 +1,203 @@
+package com.wdkl.ncs.android.component.home.helper;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+import static com.wdkl.ncs.android.component.home.helper.AppUpdateHelper.FILE_APK_NAME;
+import static com.wdkl.ncs.android.component.home.helper.AppUpdateHelper.FILE_APK_PATH;
+
+
+public class HttpHelper {
+    private static OkHttpClient okHttpClient;
+
+    public static void postData(String url, String jsonData, final UploadCallback callback) {
+        if (okHttpClient == null) {
+            okHttpClient = new OkHttpClient();
+        }
+
+        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonData);
+        Request request = new Request.Builder()
+                .url(url)
+                .post(requestBody)
+                .build();
+
+        okHttpClient.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("HttpHelper", "post response: " + response.toString());
+                if( response.code()==200) {
+                    if (callback != null) {
+                        callback.onSuccess("success");
+                    }
+                } else {
+                    if (callback != null) {
+                        callback.onFail();
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * @param url   服务器地址
+     * @param file  所要上传的文件
+     */
+    public static void upload(String url, File file, final UploadCallback callback) {
+        if (okHttpClient == null) {
+            okHttpClient = 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();
+
+        okHttpClient.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("HttpHelper", "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) {
+        if (okHttpClient == null) {
+            okHttpClient = new OkHttpClient();
+        }
+
+        Request request = new Request.Builder().url(url).build();
+        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();
+    }
+}

+ 64 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/helper/SerialPortHelper.java

@@ -0,0 +1,64 @@
+//package com.wdkl.ncs.android.component.home.helper;
+//
+//import android.content.Context;
+//
+//import com.wdkl.ncs.android.component.nursehome.util.VoiceManagerUtil;
+//
+//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");
+//        }
+//    }
+//
+//    /**
+//     * 设置免提
+//     */
+//    public static void setHandsFree(boolean isHandsFree) {
+////        LogUtil.d(LogUtil.getTag(null), "是否需要免提==" + isHandsFree);
+//        if (isHandsFree) {
+//            if (null != SerialPortUtil.getInstance()) {
+//                SerialPortUtil.getInstance().sendCommand(SerialPortUtil.AUDIO, "0", "F");
+//            }
+//        } else {
+//            if (null != SerialPortUtil.getInstance()) {
+//                SerialPortUtil.getInstance().sendCommand(SerialPortUtil.AUDIO, "1", "F");
+//            }
+//        }
+//    }
+//    public static void setCallVoice(Context context, int type) { //0为外喇叭 1为 手柄
+////        int sb = SharedPreferencesUtil.getIntSp(getActivity(), Constants.MSG_SP, SharedPreferencesUtil.SbCallingVoice);
+////        int sbing = SharedPreferencesUtil.getIntSp(getActivity(), Constants.MSG_SP, SharedPreferencesUtil.SbingCallingVoice);
+//        int sb = 60;
+//        int sbing = 70;
+//        if (0 == type) {
+//            if (sb < 0) {
+//                VoiceManagerUtil.setCallVoice(context, 90);
+//            } else {
+//                VoiceManagerUtil.setCallVoice(context, sb);
+//            }
+//        }
+//
+//        if (1 == type) {
+//            if (sbing < 0) {
+//                VoiceManagerUtil.setCallVoice(context, 90);
+//            } else {
+//                VoiceManagerUtil.setCallVoice(context, sbing);
+//            }
+//        }
+//
+//    }
+//
+//
+//
+//
+//
+//}

+ 27 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/launch/HomeLaunch.kt

@@ -0,0 +1,27 @@
+package com.wdkl.ncs.android.component.home.launch
+
+import com.wdkl.ncs.android.component.home.di.DaggerHomeComponent
+import com.wdkl.ncs.android.component.home.di.HomeComponent
+import com.enation.javashop.android.jrouter.external.annotation.Router
+import com.wdkl.ncs.android.lib.base.BaseLaunch
+import com.wdkl.ncs.android.middleware.di.DaggerManager
+
+/**
+ * @author LDD
+ * @Date   2018/1/22 下午2:44
+ * @From   com.wdkl.ncs.android.component.home.launch
+ * @Note   home模块启动类 代替Application 在壳工程Application中反射调用
+ */
+@Router(path = "/home/launch")
+class HomeLaunch : BaseLaunch() {
+    companion object {
+        lateinit var component:HomeComponent
+    }
+
+    override fun moduleInit() {
+        component = DaggerHomeComponent.builder()
+                .applicationComponent(DaggerManager.APPLICATION_COMPONENT)
+                .build()
+    }
+
+}

+ 35 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/DefaultFuzzySearchRule.java

@@ -0,0 +1,35 @@
+package com.wdkl.ncs.android.component.home.search;
+
+import android.text.TextUtils;
+
+import java.util.List;
+
+public class DefaultFuzzySearchRule implements IFuzzySearchRule {
+
+	@Override
+	public boolean accept(CharSequence constraint, String itemSource, List<String> itemPinYinList) {
+		/**
+		 * 1. 先匹配原始的字符,比如 原始字符是 "中国"  输入 "中" 也能保证匹配到
+		 */
+		if ((itemSource != null && itemSource.toLowerCase().contains(constraint.toString().toLowerCase()))) {
+			return true;
+		}
+		/**
+		 * 2. 拼音匹配 这里咱们匹配每个拼音的首字母
+		 */
+		if (itemPinYinList != null && !itemPinYinList.isEmpty()) {
+			StringBuilder firstWord = null;
+			for (String wordPinYin : itemPinYinList) {
+				if (!TextUtils.isEmpty(wordPinYin)) {
+					if (firstWord == null) {
+						firstWord = new StringBuilder(wordPinYin.substring(0, 1));
+					} else {
+						firstWord.append(wordPinYin.substring(0, 1));
+					}
+				}
+			}
+			return firstWord != null && firstWord.toString().toLowerCase().contains(constraint.toString().toLowerCase());
+		}
+		return false;
+	}
+}

+ 87 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/FuzzySearchBaseAdapter.java

@@ -0,0 +1,87 @@
+package com.wdkl.ncs.android.component.home.search;
+
+import android.text.TextUtils;
+import android.widget.Filter;
+import android.widget.Filterable;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class FuzzySearchBaseAdapter<ITEM extends IFuzzySearchItem, VH extends RecyclerView.ViewHolder>
+	extends RecyclerView.Adapter<VH> implements Filterable {
+
+	private   FuzzySearchFilter mFilter;
+	private   List<ITEM>        mBackDataList;
+	protected List<ITEM>        mDataList;
+	private   IFuzzySearchRule  mIFuzzySearchRule;
+
+	public FuzzySearchBaseAdapter(IFuzzySearchRule rule) {
+		this(rule, null);
+	}
+
+	public FuzzySearchBaseAdapter(IFuzzySearchRule rule, List<ITEM> dataList) {
+		if (rule == null) {
+			mIFuzzySearchRule = new DefaultFuzzySearchRule();
+		}
+		mBackDataList = dataList;
+		mDataList = dataList;
+	}
+
+	public void setDataList(List<ITEM> dataList) {
+		mBackDataList = dataList;
+		mDataList = dataList;
+		notifyDataSetChanged();
+	}
+
+
+	@Override
+	public int getItemCount() {
+		return mDataList == null ? 0 : mDataList.size();
+	}
+
+	@Override
+	public Filter getFilter() {
+		if (mFilter == null) {
+			mFilter = new FuzzySearchFilter();
+		}
+		return mFilter;
+	}
+
+	private class FuzzySearchFilter extends Filter {
+
+		/**
+		 * 执行过滤操作,如果搜索的关键字为空,默认所有结果
+		 */
+		@Override
+		protected FilterResults performFiltering(CharSequence constraint) {
+			FilterResults result = new FilterResults();
+			List<ITEM> filterList;
+			if (TextUtils.isEmpty(constraint)) {
+				filterList = mBackDataList;
+			} else {
+				filterList = new ArrayList<>();
+				for (ITEM item : mBackDataList) {
+					if (mIFuzzySearchRule.accept(constraint, item.getSourceKey(), item.getFuzzyKey())) {
+						filterList.add(item);
+					}
+				}
+			}
+			result.values = filterList;
+			result.count = filterList.size();
+			return result;
+		}
+
+		/**
+		 * 得到过滤结果
+		 */
+		@SuppressWarnings("unchecked")
+		@Override
+		protected void publishResults(CharSequence constraint, FilterResults results) {
+			mDataList = (List<ITEM>) results.values;
+			notifyDataSetChanged();
+		}
+	}
+
+}

+ 12 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/IAZItem.java

@@ -0,0 +1,12 @@
+package com.wdkl.ncs.android.component.home.search;
+
+public interface IAZItem {
+
+	/**
+	 * 获取item对应首字母
+	 *
+	 * @return 首字母
+	 */
+	String getSortLetters();
+
+}

+ 24 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/IFuzzySearchItem.java

@@ -0,0 +1,24 @@
+package com.wdkl.ncs.android.component.home.search;
+
+import java.util.List;
+
+/**
+ * 先匹配原始数据,再匹配模糊数据
+ */
+public interface IFuzzySearchItem {
+
+	/**
+	 * 获取item原始字符串
+	 *
+	 * @return 原始item字符串
+	 */
+	String getSourceKey();
+
+	/**
+	 * 获取item模糊字符串,item对应的拼音 江西省->["jiang", "xi", "sheng"]
+	 *
+	 * @return 模糊item字符串
+	 */
+	List<String> getFuzzyKey();
+
+}

+ 20 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/IFuzzySearchRule.java

@@ -0,0 +1,20 @@
+package com.wdkl.ncs.android.component.home.search;
+
+import java.util.List;
+
+/**
+ * 模糊搜索匹配规则接口
+ */
+public interface IFuzzySearchRule {
+
+	/**
+	 * 匹配规则
+	 *
+	 * @param constraint     匹配字符
+	 * @param itemSource     item 对应的原始字符
+	 * @param itemPinYinList item 原始字符对应的拼音列表
+	 * @return 是否匹配
+	 */
+	boolean accept(CharSequence constraint, String itemSource, List<String> itemPinYinList);
+
+}

+ 897 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/search/PinyinUtil.java

@@ -0,0 +1,897 @@
+package com.wdkl.ncs.android.component.home.search;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PinyinUtil {
+
+	private static int[]    sPinYinValue = new int[]{-20319,
+												  -20317,
+												  -20304,
+												  -20295,
+												  -20292,
+												  -20283,
+												  -20265,
+												  -20257,
+												  -20242,
+												  -20230,
+												  -20051,
+												  -20036,
+												  -20032,
+												  -20026,
+												  -20002,
+												  -19990,
+												  -19986,
+												  -19982,
+												  -19976,
+												  -19805,
+												  -19784,
+												  -19775,
+												  -19774,
+												  -19763,
+												  -19756,
+												  -19751,
+												  -19746,
+												  -19741,
+												  -19739,
+												  -19728,
+												  -19725,
+												  -19715,
+												  -19540,
+												  -19531,
+												  -19525,
+												  -19515,
+												  -19500,
+												  -19484,
+												  -19479,
+												  -19467,
+												  -19289,
+												  -19288,
+												  -19281,
+												  -19275,
+												  -19270,
+												  -19263,
+												  -19261,
+												  -19249,
+												  -19243,
+												  -19242,
+												  -19238,
+												  -19235,
+												  -19227,
+												  -19224,
+												  -19218,
+												  -19212,
+												  -19038,
+												  -19023,
+												  -19018,
+												  -19006,
+												  -19003,
+												  -18996,
+												  -18977,
+												  -18961,
+												  -18952,
+												  -18783,
+												  -18774,
+												  -18773,
+												  -18763,
+												  -18756,
+												  -18741,
+												  -18735,
+												  -18731,
+												  -18722,
+												  -18710,
+												  -18697,
+												  -18696,
+												  -18526,
+												  -18518,
+												  -18501,
+												  -18490,
+												  -18478,
+												  -18463,
+												  -18448,
+												  -18447,
+												  -18446,
+												  -18239,
+												  -18237,
+												  -18231,
+												  -18220,
+												  -18211,
+												  -18201,
+												  -18184,
+												  -18183,
+												  -18181,
+												  -18012,
+												  -17997,
+												  -17988,
+												  -17970,
+												  -17964,
+												  -17961,
+												  -17950,
+												  -17947,
+												  -17931,
+												  -17928,
+												  -17922,
+												  -17759,
+												  -17752,
+												  -17733,
+												  -17730,
+												  -17721,
+												  -17703,
+												  -17701,
+												  -17697,
+												  -17692,
+												  -17683,
+												  -17676,
+												  -17496,
+												  -17487,
+												  -17482,
+												  -17468,
+												  -17454,
+												  -17433,
+												  -17427,
+												  -17417,
+												  -17202,
+												  -17185,
+												  -16983,
+												  -16970,
+												  -16942,
+												  -16915,
+												  -16733,
+												  -16708,
+												  -16706,
+												  -16689,
+												  -16664,
+												  -16657,
+												  -16647,
+												  -16474,
+												  -16470,
+												  -16465,
+												  -16459,
+												  -16452,
+												  -16448,
+												  -16433,
+												  -16429,
+												  -16427,
+												  -16423,
+												  -16419,
+												  -16412,
+												  -16407,
+												  -16403,
+												  -16401,
+												  -16393,
+												  -16220,
+												  -16216,
+												  -16212,
+												  -16205,
+												  -16202,
+												  -16187,
+												  -16180,
+												  -16171,
+												  -16169,
+												  -16158,
+												  -16155,
+												  -15959,
+												  -15958,
+												  -15944,
+												  -15933,
+												  -15920,
+												  -15915,
+												  -15903,
+												  -15889,
+												  -15878,
+												  -15707,
+												  -15701,
+												  -15681,
+												  -15667,
+												  -15661,
+												  -15659,
+												  -15652,
+												  -15640,
+												  -15631,
+												  -15625,
+												  -15454,
+												  -15448,
+												  -15436,
+												  -15435,
+												  -15419,
+												  -15416,
+												  -15408,
+												  -15394,
+												  -15385,
+												  -15377,
+												  -15375,
+												  -15369,
+												  -15363,
+												  -15362,
+												  -15183,
+												  -15180,
+												  -15165,
+												  -15158,
+												  -15153,
+												  -15150,
+												  -15149,
+												  -15144,
+												  -15143,
+												  -15141,
+												  -15140,
+												  -15139,
+												  -15128,
+												  -15121,
+												  -15119,
+												  -15117,
+												  -15110,
+												  -15109,
+												  -14941,
+												  -14937,
+												  -14933,
+												  -14930,
+												  -14929,
+												  -14928,
+												  -14926,
+												  -14922,
+												  -14921,
+												  -14914,
+												  -14908,
+												  -14902,
+												  -14894,
+												  -14889,
+												  -14882,
+												  -14873,
+												  -14871,
+												  -14857,
+												  -14678,
+												  -14674,
+												  -14670,
+												  -14668,
+												  -14663,
+												  -14654,
+												  -14645,
+												  -14630,
+												  -14594,
+												  -14429,
+												  -14407,
+												  -14399,
+												  -14384,
+												  -14379,
+												  -14368,
+												  -14355,
+												  -14353,
+												  -14345,
+												  -14170,
+												  -14159,
+												  -14151,
+												  -14149,
+												  -14145,
+												  -14140,
+												  -14137,
+												  -14135,
+												  -14125,
+												  -14123,
+												  -14122,
+												  -14112,
+												  -14109,
+												  -14099,
+												  -14097,
+												  -14094,
+												  -14092,
+												  -14090,
+												  -14087,
+												  -14083,
+												  -13917,
+												  -13914,
+												  -13910,
+												  -13907,
+												  -13906,
+												  -13905,
+												  -13896,
+												  -13894,
+												  -13878,
+												  -13870,
+												  -13859,
+												  -13847,
+												  -13831,
+												  -13658,
+												  -13611,
+												  -13601,
+												  -13406,
+												  -13404,
+												  -13400,
+												  -13398,
+												  -13395,
+												  -13391,
+												  -13387,
+												  -13383,
+												  -13367,
+												  -13359,
+												  -13356,
+												  -13343,
+												  -13340,
+												  -13329,
+												  -13326,
+												  -13318,
+												  -13147,
+												  -13138,
+												  -13120,
+												  -13107,
+												  -13096,
+												  -13095,
+												  -13091,
+												  -13076,
+												  -13068,
+												  -13063,
+												  -13060,
+												  -12888,
+												  -12875,
+												  -12871,
+												  -12860,
+												  -12858,
+												  -12852,
+												  -12849,
+												  -12838,
+												  -12831,
+												  -12829,
+												  -12812,
+												  -12802,
+												  -12607,
+												  -12597,
+												  -12594,
+												  -12585,
+												  -12556,
+												  -12359,
+												  -12346,
+												  -12320,
+												  -12300,
+												  -12120,
+												  -12099,
+												  -12089,
+												  -12074,
+												  -12067,
+												  -12058,
+												  -12039,
+												  -11867,
+												  -11861,
+												  -11847,
+												  -11831,
+												  -11798,
+												  -11781,
+												  -11604,
+												  -11589,
+												  -11536,
+												  -11358,
+												  -11340,
+												  -11339,
+												  -11324,
+												  -11303,
+												  -11097,
+												  -11077,
+												  -11067,
+												  -11055,
+												  -11052,
+												  -11045,
+												  -11041,
+												  -11038,
+												  -11024,
+												  -11020,
+												  -11019,
+												  -11018,
+												  -11014,
+												  -10838,
+												  -10832,
+												  -10815,
+												  -10800,
+												  -10790,
+												  -10780,
+												  -10764,
+												  -10587,
+												  -10544,
+												  -10533,
+												  -10519,
+												  -10331,
+												  -10329,
+												  -10328,
+												  -10322,
+												  -10315,
+												  -10309,
+												  -10307,
+												  -10296,
+												  -10281,
+												  -10274,
+												  -10270,
+												  -10262,
+												  -10260,
+												  -10256,
+												  -10254};
+	private static String[] sPinYinStr   = new String[]{"a",
+														"ai",
+														"an",
+														"ang",
+														"ao",
+														"ba",
+														"bai",
+														"ban",
+														"bang",
+														"bao",
+														"bei",
+														"ben",
+														"beng",
+														"bi",
+														"bian",
+														"biao",
+														"bie",
+														"bin",
+														"bing",
+														"bo",
+														"bu",
+														"ca",
+														"cai",
+														"can",
+														"cang",
+														"cao",
+														"ce",
+														"ceng",
+														"cha",
+														"chai",
+														"chan",
+														"chang",
+														"chao",
+														"che",
+														"chen",
+														"cheng",
+														"chi",
+														"chong",
+														"chou",
+														"chu",
+														"chuai",
+														"chuan",
+														"chuang",
+														"chui",
+														"chun",
+														"chuo",
+														"ci",
+														"cong",
+														"cou",
+														"cu",
+														"cuan",
+														"cui",
+														"cun",
+														"cuo",
+														"da",
+														"dai",
+														"dan",
+														"dang",
+														"dao",
+														"de",
+														"deng",
+														"di",
+														"dian",
+														"diao",
+														"die",
+														"ding",
+														"diu",
+														"dong",
+														"dou",
+														"du",
+														"duan",
+														"dui",
+														"dun",
+														"duo",
+														"e",
+														"en",
+														"er",
+														"fa",
+														"fan",
+														"fang",
+														"fei",
+														"fen",
+														"feng",
+														"fo",
+														"fou",
+														"fu",
+														"ga",
+														"gai",
+														"gan",
+														"gang",
+														"gao",
+														"ge",
+														"gei",
+														"gen",
+														"geng",
+														"gong",
+														"gou",
+														"gu",
+														"gua",
+														"guai",
+														"guan",
+														"guang",
+														"gui",
+														"gun",
+														"guo",
+														"ha",
+														"hai",
+														"han",
+														"hang",
+														"hao",
+														"he",
+														"hei",
+														"hen",
+														"heng",
+														"hong",
+														"hou",
+														"hu",
+														"hua",
+														"huai",
+														"huan",
+														"huang",
+														"hui",
+														"hun",
+														"huo",
+														"ji",
+														"jia",
+														"jian",
+														"jiang",
+														"jiao",
+														"jie",
+														"jin",
+														"jing",
+														"jiong",
+														"jiu",
+														"ju",
+														"juan",
+														"jue",
+														"jun",
+														"ka",
+														"kai",
+														"kan",
+														"kang",
+														"kao",
+														"ke",
+														"ken",
+														"keng",
+														"kong",
+														"kou",
+														"ku",
+														"kua",
+														"kuai",
+														"kuan",
+														"kuang",
+														"kui",
+														"kun",
+														"kuo",
+														"la",
+														"lai",
+														"lan",
+														"lang",
+														"lao",
+														"le",
+														"lei",
+														"leng",
+														"li",
+														"lia",
+														"lian",
+														"liang",
+														"liao",
+														"lie",
+														"lin",
+														"ling",
+														"liu",
+														"long",
+														"lou",
+														"lu",
+														"lv",
+														"luan",
+														"lue",
+														"lun",
+														"luo",
+														"ma",
+														"mai",
+														"man",
+														"mang",
+														"mao",
+														"me",
+														"mei",
+														"men",
+														"meng",
+														"mi",
+														"mian",
+														"miao",
+														"mie",
+														"min",
+														"ming",
+														"miu",
+														"mo",
+														"mou",
+														"mu",
+														"na",
+														"nai",
+														"nan",
+														"nang",
+														"nao",
+														"ne",
+														"nei",
+														"nen",
+														"neng",
+														"ni",
+														"nian",
+														"niang",
+														"niao",
+														"nie",
+														"nin",
+														"ning",
+														"niu",
+														"nong",
+														"nu",
+														"nv",
+														"nuan",
+														"nue",
+														"nuo",
+														"o",
+														"ou",
+														"pa",
+														"pai",
+														"pan",
+														"pang",
+														"pao",
+														"pei",
+														"pen",
+														"peng",
+														"pi",
+														"pian",
+														"piao",
+														"pie",
+														"pin",
+														"ping",
+														"po",
+														"pu",
+														"qi",
+														"qia",
+														"qian",
+														"qiang",
+														"qiao",
+														"qie",
+														"qin",
+														"qing",
+														"qiong",
+														"qiu",
+														"qu",
+														"quan",
+														"que",
+														"qun",
+														"ran",
+														"rang",
+														"rao",
+														"re",
+														"ren",
+														"reng",
+														"ri",
+														"rong",
+														"rou",
+														"ru",
+														"ruan",
+														"rui",
+														"run",
+														"ruo",
+														"sa",
+														"sai",
+														"san",
+														"sang",
+														"sao",
+														"se",
+														"sen",
+														"seng",
+														"sha",
+														"shai",
+														"shan",
+														"shang",
+														"shao",
+														"she",
+														"shen",
+														"sheng",
+														"shi",
+														"shou",
+														"shu",
+														"shua",
+														"shuai",
+														"shuan",
+														"shuang",
+														"shui",
+														"shun",
+														"shuo",
+														"si",
+														"song",
+														"sou",
+														"su",
+														"suan",
+														"sui",
+														"sun",
+														"suo",
+														"ta",
+														"tai",
+														"tan",
+														"tang",
+														"tao",
+														"te",
+														"teng",
+														"ti",
+														"tian",
+														"tiao",
+														"tie",
+														"ting",
+														"tong",
+														"tou",
+														"tu",
+														"tuan",
+														"tui",
+														"tun",
+														"tuo",
+														"wa",
+														"wai",
+														"wan",
+														"wang",
+														"wei",
+														"wen",
+														"weng",
+														"wo",
+														"wu",
+														"xi",
+														"xia",
+														"xian",
+														"xiang",
+														"xiao",
+														"xie",
+														"xin",
+														"xing",
+														"xiong",
+														"xiu",
+														"xu",
+														"xuan",
+														"xue",
+														"xun",
+														"ya",
+														"yan",
+														"yang",
+														"yao",
+														"ye",
+														"yi",
+														"yin",
+														"ying",
+														"yo",
+														"yong",
+														"you",
+														"yu",
+														"yuan",
+														"yue",
+														"yun",
+														"za",
+														"zai",
+														"zan",
+														"zang",
+														"zao",
+														"ze",
+														"zei",
+														"zen",
+														"zeng",
+														"zha",
+														"zhai",
+														"zhan",
+														"zhang",
+														"zhao",
+														"zhe",
+														"zhen",
+														"zheng",
+														"zhi",
+														"zhong",
+														"zhou",
+														"zhu",
+														"zhua",
+														"zhuai",
+														"zhuan",
+														"zhuang",
+														"zhui",
+														"zhun",
+														"zhuo",
+														"zi",
+														"zong",
+														"zou",
+														"zu",
+														"zuan",
+														"zui",
+														"zun",
+														"zuo"};
+
+	/**
+	 * 单个汉字转成ASCII码
+	 */
+	private static int getAscii(String chs) {
+		int asc = 0;
+		try {
+			byte[] bytes = chs.getBytes("gb2312");
+			if (bytes.length > 2 || bytes.length <= 0) {
+				throw new RuntimeException("illegal resource string");
+			}
+			if (bytes.length == 1) {
+				asc = bytes[0];
+			}
+			if (bytes.length == 2) {
+				int heightByte = 256 + bytes[0];
+				int lowByte = 256 + bytes[1];
+				asc = (256 * heightByte + lowByte) - 256 * 256;
+			}
+		} catch (Exception e) {
+			System.out.println("ERROR:ChineseSpelling.class-char2Ascii(String chs)" + e);
+		}
+		return asc;
+	}
+
+	/**
+	 * 单个汉字转换成拼音
+	 **/
+	private static String getSinglePinYin(String str) {
+		String result = null;
+		int ascii = getAscii(str);
+		if (ascii > 0 && ascii < 160) {
+			result = String.valueOf((char) ascii);
+		} else {
+			for (int i = (sPinYinValue.length - 1); i >= 0; i--) {
+				if (sPinYinValue[i] <= ascii) {
+					result = sPinYinStr[i];
+					break;
+				}
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * 中文转换成拼音,返回结果是list
+	 *
+	 * @param source 原始字符
+	 * @return 中国->["zhong", "guo"]
+	 */
+	public static List<String> getPinYinList(String source) {
+		if (source == null || source.isEmpty()) {
+			return null;
+		}
+		List<String> pinyinList = new ArrayList<>();
+		for (int i = 0; i < source.length(); i++) {
+			String item = source.substring(i, i + 1);
+			if (item.getBytes().length >= 2) {
+				String pinyin = getSinglePinYin(item);
+				if (pinyin == null) {
+					pinyin = item;
+				}
+				pinyinList.add(pinyin);
+			} else {
+				pinyinList.add(item);
+			}
+		}
+		return pinyinList;
+	}
+
+	/**
+	 * 中文转换成拼音
+	 *
+	 * @param source 原始字符
+	 * @return 中国->"zhongguo"
+	 */
+	public static String getPinYin(String source) {
+		if (source == null || source.isEmpty()) {
+			return null;
+		}
+		StringBuilder pinyinList = new StringBuilder();
+		for (int i = 0; i < source.length(); i++) {
+			String item = source.substring(i, i + 1);
+			if (item.getBytes().length >= 2) {
+				String pinyin = getSinglePinYin(item);
+				if (pinyin == null) {
+					pinyin = item;
+				}
+				pinyinList.append(pinyin);
+			} else {
+				pinyinList.append(item);
+			}
+		}
+		return pinyinList.toString();
+	}
+
+
+}

+ 276 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/FloatingService.java

@@ -0,0 +1,276 @@
+package com.wdkl.ncs.android.component.home.service;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity;
+import com.wdkl.ncs.android.component.home.util.ActivityStackUtil;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.lib.vo.MessageEvent;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+public class FloatingService extends Service {
+    private static final String TAG = "FloatingService";
+
+    private WindowManager mWindowManager;
+    private WindowManager.LayoutParams wmParams;
+    //浮动布局view
+    private View mFloatingLayout;
+    private TextView mCallView;
+
+    private long clickTime = 0;
+    private boolean callResumed = false;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        initWindow();//设置悬浮窗基本参数(位置、宽高等)
+        //EventBus.getDefault().register(this);
+    }
+
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (!EventBus.getDefault().isRegistered(this)) {
+            EventBus.getDefault().register(this);
+        }
+        initFloating();//悬浮框点击事件的处理
+
+        return new MyBinder();
+    }
+
+    public class MyBinder extends Binder {
+        public FloatingService getService() {
+            return FloatingService.this;
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        return super.onStartCommand(intent, flags, startId);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mFloatingLayout != null) {
+            // 移除悬浮窗口
+            mWindowManager.removeView(mFloatingLayout);
+            mFloatingLayout = null;
+            Constants.Companion.setShowFloatWindow(false);
+        }
+
+        if (EventBus.getDefault().isRegistered(this)) {
+            EventBus.getDefault().unregister(this);
+        }
+        Log.d(TAG, "floating service destroyed");
+    }
+
+    /**
+     * 设置悬浮框基本参数(位置、宽高等)
+     */
+    private void initWindow() {
+        mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
+        //设置好悬浮窗的参数
+        wmParams = getParams();
+        //得到容器,通过这个inflater来获得悬浮窗控件
+        LayoutInflater inflater = LayoutInflater.from(getApplicationContext());
+        // 获取浮动窗口视图所在布局
+        mFloatingLayout = inflater.inflate(R.layout.float_call_window_layout, null);
+        // 添加悬浮窗的视图
+        mWindowManager.addView(mFloatingLayout, wmParams);
+    }
+
+    private WindowManager.LayoutParams getParams() {
+        wmParams = new WindowManager.LayoutParams();
+        if (Build.VERSION.SDK_INT  >= Build.VERSION_CODES.O) {
+            wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+        } else {
+            wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
+        }
+
+        wmParams.gravity = Gravity.LEFT | Gravity.TOP;
+        //wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        wmParams.flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        //设置悬浮窗口长宽数据
+        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+        wmParams.x = 200;
+        wmParams.y = 200;
+        return wmParams;
+    }
+
+    private void initFloating() {
+        mCallView = mFloatingLayout.findViewById(R.id.float_call_view);
+        Constants.Companion.setShowFloatWindow(true);
+
+        //悬浮框触摸事件,设置悬浮框可拖动
+        mCallView.setOnTouchListener(new FloatingListener());
+        //悬浮框点击事件
+        mCallView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                //在这里实现点击重新回到Activity
+                if (System.currentTimeMillis() - clickTime > 3000) {
+                    if (!callResumed) {
+                        resumeCallActivity();
+                    } else {
+                        Toast.makeText(getApplicationContext(), R.string.wait_moment, Toast.LENGTH_LONG).show();
+                    }
+                } else {
+                    Toast.makeText(getApplicationContext(), R.string.wait_moment, Toast.LENGTH_LONG).show();
+                }
+                clickTime = System.currentTimeMillis();
+            }
+        });
+    }
+
+    private void resumeCallActivity() {
+        callResumed = true;
+        Intent intent = new Intent(FloatingService.this, CallSingleActivity.class);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+        Toast.makeText(getApplicationContext(), "Resume", Toast.LENGTH_LONG).show();
+    }
+
+    //开始触控的坐标,移动时的坐标(相对于屏幕左上角的坐标)
+    private int mTouchStartX, mTouchStartY, mTouchCurrentX, mTouchCurrentY;
+    //开始时的坐标和结束时的坐标(相对于自身控件的坐标)
+    private int mStartX, mStartY, mStopX, mStopY;
+    //判断悬浮窗口是否移动,这里做个标记,防止移动后松手触发了点击事件
+    private boolean isMove;
+
+    private class FloatingListener implements View.OnTouchListener {
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            int action = event.getAction();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                    isMove = false;
+                    mTouchStartX = (int) event.getRawX();
+                    mTouchStartY = (int) event.getRawY();
+                    mStartX = (int) event.getX();
+                    mStartY = (int) event.getY();
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    mTouchCurrentX = (int) event.getRawX();
+                    mTouchCurrentY = (int) event.getRawY();
+                    wmParams.x += mTouchCurrentX - mTouchStartX;
+                    wmParams.y += mTouchCurrentY - mTouchStartY;
+                    mWindowManager.updateViewLayout(mFloatingLayout, wmParams);
+                    mTouchStartX = mTouchCurrentX;
+                    mTouchStartY = mTouchCurrentY;
+                    break;
+                case MotionEvent.ACTION_UP:
+                    mStopX = (int) event.getX();
+                    mStopY = (int) event.getY();
+                    if (Math.abs(mStartX - mStopX)  >= 1 || Math.abs(mStartY - mStopY)  >= 1) {
+                        isMove = true;
+                    }
+                    break;
+                default:
+                    break;
+            }
+
+            //如果是移动事件不触发OnClick事件,防止移动的时候一放手形成点击事件
+            return isMove;
+        }
+    }
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onEvent(MessageEvent messageEvent) {
+        int tag = messageEvent.getTag();
+        if (tag == 2) {
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            Log.i(TAG, "received tcpModel: " + tcpModel.toJson());
+            int curInteractionId = -1;
+            if (tcpModel.getData() != null) {
+                InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
+                curInteractionId = interactionVO.getId();
+            }
+
+            if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    MediaPlayHelper.getInstance().stopMusic(true);
+                    RingPlayHelper.stopRingTone();
+                    SpeechUtil.getInstance().stopSpeak();
+                    Log.i(TAG, "对方接听电话啦");
+                    //对方接听电话,恢复到call界面
+                    resumeCallActivity();
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) { //对方挂断
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                    if (callSingleActivity != null) {
+                        callSingleActivity.finish();
+                    }
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                    if (callSingleActivity != null) {
+                        callSingleActivity.finish();
+                    }
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                    if (callSingleActivity != null) {
+                        callSingleActivity.finish();
+                    }
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                    if (callSingleActivity != null) {
+                        callSingleActivity.finish();
+                    }
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                    if (callSingleActivity != null) {
+                        callSingleActivity.finish();
+                    }
+                }
+            }
+        } else if (tag == 1) {
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            if (tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS){ //服务器返回成功
+                CallSingleActivity callSingleActivity = ActivityStackUtil.getCallSingleActivity();
+                if (callSingleActivity != null) {
+                    callSingleActivity.callOutSuccess(tcpModel);
+                }
+
+                //重新拉起通话界面
+                resumeCallActivity();
+            }
+        }
+    }
+}

+ 197 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/PhoneStateReceiver.java

@@ -0,0 +1,197 @@
+package com.wdkl.ncs.android.component.home.service;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.activity.SmsReceivedActivity;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.util.AppUtils;
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect;
+import com.wdkl.ncs.android.component.home.util.PhoneCallUtil;
+import com.wdkl.ncs.android.component.home.util.RecordHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.component.home.util.Util;
+import com.wdkl.ncs.android.lib.vo.MessageEvent;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactVO;
+import com.wdkl.ncs.android.middleware.tcp.NettyClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel;
+import com.wdkl.ncs.android.middleware.tcp.channel.PhoneUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.utils.ContactHelper;
+
+import org.greenrobot.eventbus.EventBus;
+
+import static com.wdkl.ncs.android.lib.utils.ExtendMethodsKt.showMessage;
+
+public class PhoneStateReceiver extends BroadcastReceiver {
+    private static final String TAG = "PhoneStateReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        Log.d(TAG, "PhoneStateReceiver action: " + action);
+
+        if (action.equals("android.provider.Telephony.SMS_RECEIVED")) {
+            // 从Intent参数中取出一个Bundle对象
+            Bundle bundle = intent.getExtras();
+            //使用pdu密钥来提取一个SMS  pdus数组,其中每一个pdu都表示一条短信消息
+            Object[] pdus = (Object[]) bundle.get("pdus");
+            SmsMessage[] messages = new SmsMessage[pdus.length];
+            for (int i = 0; i < messages.length; i++) {
+                //使用SmsMessage()的静态方法createFromPdu()方法将每一个pdu字节数组转换为SmsMessage对象
+                messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
+            }
+            //调用SmsMessage对象的getOriginatingAddress()方法就可以获取到短信的发送发号码
+            String address = messages[0].getOriginatingAddress();
+            String fullMessage = "";
+            for (SmsMessage message : messages) {
+                //调用SmsMessage对象的getMessageBody()方法就可以获取到短信的内容,将每一个SmsMessage对象中的短信内容拼接起来,就组成了一条完整的短信内容
+                fullMessage += message.getMessageBody();
+            }
+
+            //如果是运营商短信且是点击了号码查询才提示
+            if (("10001".equals(address) || SettingConfig.getOperatorNumber(context).equals(address)) && Constants.Companion.getSmsCheck()) {
+                String regex = "(1[3-9][0-9])\\d{8}";
+                boolean find = AppUtils.checkMobileNumber(fullMessage, regex);
+
+                if (find) {
+                    Intent smsIntent = new Intent(context, SmsReceivedActivity.class);
+                    smsIntent.putExtra("address", address);
+                    smsIntent.putExtra("sms_data", fullMessage);
+                    smsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    context.startActivity(smsIntent);
+                } else {
+                    Log.e(TAG, "find no phone number!!!");
+                }
+            }
+            Log.e(TAG, "received sms: " + fullMessage + ", address: " + address + ", check: " + Constants.Companion.getSmsCheck());
+        } else if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
+            String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
+            DeviceChannel.calling = true;
+            Log.d(TAG, "PhoneStateReceiver EXTRA_PHONE_NUMBER: " + phoneNumber);
+        } else {
+            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
+            String extraIncomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
+            Log.d(TAG, "PhoneStateReceiver onReceive state: " + state + ", extraIncomingNumber: " + extraIncomingNumber);
+
+            if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)) {
+                //待机中
+                //Log.d(TAG, "PhoneStateReceiver idle");
+                if (Constants.Companion.getPhoneState() != Constants.PHONE_IDLE) {
+                    if (!Constants.uploadCalllog) {
+                        //发送tcp
+                        if (Constants.Companion.getPhoneDataVO() != null) {
+                            TcpModel tcpModel = PhoneUtil.phoneHandoff(Constants.Companion.getDeviceId(), Constants.Companion.getPhoneDataVO());
+                            NettyClient.Companion.getInstance().sendMsg(tcpModel.toJson()).subscribe(it -> {
+                                if (it) {
+                                    Log.d(TAG, "TCP.发送消息完成");
+                                } else {
+                                    Log.e(TAG, "TCP.发送消息失败");
+                                    HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                                }
+                            });
+                            Constants.Companion.setPhoneDataVO(null);
+                        }
+                    } else {
+                        //通话结束,上传通话记录
+                        EventBus.getDefault().post(new MessageEvent("call_end", Constants.EVENT_CALL_END));
+                    }
+                }
+
+                DeviceChannel.calling = false;
+                Constants.Companion.setPhoneState(Constants.PHONE_IDLE);
+                SpeechUtil.getInstance().stopSpeak();
+            } else if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING) && extraIncomingNumber != null) {
+                //非白名单号码拦截并删除记录
+                //if (Constants.Companion.getPhoneWhiteList() != null) {
+                    for (String s : Constants.Companion.getPhoneWhiteList()) {
+                        if (s.length() > 10 && (s.contains(extraIncomingNumber) || extraIncomingNumber.contains(s))) { //号码在白名单内修改通话状态,并退出方法
+                            //Log.d(TAG, "PhoneStateReceiver incoming call: " + extraIncomingNumber);
+                            DeviceChannel.calling = true;
+                            Constants.Companion.setPhoneState(Constants.PHONE_INCOMING);
+                            //来电时取消留言
+                            RecordHelper.getInstance().stopCancelRecordByOther(true);
+                            EventBus.getDefault().post(new MessageEvent(true, Constants.EVENT_CLEAR_IM));
+
+                            //tts语音播报
+                            /*if (ContactHelper.getContacts().size() > 0) {
+                                for (WatchContactVO contactVO : ContactHelper.getContacts()) {
+                                    if (contactVO.getPhoneNumber() != null && contactVO.getPhoneNumber().length() > 10
+                                            && (contactVO.getPhoneNumber().contains(extraIncomingNumber) || extraIncomingNumber.contains(contactVO.getPhoneNumber()))) {
+                                        String frameName = Util.INSTANCE.appendSpace(contactVO.getName().replace("-", ""));
+                                        SpeechUtil.getInstance().newSpeech(frameName + "来电, " + frameName + "来电, " + frameName + "来电, " + frameName + "来电, " + frameName + "来电", false);
+                                        break;
+                                    }
+                                }
+                            }*/
+                            return;
+                        }
+                    }
+                    //循环结束,说明来电号码不在白名单内,挂断来电
+                    showMessage(context.getString(R.string.invalid_phone_number, extraIncomingNumber));
+                    boolean res = PhoneCallUtil.endCall(context);
+                    Log.d(TAG, "end call result: " + res);
+                    PhoneCallUtil.deleteCallLog(context, extraIncomingNumber);
+                    DeviceChannel.calling = false;
+                //}
+            } else if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK) && extraIncomingNumber != null) {
+                //移动联通电信运营商号码默认为白名单
+                if ("10000".equals(extraIncomingNumber) || "10010".equals(extraIncomingNumber) || "10086".equals(extraIncomingNumber)) {
+                    return;
+                }
+
+                //通话中
+                //Log.d(TAG, "PhoneStateReceiver in call: " + extraIncomingNumber);
+
+                SpeechUtil.getInstance().stopSpeak();
+                DeviceChannel.calling = true;
+                RecordHelper.getInstance().stopCancelRecordByOther(true);
+                EventBus.getDefault().post(new MessageEvent(true, Constants.EVENT_CLEAR_IM));
+                if (Constants.Companion.getPhoneState() == Constants.PHONE_INCOMING) {
+                    if (!Constants.uploadCalllog) {
+                        //发送tcp
+                        if (Constants.Companion.getPhoneDataVO() != null) {
+                            TcpModel tcpModel = PhoneUtil.phoneAccept(Constants.Companion.getDeviceId(), Constants.Companion.getPhoneDataVO());
+                            NettyClient.Companion.getInstance().sendMsg(tcpModel.toJson()).subscribe(it -> {
+                                if (it) {
+                                    Log.d(TAG, "TCP.发送消息完成");
+                                } else {
+                                    Log.e(TAG, "TCP.发送消息失败");
+                                    HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                                }
+                            });
+                        }
+                    }
+                } else if (Constants.Companion.getPhoneState() == Constants.PHONE_IDLE) {
+                    Constants.Companion.setPhoneState(Constants.PHONE_OUTGOING);
+                } else {
+                    Constants.Companion.setPhoneState(Constants.PHONE_CALLING);
+                }
+
+
+                //非白名单号码拦截并删除记录
+                //if (Constants.Companion.getPhoneWhiteList() != null) {
+                    for (String s : Constants.Companion.getPhoneWhiteList()) {
+                        if (s.length() > 10 && (s.contains(extraIncomingNumber) || extraIncomingNumber.contains(s))) { //号码在白名单内修改通话状态,并退出方法
+                            return;
+                        }
+                    }
+                    //循环结束,说明来电号码不在白名单内,挂断来电
+                    showMessage(context.getString(R.string.invalid_phone_number, extraIncomingNumber));
+                    Constants.Companion.setPhoneState(Constants.PHONE_IDLE);
+                    boolean res = PhoneCallUtil.endCall(context);
+                    Log.d(TAG, "end call result: " + res);
+                    PhoneCallUtil.deleteCallLog(context, extraIncomingNumber);
+                    DeviceChannel.calling = false;
+                //}
+            }
+        }
+    }
+}

+ 21 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/WdBootReceiver.java

@@ -0,0 +1,21 @@
+package com.wdkl.ncs.android.component.home.service;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.wdkl.ncs.android.component.home.activity.WatchHome2Activity;
+
+public class WdBootReceiver extends BroadcastReceiver {
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
+            Log.d("wdBoot", "收到开机广播,启动app");
+            Intent startIntent= new Intent(context, WatchHome2Activity.class);
+            startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            context.startActivity(startIntent);
+        }
+    }
+}

File diff suppressed because it is too large
+ 1059 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt


+ 108 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/settingconfig/SettingConfig.java

@@ -0,0 +1,108 @@
+package com.wdkl.ncs.android.component.home.settingconfig;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public class SettingConfig {
+
+    private static final String SP_NAME = "SP_FUNCTION";
+
+    //呼叫转接时间设置
+    private static final String KEY_SP_COUNTDOWN_TIME = "KEY_SP_COUNTDOWN_TIME";
+    private static final int CountdownTime = 15;
+
+    //电话功能类型
+    private static final String KEY_SP_VOICE_CALL_TYPE = "KEY_SP_VOICE_CALL_TYPE";
+    public static final int SIP_CALL = 1;  //网络电话
+    public static final int PHONE_CALL = 2;  //普通电话
+
+    //短信查询本机号码指令
+    private static final String KEY_SP_SMS_CMD = "KEY_SP_SMS_CMD";
+    private static final String KEY_SP_OPERATOR_NUMBER = "KEY_SP_OPERATOR_NUMBER";
+
+    private static final String KEY_SP_IM_CHANNEL = "KEY_SP_IM_CHANNEL";
+    public static final int IM_CHANNEL_ON = 1;
+    public static final int IM_CHANNEL_OFF = 2;
+
+    //默认语言
+    private static final String KEY_LANGUAGE_ID = "KEY_LANGUAGE_ID";
+    private static final String KEY_LANGUAGE_MODE = "KEY_LANGUAGE_MODE";
+
+    public static int getLanguageId(Context context) {
+        //0--auto, 1--English, 2--中文, 3--西班牙语, 4--俄语
+        return getSP(context).getInt(KEY_LANGUAGE_ID, 0);
+    }
+
+    public static void setLanguageId(Context context, int id) {
+        getEditor(context).putInt(KEY_LANGUAGE_ID, id).apply();
+    }
+
+    public static int getLanguageMode(Context context) {
+        return getSP(context).getInt(KEY_LANGUAGE_MODE, 0);
+    }
+
+    public static void setLanguageMode(Context context, int mode) {
+        getEditor(context).putInt(KEY_LANGUAGE_MODE, mode).apply();
+    }
+
+
+    /**
+     * 获取转发时间
+     *
+     * @return
+     */
+    public static int getCountdownTime(Context context) {
+        return getSP(context).getInt(KEY_SP_COUNTDOWN_TIME, CountdownTime);
+    }
+
+    /**
+     * 设置转发时间
+     *
+     * @param value
+     */
+    public static void setCountdownTime(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_COUNTDOWN_TIME, value).apply();
+    }
+
+
+    public static int getVoiceCallType(Context context) {
+        return getSP(context).getInt(KEY_SP_VOICE_CALL_TYPE, SettingConfig.PHONE_CALL);
+    }
+
+    public static void setVoiceCallType(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_VOICE_CALL_TYPE, value).apply();
+    }
+
+    public static String getSmsCommand(Context context) {
+        return getSP(context).getString(KEY_SP_SMS_CMD, "BJ");
+    }
+
+    public static void setSmsCommand(Context context, String cmd) {
+        getEditor(context).putString(KEY_SP_SMS_CMD, cmd).apply();
+    }
+
+    public static String getOperatorNumber(Context context) {
+        return getSP(context).getString(KEY_SP_OPERATOR_NUMBER, "10086");
+    }
+
+    public static void setOperatorNumber(Context context, String number) {
+        getEditor(context).putString(KEY_SP_OPERATOR_NUMBER, number).apply();
+    }
+
+    public static int getImChannel(Context context) {
+        return getSP(context).getInt(KEY_SP_IM_CHANNEL, -1);
+    }
+
+    public static void setImChannel(Context context, int value) {
+        getEditor(context).putInt(KEY_SP_IM_CHANNEL, 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();
+    }
+}

+ 464 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/CallSingleActivity.java

@@ -0,0 +1,464 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Color;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentManager;
+
+import com.enation.javashop.utils.base.tool.CommonTool;
+import com.enation.javashop.utils.base.widget.LoadingDialog;
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.service.FloatingService;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.util.ActivityStackUtil;
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect;
+import com.wdkl.ncs.android.component.home.util.HomeWatcher;
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils;
+import com.wdkl.ncs.android.component.home.util.LogUpload;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RecordHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.NettyClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel;
+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.DeviceTypeEnum;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType;
+import com.wdkl.rtc.util.JanusConstant;
+import com.wdkl.rtc.entity.Room;
+import com.wdkl.rtc.janus.JanusClient;
+import com.wdkl.rtc.janus.VideoRoomCallback;
+import com.wdkl.rtc.rtc.WebRTCEngine;
+import com.wdkl.rtc.util.EnumType;
+import com.wdkl.rtc.util.ILogUpload;
+
+import java.math.BigInteger;
+
+import pub.devrel.easypermissions.EasyPermissions;
+
+/**
+ * 单人通话界面
+ */
+public class CallSingleActivity extends AppCompatActivity {
+
+    public static final String EXTRA_ROOM_ID = "roomId";
+    public static final String EXTRA_MO = "isOutGoing";
+    public static final String EXTRA_AUDIO_ONLY = "audioOnly";
+    public static final String EXTRA_TCPMODEL = "tcpModel";
+    public static final String EXTRA_SHOWNAME = "showName";
+    private static final String TAG = "CallSingleActivity";
+
+    private boolean isOutgoing;
+    private BigInteger roomId;
+    private Room room;
+    private BigInteger localUserId = BigInteger.valueOf(Constants.Companion.getMemberId());
+
+    boolean isAudioOnly = true;
+    String showName;
+    TcpModel recTcpModel;
+    JanusClient janusClient;
+    ILogUpload iLogUpload = new LogUpload();
+    VideoRoomCallback videoRoomCallback;
+    boolean isSimulateBed = false;
+
+    private SingleCallFragment currentFragment;
+    //振动
+    Vibrator mVibrator;
+    public LoadingDialog loadingDialog;
+
+    private boolean mServiceBound = false;
+    private HomeWatcher mHomeWatcher;
+
+    private ServiceConnection mCallServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "floating service connected");
+            // 获取服务的操作对象
+            FloatingService.MyBinder binder = (FloatingService.MyBinder) service;
+            binder.getService();
+        }
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, "floating service disconnected");
+        }
+    };
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        //切换语言
+        int languageId = SettingConfig.getLanguageId(this);
+        LocaleMangerUtils.setApplicationLanguageByIndex(this, languageId);
+
+        super.onCreate(savedInstanceState);
+        setStatusBarOrScreenStatus(this);
+        setContentView(R.layout.activity_single_call);
+
+        DeviceChannel.calling = true;
+
+        mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+        loadingDialog = CommonTool.createLoadingDialog(this, R.layout.custom_loading,R.id.loadding_image);
+
+        final Intent intent = getIntent();
+        roomId = new BigInteger(intent.getStringExtra(EXTRA_ROOM_ID));
+        isOutgoing = intent.getBooleanExtra(EXTRA_MO, false);
+        isAudioOnly = intent.getBooleanExtra(EXTRA_AUDIO_ONLY,true);
+        recTcpModel = (TcpModel) intent.getSerializableExtra(EXTRA_TCPMODEL);
+        showName = intent.getStringExtra(EXTRA_SHOWNAME);
+
+        if (recTcpModel !=null && recTcpModel.getData() != null) {
+            Log.d(TAG, recTcpModel.toJson());
+            InteractionVO interactionVO = new Gson().fromJson(recTcpModel.getData().toString(), InteractionVO.class);
+            isSimulateBed = (interactionVO.getFromDeviceType() == DeviceTypeEnum.SIMULATE_BED_DEVICE.value());
+        }
+
+        // 权限检测
+        String[] perms;
+        if (isAudioOnly) {
+            perms = new String[]{Manifest.permission.RECORD_AUDIO};
+        } else {
+            perms = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};
+        }
+        if (!EasyPermissions.hasPermissions(CallSingleActivity.this, perms)) {
+            EasyPermissions.requestPermissions(CallSingleActivity.this, "need record, camera permissions",
+                    100, perms);
+        } else {
+            //初始化 engine
+            WebRTCEngine.getInstance().init(isAudioOnly, this);
+            init(roomId, isAudioOnly);
+        }
+
+        RecordHelper.getInstance().stopCancelRecordByOther(true);
+
+        //home和recent按键事件监听
+        mHomeWatcher = new HomeWatcher(this);
+        mHomeWatcher.setOnHomePressedListener(new HomeWatcher.OnHomePressedListener() {
+            @Override
+            public void onHomePressed() {
+                //如果悬浮窗没有显示 就开启服务展示悬浮窗
+                if (!Constants.Companion.getShowFloatWindow()) {
+                    startFloatingService();
+                }
+            }
+
+            @Override
+            public void onRecentAppPressed() {
+                //如果悬浮窗没有显示 就开启服务展示悬浮窗
+                if (!Constants.Companion.getShowFloatWindow()) {
+                    startFloatingService();
+                }
+            }
+        });
+        mHomeWatcher.startWatch();
+
+        ActivityStackUtil.setCallSingleActivity(this);
+    }
+
+    private void startFloatingService() {
+        //先检查权限
+        if (Build.VERSION.SDK_INT >= 23) {
+            if (!Settings.canDrawOverlays(this)) {
+                Toast.makeText(CallSingleActivity.this, "need floating window permission", Toast.LENGTH_LONG).show();
+                return;
+            }
+        }
+
+        //最小化Activity
+        moveTaskToBack(true);
+
+        //开启服务显示悬浮框
+        Intent floatVideoIntent = new Intent(this, FloatingService.class);
+        mServiceBound = bindService(floatVideoIntent, mCallServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        //界面恢复,移除悬浮框
+        if (mServiceBound) {
+            unbindService(mCallServiceConnection);
+            mServiceBound = false;
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        //通话时不能按返回键,跟微信同现象,只能挂断结束或者接听
+        /*super.onBackPressed();
+        if (currentFragment != null) {
+            if (currentFragment instanceof FragmentAudio) {
+                ((FragmentAudio) currentFragment).onBackPressed();
+            } else if (currentFragment instanceof FragmentVideo) {
+                ((FragmentVideo) currentFragment).onBackPressed();
+            }
+        }*/
+    }
+
+    public SingleCallFragment getCurrentFragment() {
+        return currentFragment;
+    }
+
+    public Vibrator getmVibrator() {
+        return mVibrator;
+    }
+
+    private void init(BigInteger roomId, boolean audioOnly) {
+        SingleCallFragment fragment;
+        if (audioOnly) {
+            fragment = new FragmentAudio();
+        } else {
+            fragment = new FragmentVideo();
+        }
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        currentFragment = fragment;
+        fragmentManager.beginTransaction()
+                    .replace(R.id.fragment_content, fragment)
+                    .commit();
+
+        //启动通话界面默认切换到外放模式
+        //WebRTCEngine.getInstance().toggleSpeaker(true);
+
+        room = new Room(roomId);
+
+        //================ 信令
+        //初始化 janusClient
+        janusClient = new JanusClient(JanusConstant.JANUS_URL, localUserId);
+
+        //================ 信令
+        videoRoomCallback = new VideoRoomCallback(janusClient,room, localUserId, isOutgoing, iLogUpload);
+        janusClient.setJanusCallback(videoRoomCallback);
+        // 1,连接,监听 onOpen, createSession,createSession 成功时设置janusClient sessionId,回调videoRoomCallback.onCreateSession
+        // 2,在 videoRoomCallback.onCreateSession 中使用 janusClient.attachPlugin("janus.plugin.videoroom") 获得插件 handleId,回调 videoRoomCallback.onAttached
+        // 3,在 videoRoomCallback.onAttached 中设置 videoRoomHandlerId变量为handleId,并调用 janusClient.joinRoom(handleId, room.getId(), inviteUserId.toString())
+        // 4,发送 joinRoom 后,经过janusClient onMeesage处理后,由 videoRoomCallback.onMessage 处理 plugindata.data
+        // 5,处理 janus:event,plugindata.data.videoroom:joined,在 peerConnection上createOffer,设置本地 sdp,回调 offerCallback.onCreateOfferSuccess,发布 publisher
+        if (isOutgoing) {
+            //MediaPlayHelper.getInstance().playResMusic(R.raw.outgoing_call, 0.8f, true);
+            //RingPlayHelper.playRingTone(this, R.raw.ring_back2, true);
+            janusClient.setCallState(EnumType.CallState.Outgoing);
+            janusClient.connect();
+        } else {
+            //模拟分机呼叫由主机建立通话
+            if (isSimulateBed) {
+                janusClient.setCallState(EnumType.CallState.Outgoing);
+                janusClient.connect();
+            } else {
+                janusClient.setCallState(EnumType.CallState.Incoming);
+            }
+
+            //MediaPlayHelper.getInstance().playResMusic(R.raw.incoming_call, 1.0f, true);
+            //RingPlayHelper.playRingTone(this, R.raw.incoming_call, true);
+            long[] pattern = new long[]{100L, 2000L, 1000L, 2000L, 1000L, 2000L};
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+                mVibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
+            } else {
+                mVibrator.vibrate(pattern, -1);
+            }
+        }
+
+        // 6,处理 publishers,每个publisher 包含 id (feedId) 和 display,attach 到发布者的 handler 上,准备接收视频流,每个发布者都要 attach 一遍,然后协商 sdp, SFU,subscribeAttach 到发布者的 handle 上 [janusClient.subscribeAttach(feedId)]。将publisher 放到Room对象中的publishers, 在 janusClient.subscribeAttach(feedId) 中回调 videoRoomCallback.onSubscribeAttached
+        // 7,在 videoRoomCallback.onSubscribeAttached 中订阅发布者,如果有 publisher 的话
+    }
+
+    public void callOutSuccess(TcpModel tcpModel) {
+        if (currentFragment != null) {
+            currentFragment.callOutSuccess(tcpModel);
+        }
+    }
+
+    // 切换到语音通话
+    public void switchAudio() {
+        //init(targetId, isOutgoing, true, true);
+    }
+
+    public Room getRoom() {
+        return room;
+    }
+
+    @TargetApi(19)
+    private static int getSystemUiVisibility() {
+        int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN |
+                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+        }
+        return flags;
+    }
+
+    /**
+     * 设置状态栏透明
+     */
+    @TargetApi(19)
+    public void setStatusBarOrScreenStatus(Activity activity) {
+        Window window = activity.getWindow();
+        //全屏+锁屏+常亮显示
+        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN |
+                //WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
+                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
+                WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+        window.getDecorView().setSystemUiVisibility(getSystemUiVisibility());
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
+            layoutParams.layoutInDisplayCutoutMode =
+                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+            window.setAttributes(layoutParams);
+        }
+        // 5.0以上系统状态栏透明
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            //清除透明状态栏
+            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+            //设置状态栏颜色必须添加
+            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+            window.setStatusBarColor(Color.TRANSPARENT);//设置透明
+        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //19
+            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    public void sendHandOffTcp(){
+        if (recTcpModel.getType() == TcpType.VOICE) {
+            InteractionVO interactionVO = null;
+            if (recTcpModel.getData()!=null){
+                interactionVO = new Gson().fromJson(recTcpModel.getData().toString(), InteractionVO.class);
+            }
+
+            if (isOutgoing && (recTcpModel.getAction() == TcpAction.VoiceAction.SUCCESS || recTcpModel.getAction() == TcpAction.VoiceAction.ACCEPT)) {
+                int toId;
+                if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                    toId = interactionVO.getToDeviceId();
+                } else {
+                    toId = interactionVO.getFromDeviceId();
+                }
+                TcpModel voiceUtilTcpModel = VoiceUtil.voiceHandoff(Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+
+                NettyClient.Companion.getInstance().sendMsg(voiceUtilTcpModel.toJson()).subscribe(it-> {
+                    if (it) {
+                        Log.d(TAG, "TCP.发送消息完成");
+                    } else {
+                        Log.e(TAG, "TCP.发送消息失败");
+                        HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                    }
+                });
+            } else if (recTcpModel.getAction() == TcpAction.VoiceAction.CALL) {
+                int toId;
+                if (interactionVO==null){
+                    toId = recTcpModel.getToId();
+                } else {
+                    if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                        toId = interactionVO.getToDeviceId();
+                    } else {
+                        toId = interactionVO.getFromDeviceId();
+                    }
+                }
+                TcpModel voiceUtilTcpModel;
+                if (interactionVO == null){
+                    voiceUtilTcpModel = VoiceUtil.voiceCancel(Constants.Companion.getDeviceId(),recTcpModel.getToId());
+                } else {
+                    voiceUtilTcpModel = VoiceUtil.voiceHandoff(Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+                }
+                NettyClient.Companion.getInstance().sendMsg(voiceUtilTcpModel.toJson()).subscribe(it-> {
+                    if (it) {
+                        Log.d(TAG, "TCP.发送消息完成");
+                    } else {
+                        Log.e(TAG, "TCP.发送消息失败");
+                        HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                    }
+                });
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event){
+        return currentFragment.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public synchronized void finish(){
+        Log.d(TAG, "call activity end");
+        if (janusClient != null && janusClient.getWebSocketChannel()!=null) {
+            janusClient.setCallState(EnumType.CallState.Idle);
+            if (isOutgoing){
+                if (janusClient.getCurrentHandleId()!=null) {
+                    janusClient.destroyRoom(janusClient.getCurrentHandleId(), null);
+                }
+            } else {
+                if (isSimulateBed) {
+                    if (janusClient.getCurrentHandleId()!=null) {
+                        janusClient.destroyRoom(janusClient.getCurrentHandleId(), null);
+                    }
+                } else {
+                    janusClient.leaveRoom();
+                }
+            }
+            janusClient.disConnect();
+        }
+
+        if (loadingDialog != null) {
+            loadingDialog.dismiss();
+        }
+
+        if (mVibrator != null) {
+            mVibrator.cancel();
+        }
+
+        if (!isFinishing()) {
+            super.finish();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.d(TAG, "call activity destroyed");
+        MediaPlayHelper.getInstance().stopMusic(true);
+        RingPlayHelper.stopRingTone();
+        SpeechUtil.getInstance().stopSpeak();
+        if (mHomeWatcher != null) {
+            mHomeWatcher.stopWatch();
+        }
+
+        DeviceChannel.calling = false;
+        DeviceChannel.callId = 0;
+
+        //解绑,不显示悬浮框
+        if (mServiceBound) {
+            unbindService(mCallServiceConnection);
+            mServiceBound = false;
+        }
+
+        ActivityStackUtil.setCallSingleActivity(null);
+
+        super.onDestroy();
+    }
+
+    public boolean isOutgoing() {
+        return isOutgoing;
+    }
+}

+ 437 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/FragmentAudio.java

@@ -0,0 +1,437 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.annotation.SuppressLint;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.BarUtils;
+import com.enation.javashop.utils.base.tool.CommonTool;
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect;
+import com.wdkl.ncs.android.component.home.util.LocaleMangerUtils;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.component.home.util.Util;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.NettyClient;
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.utils.StringUtil;
+import com.wdkl.rtc.entity.Publisher;
+import com.wdkl.rtc.entity.Room;
+import com.wdkl.rtc.rtc.WebRTCEngine;
+import com.wdkl.rtc.util.EnumType;
+import com.wdkl.rtc.util.OSUtils;
+
+import java.util.Locale;
+
+/**
+ * 语音通话控制界面
+ */
+public class FragmentAudio extends SingleCallFragment implements View.OnClickListener {
+    private static final String TAG = "FragmentAudio";
+    private ImageView muteImageView;
+    private ImageView speakerImageView;
+    private boolean micEnabled = false; // 静音
+    private boolean isSpeakerOn = false; // 扬声器
+    private boolean accepted = false;
+
+    TransHandler transHandler;
+
+    @Override
+    int getLayout() {
+        return R.layout.fragment_audio;
+    }
+
+    @Override
+    public void initView(View view) {
+        super.initView(view);
+        muteImageView = view.findViewById(R.id.muteImageView);
+        speakerImageView = view.findViewById(R.id.speakerImageView);
+        minimizeImageView.setVisibility(View.GONE);
+        outgoingHangupImageView.setOnClickListener(this);
+        incomingHangupImageView.setOnClickListener(this);
+        incomingTransImageView.setOnClickListener(this);
+        minimizeImageView.setOnClickListener(this);
+        muteImageView.setOnClickListener(this);
+        acceptImageView.setOnClickListener(this);
+        speakerImageView.setOnClickListener(this);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || OSUtils.isMiui() || OSUtils.isFlyme()) {
+            lytParent.post(() -> {
+                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) minimizeImageView.getLayoutParams();
+                params.topMargin = BarUtils.getStatusBarHeight();
+                minimizeImageView.setLayoutParams(params);
+            });
+        }
+    }
+
+    @Override
+    public void init() {
+        super.init();
+
+        if (callSingleActivity.loadingDialog==null){
+            callSingleActivity.loadingDialog = CommonTool.createLoadingDialog(this.getContext(), R.layout.custom_loading,R.id.loadding_image);
+        }
+        InteractionVO interactionVO = null;
+        if (callSingleActivity.recTcpModel.getData()!=null) {
+            interactionVO = new Gson().fromJson(callSingleActivity.recTcpModel.getData().toString(), InteractionVO.class);
+        }
+        transHandler = new TransHandler();
+        //是来电,且是客户来电进入延时转接
+        if (!isOutgoing && interactionVO.getFromClerkId()==null) {
+            int transSeconds = SettingConfig.getCountdownTime(this.getContext());
+            transHandler.sendEmptyMessageDelayed(TRANS, transSeconds * 1000);
+        }
+
+        // 如果已经接通
+        if (callSingleActivity.janusClient != null && callSingleActivity.janusClient.getCallState() == EnumType.CallState.Connected) {
+            descTextView.setVisibility(View.GONE); // 提示语
+            outgoingActionContainer.setVisibility(View.VISIBLE);
+            durationTextView.setVisibility(View.VISIBLE);
+            startRefreshTime();
+        } else {
+            // 如果未接通
+            if (isOutgoing) {
+                descTextView.setText(getShowName() + "\n" + StringUtil.getResString(R.string.call_in_calling));
+                outgoingActionContainer.setVisibility(View.VISIBLE);
+                incomingActionContainer.setVisibility(View.GONE);
+                //MediaPlayHelper.getInstance().playResMusic(R.raw.outgoing_call, 0.8f, true);
+            } else {
+                descTextView.setText(getShowName() + "\n" + StringUtil.getResString(R.string.call_incoming));
+                outgoingActionContainer.setVisibility(View.GONE);
+                incomingActionContainer.setVisibility(View.VISIBLE);
+
+                if (interactionVO != null) {
+                    String frameName;
+                    //是客户
+                    if (interactionVO.getFromClerkId() == null) {
+                        frameName = Util.INSTANCE.appendSpace(interactionVO.getFromFrameFullName().replace("-", ""));
+                    }
+                    //是同事
+                    else {
+                        frameName = interactionVO.getFromMemberName();
+                    }
+
+                    if (!TextUtils.isEmpty(frameName)) {
+                        if (Locale.CHINESE.getLanguage().equals(LocaleMangerUtils.getApplicationLocale().getLanguage())) {
+                            SpeechUtil.getInstance().startSpeak(frameName + "来电, " + frameName + "来电, " + frameName + "来电");
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private String getShowName(){
+        InteractionVO interactionVO = null;
+        if (callSingleActivity.recTcpModel.getData()!=null) {
+            interactionVO = new Gson().fromJson(callSingleActivity.recTcpModel.getData().toString(), InteractionVO.class);
+        }
+
+        String showName = "";
+        if (interactionVO==null){
+            showName = callSingleActivity.showName;
+        } else {
+            //是自己拨出
+            if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                if (interactionVO.getToClerkId() == null) {
+                    showName = interactionVO.getToFrameFullName() + " " + interactionVO.getToMemberName();
+                    portraitImageView.setImageResource(R.drawable.face_customer);
+                } else {
+                    showName = interactionVO.getToMemberName();
+                    portraitImageView.setImageResource(R.drawable.face_clerk);
+                }
+            }
+            //是他人呼入
+            else {
+                //是客户
+                if (interactionVO.getFromClerkId() == null) {
+                    if (TextUtils.isEmpty(interactionVO.getFromMemberName())) {
+                        showName = interactionVO.getFromFrameFullName();
+
+                        if (!isOutgoing) {
+                            hangupLinearLayout.setVisibility(View.VISIBLE);
+                        }
+                    } else {
+                        showName = interactionVO.getFromFrameFullName() + " " + interactionVO.getFromMemberName();
+
+                        if (!isOutgoing && Constants.Companion.getUserRoleName() != null && (Constants.Companion.getUserRoleName().contains("护士") || Constants.Companion.getUserRoleName().contains("组长"))) {
+                            transLinearLayout.setVisibility(View.VISIBLE);
+                        }
+                    }
+                    portraitImageView.setImageResource(R.drawable.face_customer);
+                }
+                //是同事
+                else {
+                    showName = interactionVO.getFromMemberName();
+                    portraitImageView.setImageResource(R.drawable.face_clerk);
+
+                    if (!isOutgoing) {
+                        hangupLinearLayout.setVisibility(View.VISIBLE);
+                    }
+                }
+            }
+        }
+        return showName;
+    }
+
+    @Override
+    public void onBackPressed() {
+        super.onDestroy();
+    }
+
+    @Override
+    public void onDestroyView() {
+        transHandler.removeCallbacksAndMessages(null);
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (callSingleActivity != null) {
+            currentState = callSingleActivity.janusClient.getCallState();
+            if (currentState == EnumType.CallState.Connected) {
+                //通话已连接上
+                showCallConnected();
+            } else if (currentState == EnumType.CallState.Connecting){
+                //通话正在连接,稍后再次检查
+                transHandler.sendEmptyMessageDelayed(CALL, 500);
+            }
+        }
+    }
+
+    @Override
+    public void didChangeState(EnumType.CallState state) {
+        Log.d(TAG, "didChangeState, state = " + state);
+        //callSingleActivity.janusClient.setCallState(state);
+        currentState = state;
+        if (state == EnumType.CallState.Connected) {
+            showCallConnected();
+        }
+    }
+
+    private void showCallConnected() {
+        if (callConnected) {
+            return;
+        }
+        callConnected = true;
+
+        MediaPlayHelper.getInstance().stopMusic(true);
+        RingPlayHelper.stopRingTone();
+        SpeechUtil.getInstance().stopSpeak();
+
+        runOnUiThread(() -> {
+            callSingleActivity.loadingDialog.dismiss();
+
+            incomingActionContainer.setVisibility(View.GONE);
+            outgoingActionContainer.setVisibility(View.VISIBLE);
+            //minimizeImageView.setVisibility(View.VISIBLE);
+            descTextView.setVisibility(View.GONE);
+
+            muteLinearLayout.setVisibility(View.VISIBLE);
+            speakerLinearLayout.setVisibility(View.VISIBLE);
+
+            isSpeakerOn = !isSpeakerOn;
+            if (WebRTCEngine.getInstance().isHeadphonesPlugged()) {
+                Log.d(TAG, "didChangeState 耳机" + isSpeakerOn);
+                WebRTCEngine.getInstance().toggleHeadset(isSpeakerOn);
+            } else {
+                Log.d(TAG, "didChangeState 外放切换" + isSpeakerOn);
+                WebRTCEngine.getInstance().toggleSpeaker(isSpeakerOn);
+                speakerImageView.setSelected(isSpeakerOn);
+            }
+
+            /*Room room = callSingleActivity.getRoom();
+            StringBuilder sb = new StringBuilder(512);
+            for (Publisher publisher : room.getPublishers()) {
+                sb.append("id:" + publisher.getId() + ",name:" + publisher.getDisplay());
+            }*/
+
+            nameTextView.setText(getShowName());
+
+            startRefreshTime();
+        });
+    }
+
+    public static final int TRANS = 0x03;
+    public static final int CHECK = 0x04;
+    public static final int CALL = 0x05;
+
+    class TransHandler extends Handler {
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            if (msg.what == TRANS){
+                if (callSingleActivity!=null && callSingleActivity.janusClient.getCallState()!= EnumType.CallState.Connected && !isHandoff){
+                    transCall();
+                }
+            } else if (msg.what == CHECK) {
+                if (callSingleActivity!=null && callSingleActivity.janusClient.getCallState()!= EnumType.CallState.Connected && !isHandoff){
+                    Toast.makeText(callSingleActivity, R.string.call_disconnect, Toast.LENGTH_LONG).show();
+                }
+            } else if (msg.what == CALL) {
+                currentState = callSingleActivity.janusClient.getCallState();
+                if (currentState == EnumType.CallState.Connected) {
+                    //通话已连接上
+                    showCallConnected();
+                    transHandler.removeMessages(CALL);
+                } else if (currentState == EnumType.CallState.Connecting){
+                    //通话正在连接,稍后再次检查
+                    transHandler.sendEmptyMessageDelayed(CALL, 500);
+                }
+            }
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    private void transCall(){
+        callSingleActivity.loadingDialog.show();
+        MediaPlayHelper.getInstance().stopMusic(true);
+        RingPlayHelper.stopRingTone();
+        SpeechUtil.getInstance().stopSpeak();
+
+        //给服务器发送转接 tcp
+        InteractionVO interactionVO = new Gson().fromJson(callSingleActivity.recTcpModel.getData().toString(), InteractionVO.class);
+        int toId;
+        if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+            toId = interactionVO.getToDeviceId();
+        } else {
+            toId = interactionVO.getFromDeviceId();
+        }
+        TcpModel voiceTransferTcpModel = VoiceUtil.voiceTransfer(Constants.Companion.getDeviceId(), toId, interactionVO);
+        NettyClient.Companion.getInstance().sendMsg(voiceTransferTcpModel.toJson()).subscribe(it-> {
+            if (it) {
+                Log.d(TAG, "TCP.发送消息完成");
+                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 500);
+            } else {
+                Log.e(TAG, "TCP.发送消息失败");
+                HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+            }
+        });
+    }
+
+    public void acceptCall(){
+        if (callSingleActivity!=null){
+            //取消自动转接
+            transHandler.removeMessages(TRANS);
+            //transHandler = null;
+            accepted = true;
+
+            callSingleActivity.loadingDialog.show();
+            callSingleActivity.janusClient.connect();
+            callSingleActivity.mVibrator.cancel();
+            MediaPlayHelper.getInstance().stopMusic(true);
+            RingPlayHelper.stopRingTone();
+            SpeechUtil.getInstance().stopSpeak();
+            //发出accept
+            InteractionVO interactionVO = new Gson().fromJson(callSingleActivity.recTcpModel.getData().toString(), InteractionVO.class);
+            int toId;
+            if (interactionVO.getFromDeviceId().equals(Constants.Companion.getDeviceId())) {
+                toId = interactionVO.getToDeviceId();
+            } else {
+                toId = interactionVO.getFromDeviceId();
+            }
+            TcpModel voiceUtilTcpModel = VoiceUtil.voiceAccept(Constants.Companion.getDeviceId(), toId, interactionVO.getId());
+            NettyClient.Companion.getInstance().sendMsg(voiceUtilTcpModel.toJson()).subscribe(it->{
+                if (it) {
+                    Log.d(TAG, "TCP.发送消息完成");
+                } else {
+                    Log.e(TAG, "TCP.发送消息失败");
+                    callSingleActivity.loadingDialog.dismiss();
+                    HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                }
+            });
+
+            transHandler.sendEmptyMessageDelayed(CHECK, 6000);
+        }
+    }
+
+    @Override
+    protected void handleHeadsetHook() {
+        //只有来电的时候才接听
+        if (!isOutgoing) {
+            if (accepted) {
+                if (callSingleActivity != null) {
+                    isHandoff = true;
+                    transHandler.removeMessages(TRANS);
+                    callSingleActivity.sendHandOffTcp();
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                }
+            } else {
+                acceptCall();
+            }
+        } else {
+            //去电时挂断
+            if (callSingleActivity != null) {
+                isHandoff = true;
+                transHandler.removeMessages(TRANS);
+                callSingleActivity.sendHandOffTcp();
+                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+            }
+        }
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        Log.i(TAG, "keyup keyCode " + keyCode);
+        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
+            handleHeadsetHook();
+        } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+            return true;
+        }
+        return true;
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        // 转接
+        if (id == R.id.incomingTransImageView){
+            if (callSingleActivity!=null) {
+                transCall();
+            }
+        }
+        // 接听
+        if (id == R.id.acceptImageView) {
+            acceptCall();
+        }
+        // 挂断电话
+        if (id == R.id.incomingHangupImageView || id == R.id.outgoingHangupImageView) {
+            if (callSingleActivity != null) {
+                isHandoff = true;
+                //取消自动转接
+                transHandler.removeMessages(TRANS);
+                callSingleActivity.sendHandOffTcp();
+                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+            }
+        }
+        // 静音
+        if (id == R.id.muteImageView) {
+            micEnabled = !micEnabled;
+            Log.d(TAG,"静音切换" + micEnabled);
+            WebRTCEngine.getInstance().muteAudio(micEnabled);
+            muteImageView.setSelected(micEnabled);
+        }
+        // 扬声器
+        if (id == R.id.speakerImageView) {
+            isSpeakerOn=!isSpeakerOn;
+            Log.d(TAG,"外放切换" + isSpeakerOn);
+            WebRTCEngine.getInstance().toggleSpeaker(isSpeakerOn);
+            speakerImageView.setSelected(isSpeakerOn);
+        }
+    }
+}

+ 286 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/FragmentVideo.java

@@ -0,0 +1,286 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.content.Context;
+import android.os.Build;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+
+import com.blankj.utilcode.util.BarUtils;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.rtc.rtc.WebRTCEngine;
+import com.wdkl.rtc.util.EnumType;
+import com.wdkl.rtc.util.OSUtils;
+
+import org.webrtc.SurfaceViewRenderer;
+
+import java.math.BigInteger;
+
+/**
+ * 视频通话控制界面
+ */
+public class FragmentVideo extends SingleCallFragment implements View.OnClickListener {
+    private static final String TAG = "FragmentVideo";
+    private ImageView outgoingAudioOnlyImageView;
+    private LinearLayout audioLayout;
+    private ImageView incomingAudioOnlyImageView;
+    private LinearLayout hangupLinearLayout;
+    private LinearLayout acceptLinearLayout;
+    private ImageView connectedAudioOnlyImageView;
+    private ImageView connectedHangupImageView;
+    private ImageView switchCameraImageView;
+    private FrameLayout fullscreenRenderer;
+    private FrameLayout pipRenderer;
+    private LinearLayout inviteeInfoContainer;
+    private SurfaceViewRenderer localSurfaceView;
+    private SurfaceViewRenderer remoteSurfaceView;
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+    }
+
+    @Override
+    int getLayout() {
+        return R.layout.fragment_video;
+    }
+
+    @Override
+    public void initView(View view) {
+        super.initView(view);
+        fullscreenRenderer = view.findViewById(R.id.fullscreen_video_view);
+        pipRenderer = view.findViewById(R.id.pip_video_view);
+        inviteeInfoContainer = view.findViewById(R.id.inviteeInfoContainer);
+        outgoingAudioOnlyImageView = view.findViewById(R.id.outgoingAudioOnlyImageView);
+        audioLayout = view.findViewById(R.id.audioLayout);
+        incomingAudioOnlyImageView = view.findViewById(R.id.incomingAudioOnlyImageView);
+        hangupLinearLayout = view.findViewById(R.id.hangupLinearLayout);
+        acceptLinearLayout = view.findViewById(R.id.acceptLinearLayout);
+        connectedAudioOnlyImageView = view.findViewById(R.id.connectedAudioOnlyImageView);
+        connectedHangupImageView = view.findViewById(R.id.connectedHangupImageView);
+        switchCameraImageView = view.findViewById(R.id.switchCameraImageView);
+        outgoingHangupImageView.setOnClickListener(this);
+        incomingHangupImageView.setOnClickListener(this);
+        minimizeImageView.setOnClickListener(this);
+        connectedHangupImageView.setOnClickListener(this);
+        acceptImageView.setOnClickListener(this);
+        switchCameraImageView.setOnClickListener(this);
+        pipRenderer.setOnClickListener(this);
+        outgoingAudioOnlyImageView.setOnClickListener(this);
+        incomingAudioOnlyImageView.setOnClickListener(this);
+        connectedAudioOnlyImageView.setOnClickListener(this);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || OSUtils.isMiui() || OSUtils.isFlyme()) {
+            lytParent.post(() -> {
+                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) inviteeInfoContainer.getLayoutParams();
+                params.topMargin = (int) (BarUtils.getStatusBarHeight() * 1.2);
+                inviteeInfoContainer.setLayoutParams(params);
+                RelativeLayout.LayoutParams params1 = (RelativeLayout.LayoutParams) minimizeImageView.getLayoutParams();
+                params1.topMargin = BarUtils.getStatusBarHeight();
+                minimizeImageView.setLayoutParams(params1);
+            });
+
+            pipRenderer.post(() -> {
+                FrameLayout.LayoutParams params2 = (FrameLayout.LayoutParams) pipRenderer.getLayoutParams();
+                params2.topMargin = (int) (BarUtils.getStatusBarHeight() * 1.2);
+                pipRenderer.setLayoutParams(params2);
+            });
+        }
+//        if(isOutgoing){ //测试崩溃对方是否会停止
+//            lytParent.postDelayed(() -> {
+//                int i = 1 / 0;
+//            }, 10000);
+//        }
+
+    }
+
+    @Override
+    public void init() {
+        super.init();
+        if (EnumType.CallState.Connected == callSingleActivity.janusClient.getCallState()) {
+            incomingActionContainer.setVisibility(View.GONE);
+            outgoingActionContainer.setVisibility(View.GONE);
+            connectedActionContainer.setVisibility(View.VISIBLE);
+            inviteeInfoContainer.setVisibility(View.GONE);
+            startRefreshTime();
+        } else {
+            if (isOutgoing) {
+                incomingActionContainer.setVisibility(View.GONE);
+                outgoingActionContainer.setVisibility(View.VISIBLE);
+                connectedActionContainer.setVisibility(View.GONE);
+                descTextView.setText(R.string.av_waiting);
+            } else {
+                incomingActionContainer.setVisibility(View.VISIBLE);
+                outgoingActionContainer.setVisibility(View.GONE);
+                connectedActionContainer.setVisibility(View.GONE);
+                descTextView.setText(R.string.av_video_invite);
+                if (callSingleActivity.janusClient.getCallState() == EnumType.CallState.Incoming) {
+                    View surfaceView = WebRTCEngine.getInstance().startPreview(false);
+                    if (surfaceView != null) {
+                        localSurfaceView = (SurfaceViewRenderer) surfaceView;
+                        localSurfaceView.setZOrderMediaOverlay(false);
+                        fullscreenRenderer.addView(localSurfaceView);
+                    }
+                }
+            }
+        }
+    }
+
+    public void didChangeState(EnumType.CallState state) {
+        Log.d(TAG, "didChangeState, state = " + state);
+        //callSingleActivity.janusClient.setCallState(state);
+        currentState = state;
+        runOnUiThread(() -> {
+            if (state == EnumType.CallState.Connected) {
+                incomingActionContainer.setVisibility(View.GONE);
+                outgoingActionContainer.setVisibility(View.GONE);
+                connectedActionContainer.setVisibility(View.VISIBLE);
+                inviteeInfoContainer.setVisibility(View.GONE);
+                descTextView.setVisibility(View.GONE);
+
+                // 开启计时器
+                startRefreshTime();
+            } else {
+                // do nothing now
+            }
+        });
+    }
+
+    public void didChangeMode(Boolean isAudio) {
+        runOnUiThread(() -> callSingleActivity.switchAudio());
+    }
+
+    @Override
+    protected void handleHeadsetHook() {
+        //
+    }
+
+    @Override
+    public void didCreateLocalVideoTrack() {
+        if (localSurfaceView == null) {
+            View surfaceView = WebRTCEngine.getInstance().startPreview(true);
+            if (surfaceView != null) {
+                localSurfaceView = (SurfaceViewRenderer) surfaceView;
+            } else {
+                if (callSingleActivity != null) callSingleActivity.finish();
+                return;
+            }
+        } else {
+            localSurfaceView.setZOrderMediaOverlay(true);
+        }
+
+        if (remoteSurfaceView!=null){
+            pipRenderer.setVisibility(View.VISIBLE);
+            if (pipRenderer.getChildCount() != 0) pipRenderer.removeAllViews();
+
+            localSurfaceView.setZOrderMediaOverlay(true);
+            pipRenderer.addView(localSurfaceView);
+        } else {
+            pipRenderer.setVisibility(View.GONE);
+            if (fullscreenRenderer != null && fullscreenRenderer.getChildCount() != 0)
+                fullscreenRenderer.removeAllViews();
+            fullscreenRenderer.addView(localSurfaceView);
+        }
+
+        Log.d(TAG,
+                "didCreateLocalVideoTrack localSurfaceView == null is " + (localSurfaceView == null) + "; remoteSurfaceView == null = " + (remoteSurfaceView == null)
+        );
+    }
+
+    public void didReceiveRemoteVideoTrack(BigInteger userId) {
+        if (localSurfaceView != null) {
+            localSurfaceView.setZOrderMediaOverlay(true);
+            if (localSurfaceView.getParent() != null) {
+                ((ViewGroup) localSurfaceView.getParent()).removeView(localSurfaceView);
+            }
+            pipRenderer.setVisibility(View.VISIBLE);
+            pipRenderer.addView(localSurfaceView);
+        }
+
+        View surfaceView = WebRTCEngine.getInstance().setupRemoteVideo(userId, false);
+        if (surfaceView != null) {
+            fullscreenRenderer.setVisibility(View.VISIBLE);
+            remoteSurfaceView = (SurfaceViewRenderer) surfaceView;
+            fullscreenRenderer.removeAllViews();
+            if (remoteSurfaceView.getParent() != null) {
+                ((ViewGroup) remoteSurfaceView.getParent()).removeView(remoteSurfaceView);
+            }
+            fullscreenRenderer.addView(remoteSurfaceView);
+        }
+
+        // 开启计时器
+        startRefreshTime();
+    }
+
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        // 接听
+        if (id == R.id.acceptImageView) {
+            //todo: 接听
+        }
+        // 挂断电话
+        if (id == R.id.incomingHangupImageView || id == R.id.outgoingHangupImageView || id == R.id.connectedHangupImageView) {
+            //todo: 挂断
+            if (callSingleActivity != null) callSingleActivity.finish();
+        }
+
+        // 切换摄像头
+        if (id == R.id.switchCameraImageView) {
+            WebRTCEngine.getInstance().switchCamera();
+        }
+        if (id == R.id.pip_video_view) {
+            boolean isFullScreenRemote = fullscreenRenderer.getChildAt(0) == remoteSurfaceView;
+            fullscreenRenderer.removeAllViews();
+            pipRenderer.removeAllViews();
+            if (isFullScreenRemote) {
+                remoteSurfaceView.setZOrderMediaOverlay(true);
+                pipRenderer.addView(remoteSurfaceView);
+                localSurfaceView.setZOrderMediaOverlay(false);
+                fullscreenRenderer.addView(localSurfaceView);
+            } else {
+                localSurfaceView.setZOrderMediaOverlay(true);
+                pipRenderer.addView(localSurfaceView);
+                remoteSurfaceView.setZOrderMediaOverlay(false);
+                fullscreenRenderer.addView(remoteSurfaceView);
+            }
+        }
+
+        // 切换到语音拨打
+        if (id == R.id.outgoingAudioOnlyImageView || id == R.id.incomingAudioOnlyImageView || id == R.id.connectedAudioOnlyImageView) {
+            if (callSingleActivity != null) callSingleActivity.isAudioOnly = true;
+            // todo: 切换纯语音
+        }
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        Log.i(TAG, "keyup keyCode " + keyCode);
+        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
+
+        } else if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
+            return true;
+        }
+        return true;
+    }
+
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        fullscreenRenderer.removeAllViews();
+        pipRenderer.removeAllViews();
+    }
+
+    @Override
+    public void onBackPressed() {
+        onDestroy();
+    }
+}

+ 579 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/ui/SingleCallFragment.java

@@ -0,0 +1,579 @@
+package com.wdkl.ncs.android.component.home.ui;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Chronometer;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.google.gson.Gson;
+import com.wdkl.ncs.android.component.home.util.HandleTcpConnect;
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper;
+import com.wdkl.ncs.android.component.home.util.RingPlayHelper;
+import com.wdkl.ncs.android.component.home.util.SpeechUtil;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.lib.vo.MessageEvent;
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO;
+import com.wdkl.ncs.android.middleware.tcp.NettyClient;
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel;
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction;
+import com.wdkl.rtc.janus.JanusClient;
+import com.wdkl.rtc.util.EnumType;
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.rtc.rtc.WebRTCEngine;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.math.BigInteger;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public abstract class SingleCallFragment extends Fragment {
+    private static final String TAG = "SingleCallFragment";
+    ImageView minimizeImageView;
+    ImageView portraitImageView;// 用户头像
+    TextView nameTextView; // 用户昵称
+    TextView descTextView;  // 状态提示用语
+    Chronometer durationTextView; // 通话时长
+
+    LinearLayout transLinearLayout;
+    LinearLayout hangupLinearLayout;
+    LinearLayout muteLinearLayout;
+    LinearLayout speakerLinearLayout;
+
+    ImageView outgoingHangupImageView;
+    ImageView incomingHangupImageView;
+    ImageView incomingTransImageView;
+    ImageView acceptImageView;
+    TextView tvStatus;
+    View outgoingActionContainer;
+    View incomingActionContainer;
+    View connectedActionContainer;
+
+    View lytParent;
+
+    boolean isOutgoing = true;
+
+    CallSingleActivity callSingleActivity;
+    CallHandler handler;
+    boolean isHandoff = false;
+    boolean callConnected = false;
+
+    boolean endWithNoAnswerFlag = false;
+    public static final int WHAT_DELAY_END_CALL = 0x01;
+    public static final int FINISH_CURRENT = 0x02;
+    public static final int WHAT_DELAY_CHECK_CALL = 0x03;
+
+    EnumType.CallState currentState;
+    HeadsetPlugReceiver headsetPlugReceiver;
+
+    private boolean callSuccess = false;
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+
+        handler = new CallHandler();
+        EventBus.getDefault().register(this);
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(getLayout(), container, false);
+        initView(view);
+        init();
+        return view;
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        if (isOutgoing) {
+            RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.ring_back2, true);
+        } else {
+            RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.ring_tone, true);
+        }
+    }
+
+    @Override
+    public void onDestroyView() {
+        if (handler != null) {
+            handler.removeCallbacksAndMessages(null);
+        }
+        if (durationTextView != null)
+            durationTextView.stop();
+        refreshMessage(true);
+        super.onDestroyView();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        EventBus.getDefault().unregister(this);
+    }
+
+    abstract int getLayout();
+
+    @Override
+    public void onAttach(@NonNull Context context) {
+        super.onAttach(context);
+        callSingleActivity = (CallSingleActivity) getActivity();
+        if (callSingleActivity != null) {
+            isOutgoing = callSingleActivity.isOutgoing();
+            headsetPlugReceiver = new HeadsetPlugReceiver();
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_HEADSET_PLUG);
+            callSingleActivity.registerReceiver(headsetPlugReceiver, filter);
+        }
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        callSingleActivity.unregisterReceiver(headsetPlugReceiver);  //注销监听
+        callSingleActivity = null;
+    }
+
+    public void initView(View view) {
+        lytParent = view.findViewById(R.id.lytParent);
+        minimizeImageView = view.findViewById(R.id.minimizeImageView);
+        portraitImageView = view.findViewById(R.id.portraitImageView);
+        nameTextView = view.findViewById(R.id.nameTextView);
+        descTextView = view.findViewById(R.id.descTextView);
+        durationTextView = view.findViewById(R.id.durationTextView);
+        outgoingHangupImageView = view.findViewById(R.id.outgoingHangupImageView);
+        incomingHangupImageView = view.findViewById(R.id.incomingHangupImageView);
+        incomingTransImageView = view.findViewById(R.id.incomingTransImageView);
+        acceptImageView = view.findViewById(R.id.acceptImageView);
+        tvStatus = view.findViewById(R.id.tvStatus);
+        outgoingActionContainer = view.findViewById(R.id.outgoingActionContainer);
+        incomingActionContainer = view.findViewById(R.id.incomingActionContainer);
+        connectedActionContainer = view.findViewById(R.id.connectedActionContainer);
+        transLinearLayout = view.findViewById(R.id.transLinearLayout);
+        hangupLinearLayout = view.findViewById(R.id.hangupLinearLayout);
+        muteLinearLayout = view.findViewById(R.id.muteLinearLayout);
+        speakerLinearLayout = view.findViewById(R.id.speakerLinearLayout);
+
+        durationTextView.setVisibility(View.GONE);
+        if (isOutgoing) {
+            //countDownTime();
+            handler.sendEmptyMessageDelayed(WHAT_DELAY_CHECK_CALL, 5000); //5s后检查是否呼叫成功
+        }
+        handler.sendEmptyMessageDelayed(WHAT_DELAY_END_CALL, 60 * 1000);//60s之后未接通,则挂断电话
+    }
+
+    public void init() {
+        if (callSingleActivity.recTcpModel.getData()!=null){
+            InteractionVO interactionVO = new Gson().fromJson(callSingleActivity.recTcpModel.getData().toString(), InteractionVO.class);
+            Constants.Companion.setInteractionId(interactionVO.getId());
+        }
+
+        if (isOutgoing) {
+            startOutCall();
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    private void startOutCall() {
+        //发送tcp call
+        NettyClient.Companion.getInstance().sendMsg(callSingleActivity.recTcpModel.toJson()).subscribe(it -> {
+            if (it) {
+                Log.d(TAG, "TCP.发送消息完成");
+            } else {
+                Log.e(TAG, "TCP.发送消息失败");
+                HandleTcpConnect.Companion.getInstance().tcpReConnectWithMsgShow();
+                if (callSingleActivity != null) {
+                    callSingleActivity.finish();
+                }
+            }
+        });
+    }
+
+    public void callOutSuccess(TcpModel tcpModel) {
+        callSingleActivity.recTcpModel = tcpModel;
+        InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
+        Constants.Companion.setInteractionId(interactionVO.getId());
+        callSuccess = true;
+        //callSingleActivity.janusClient.connect();
+        Log.d(TAG,"voice#success =>" + callSingleActivity.recTcpModel.toJson());
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Toast.makeText(SingleCallFragment.this.getContext(), R.string.call_success, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+
+    // ======================================界面回调================================
+    /*public void didCallEndWithReason(EnumType.CallEndReason callEndReason) {
+        switch (callEndReason) {
+            case Busy: {
+                tvStatus.setText("对方忙线中");
+                break;
+            }
+            case SignalError: {
+                tvStatus.setText("连接断开");
+                break;
+            }
+            case RemoteSignalError: {
+                tvStatus.setText("对方网络断开");
+                break;
+            }
+            case Hangup: {
+                tvStatus.setText("挂断");
+                break;
+            }
+            case MediaError: {
+                tvStatus.setText("媒体错误");
+                break;
+            }
+            case RemoteHangup: {
+                tvStatus.setText("对方挂断");
+                break;
+            }
+            case OpenCameraFailure: {
+                tvStatus.setText("打开摄像头错误");
+                break;
+            }
+            case Timeout: {
+                tvStatus.setText("对方未接听");
+                break;
+            }
+            case AcceptByOtherClient: {
+                tvStatus.setText("在其它设备接听");
+                break;
+            }
+        }
+        incomingActionContainer.setVisibility(View.GONE);
+        outgoingActionContainer.setVisibility(View.GONE);
+        if (connectedActionContainer != null)
+            connectedActionContainer.setVisibility(View.GONE);
+        refreshMessage(false);
+        new Handler(Looper.getMainLooper()).postDelayed(() -> {
+            if (callSingleActivity != null) {
+                callSingleActivity.finish();
+            }
+
+        }, 11);
+    }*/
+
+    public void didChangeState(EnumType.CallState state) {
+
+    }
+
+    public void didCreateLocalVideoTrack() {
+    }
+
+    public void didReceiveRemoteVideoTrack(BigInteger userId) {
+    }
+
+    private void refreshMessage(Boolean isForCallTime) {
+        if (callSingleActivity == null) {
+            return;
+        }
+        // 刷新消息; demo中没有消息,不用处理这儿快逻辑
+    }
+
+    public void startRefreshTime() {
+        if (durationTextView != null) {
+            leftTime = 0;
+            durationTextView.setOnChronometerTickListener(null);
+            durationTextView.stop();
+            durationTextView.setVisibility(View.VISIBLE);
+            durationTextView.setBase(SystemClock.elapsedRealtime());
+            durationTextView.start();
+        }
+    }
+
+    private long leftTime = 60;
+    public void countDownTime() {
+        if (durationTextView != null) {
+            durationTextView.setVisibility(View.VISIBLE);
+            durationTextView.setBase(SystemClock.elapsedRealtime());
+            durationTextView.start();
+            durationTextView.setOnChronometerTickListener(
+                    new Chronometer.OnChronometerTickListener() {
+                        @Override
+                        public void onChronometerTick(Chronometer chronometer) {
+                            leftTime--;
+                            Date d = new Date(leftTime * 1000);
+                            SimpleDateFormat timeFormat = new SimpleDateFormat("mm:ss");
+                            chronometer.setText(timeFormat.format(d));
+                            if (leftTime == 0) {
+                                durationTextView.stop();
+                            }
+                        }
+                    });
+        }
+    }
+
+    void runOnUiThread(Runnable runnable) {
+        if (callSingleActivity != null) {
+            callSingleActivity.runOnUiThread(runnable);
+        }
+    }
+
+    @SuppressLint("HandlerLeak")
+    class CallHandler extends Handler {
+        @Override
+        public void handleMessage(@NonNull Message msg) {
+            switch (msg.what) {
+                case WHAT_DELAY_END_CALL:
+                    if (currentState != EnumType.CallState.Connected) {
+                        endWithNoAnswerFlag = true;
+                        if (callSingleActivity != null) {
+                            callSingleActivity.sendHandOffTcp();
+                            callSingleActivity.finish();
+                        }
+                    }
+                    break;
+                case WHAT_DELAY_CHECK_CALL:
+                    if (callSingleActivity != null && !callSuccess) {
+                        Toast.makeText(SingleCallFragment.this.getContext(), R.string.net_error, Toast.LENGTH_SHORT).show();
+                    }
+                    break;
+                case FINISH_CURRENT:
+                    if (callSingleActivity != null) {
+                        callSingleActivity.finish();
+                    }
+                    break;
+            }
+        }
+    }
+
+    public abstract void onBackPressed();
+
+    public abstract boolean onKeyUp(int keyCode, KeyEvent event);
+
+    protected abstract void handleHeadsetHook();
+
+    class HeadsetPlugReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.hasExtra("state")) {
+                if (intent.getIntExtra("state", 0) == 0) { //拔出耳机
+                    WebRTCEngine.getInstance().toggleHeadset(false);
+                } else if (intent.getIntExtra("state", 0) == 1) { //插入耳机
+                    WebRTCEngine.getInstance().toggleHeadset(true);
+                }
+            }
+        }
+    }
+
+    private int reJoinCount = 0;
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void onEvent(MessageEvent messageEvent) {
+        int code = messageEvent.getTag();
+        if (code == JanusClient.CALLBACK_ADD_LOCALSTREAM) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    callSingleActivity.getCurrentFragment().didCreateLocalVideoTrack();
+                }
+            });
+        } else if (code == JanusClient.CALLBACK_ADD_REMOTESTREAM) {
+            BigInteger remoteUserId = (BigInteger) messageEvent.getMessage();
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    callSingleActivity.getCurrentFragment().didReceiveRemoteVideoTrack(remoteUserId);
+                }
+            });
+        } else if (code == JanusClient.CALLBACK_HANDUP) {
+            if (isHandoff) {
+                return;
+            }
+            BigInteger handleId = (BigInteger)messageEvent.getMessage();
+            if (!handleId.equals(callSingleActivity.janusClient.getCurrentHandleId())){
+                runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        Toast.makeText(SingleCallFragment.this.getContext(),R.string.call_end,Toast.LENGTH_SHORT).show();
+                    }
+                });
+            }
+
+            handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+        } else if (code == JanusClient.CALLBACK_ROOM_ERROR) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    Toast.makeText(SingleCallFragment.this.getContext(), "Room error", Toast.LENGTH_SHORT).show();
+                }
+            });
+
+            handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+        } else if (code == JanusClient.CALLBACK_CONNECTED) {
+            //通话连接建立,停止铃声
+            MediaPlayHelper.getInstance().stopMusic(true);
+            RingPlayHelper.stopRingTone();
+            SpeechUtil.getInstance().stopSpeak();
+            didChangeState(EnumType.CallState.Connected);
+        } else if (code == JanusClient.CALLBACK_CONNECTED_TIMEOUT) {
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    Toast.makeText(SingleCallFragment.this.getContext(), "Connect timeout", Toast.LENGTH_SHORT).show();
+                }
+            });
+
+            handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+        } else if (code == JanusClient.CALLBACK_LEAVING) {
+            if (isHandoff) {
+                return;
+            }
+            if (isOutgoing){
+                handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+            } else {
+                if (reJoinCount>=3){
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(SingleCallFragment.this.getContext(), "Connect timeout", Toast.LENGTH_SHORT).show();
+                        }
+                    });
+
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                } else {
+                    //未收到挂断或主动挂断 重新进入
+                    callSingleActivity.janusClient.connect();
+                    reJoinCount++;
+                }
+            }
+            //BigInteger remoteUserId = (BigInteger)messageEvent.getMessage();
+
+        } else if (code == JanusClient.CALLBACK_REMOTE_QUIT) {
+            if (isHandoff) {
+                return;
+            }
+            //BigInteger remoteUserId = (BigInteger)messageEvent.getMessage();
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    Toast.makeText(SingleCallFragment.this.getContext(), R.string.call_end, Toast.LENGTH_SHORT).show();
+                }
+            });
+
+            handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+        }
+        else if (code == JanusClient.CALLBACK_SLOW_LINK){
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    Toast.makeText(SingleCallFragment.this.getContext(), R.string.net_error, Toast.LENGTH_SHORT).show();
+                }
+            });
+        }
+        //TCP处理
+        else if (code == 2) {
+
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            Log.i(TAG, "收到数据222: " + tcpModel.toJson());
+            int curInteractionId = -1;
+            if (tcpModel.getData() != null) {
+                InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);
+                curInteractionId = interactionVO.getId();
+            }
+
+            if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    MediaPlayHelper.getInstance().stopMusic(true);
+                    RingPlayHelper.stopRingTone();
+                    SpeechUtil.getInstance().stopSpeak();
+                    callSingleActivity.recTcpModel = tcpModel;
+                    Log.d(TAG, "voice#success =>" + callSingleActivity.recTcpModel.toJson());
+                    Log.i(TAG, "对方接听电话啦");
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF) { //对方挂断
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    isHandoff = true;
+                    callSingleActivity.janusClient.disConnect();
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(SingleCallFragment.this.getContext(), R.string.call_end, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.REJECT) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(SingleCallFragment.this.getContext(), R.string.call_reject, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CALLING) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(SingleCallFragment.this.getContext(), R.string.call_busy, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                }
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.FAILED) {
+                //if (curInteractionId == interactionId) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 1000);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(SingleCallFragment.this.getContext(), R.string.call_failed, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                //}
+            } else if (tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
+                if (curInteractionId == Constants.Companion.getInteractionId()) {
+                    handler.sendEmptyMessageDelayed(FINISH_CURRENT, 100);
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            Toast.makeText(SingleCallFragment.this.getContext(), R.string.str_cancel, Toast.LENGTH_SHORT).show();
+                        }
+                    });
+                }
+            }
+        } else if (code == 1) {
+            TcpModel tcpModel = (TcpModel) messageEvent.getMessage();
+            Log.i(TAG, "收到数据111: " + tcpModel.toJson());
+            if (isOutgoing && tcpModel.getAction() == TcpAction.VoiceAction.SUCCESS){ //服务器返回成功
+                callOutSuccess(tcpModel);
+            }
+        } else if (code == Constants.EVENT_HEADSET_HOOK) {
+            //收到耳机按键
+            handleHeadsetHook();
+        }
+    }
+}

+ 15 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/ActivityStackUtil.java

@@ -0,0 +1,15 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import com.wdkl.ncs.android.component.home.ui.CallSingleActivity;
+
+public class ActivityStackUtil {
+    private static CallSingleActivity callSingleActivity = null;
+
+    public static void setCallSingleActivity(CallSingleActivity activity) {
+        callSingleActivity = activity;
+    }
+
+    public static CallSingleActivity getCallSingleActivity() {
+        return callSingleActivity;
+    }
+}

+ 234 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/AnrFcExceptionUtil.java

@@ -0,0 +1,234 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.app.AlarmManager;
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Environment;
+import android.util.Log;
+
+import com.github.anrwatchdog.ANRError;
+import com.github.anrwatchdog.ANRWatchDog;
+import com.wdkl.ncs.android.component.home.activity.WatchHome2Activity;
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.api.UrlManager;
+import com.wdkl.ncs.android.middleware.utils.CommonUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+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;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.FormBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * 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 = "AnrFcExceptionUtil";
+    private static Application application;
+
+    private static AnrFcExceptionUtil mAnrFcExceptionUtil;
+
+    private OkHttpClient okHttpClient;
+    private UrlManager urlManager = UrlManager.Companion.build();
+
+    /**
+     * 存储异常和参数信息
+     */
+    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);
+            }
+
+            restartApp();
+        }
+    }
+
+    private void restartApp() {
+        //停止服务
+        Intent serviceIntent = new Intent(application.getApplicationContext(), WdKeepAliveService.class);
+        application.getApplicationContext().stopService(serviceIntent);
+
+        //重新启动app
+        Intent mStartActivity = new Intent(application.getApplicationContext(), WatchHome2Activity.class);
+        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());
+        System.exit(0);
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回false.
+     */
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+
+        saveCrashInfo2File(ex);
+
+        return true;
+    }
+
+    private void uploadingErrorLog(String class_name, String err_msg, String exception_name, String method_name, String stack_trace) {
+        if(okHttpClient == null){
+            okHttpClient = new OkHttpClient();
+        }
+
+        FormBody.Builder formBody = new FormBody.Builder();
+        formBody.add("device_id", ""+Constants.Companion.getDeviceId());
+        formBody.add("class_name",class_name);
+        formBody.add("method_name",method_name);
+        formBody.add("exception_name",exception_name);
+        formBody.add("err_msg",err_msg);
+        formBody.add("stack_trace",stack_trace);
+        Log.w(TAG,urlManager.getDevice_url());
+        Request request  = new Request.Builder()
+                .url(urlManager.getDevice_url() + "device/error_log")
+                .post(formBody.build())
+                .build();
+
+        okHttpClient.newCall(request).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                Log.e(TAG,"错误日志上传失败"+e.getMessage());
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws IOException {
+                Log.d(TAG,"错误日志上传成功");
+                String data = response.body().string();
+                Log.d(TAG,"错误日志数据 data "+data);
+            }
+        });
+    }
+
+    /**
+     * 保存错误信息到文件中
+     *
+     * @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);
+
+        //上传错误日志
+        uploadingErrorLog(application.getPackageName(), ex.getMessage(), ex.getClass().getSimpleName(), ex.getStackTrace()[0].getMethodName(), sb.toString());
+
+        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 = CommonUtils.getRootFilePath() + "/crash/";
+                File dir = new File(path);
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+                FileOutputStream fos = new FileOutputStream(path + fileName);
+                fos.write(sb.toString().getBytes());
+                Log.e(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) {
+                //DaemonEnv.sendStopWorkBroadcast(application.getBaseContext());
+                //NettyClient.Companion.getInstance().disConnect();
+                Log.e("anr", "Anr restart app..." + error.toString());
+                //AppUpdateHelper.reboot(application);
+
+                restartApp();
+            }
+        }).start();
+
+    }
+}

+ 76 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/AppUtils.java

@@ -0,0 +1,76 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.annotation.SuppressLint;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.wdkl.ncs.android.component.home.activity.WatchHome2Activity;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.keepbackground.work.DaemonEnv;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class AppUtils {
+
+    public static void restartApp() {
+        //DaemonEnv.sendStopWorkBroadcast(BaseApplication.appContext);
+        try {
+            Log.i("AppUtil", "restartApp ======================");
+            Intent intent = new Intent(BaseApplication.appContext, WatchHome2Activity.class);
+            @SuppressLint("WrongConstant") PendingIntent mPendingIntent = PendingIntent.getActivity(
+                    BaseApplication.appContext, 0, intent,
+                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+            AlarmManager mgr = (AlarmManager) BaseApplication.appContext.getSystemService(Context.ALARM_SERVICE);
+            mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1500, mPendingIntent);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        android.os.Process.killProcess(android.os.Process.myPid());
+        System.exit(0);
+    }
+
+    /**
+     * 提取手机号码
+     * 移动号段:   134 135 136 137 138 139 147 148 150 151 152 157 158 159  165 172 178 182 183 184 187 188 198
+     * 联通号段:   130 131 132 145 146 155 156 166 170 171 175 176 185 186
+     * 电信号段:   133 149 153 170 173 174 177 180 181 189  191  199
+     * 虚拟运营商: 170
+     */
+    public static String getMobileNumber(String str, String reg){
+        if (TextUtils.isEmpty(str)) {
+            return "";
+        }
+
+        // 将给定的正则表达式编译到模式中
+        Pattern pattern = Pattern.compile(reg);
+        // 创建匹配给定输入与此模式的匹配器。
+        Matcher matcher = pattern.matcher(str);
+        //查找字符串中是否有符合的子字符串
+        if(matcher.find()){
+            //查找到符合的即输出
+            //Log.e("sms", "getMobileNumber: " + matcher.group());
+            return matcher.group();
+        }
+
+        return "";
+    }
+
+    public static boolean checkMobileNumber(String str, String reg){
+        if (TextUtils.isEmpty(str)) {
+            return false;
+        }
+
+        // 将给定的正则表达式编译到模式中
+        Pattern pattern = Pattern.compile(reg);
+        // 创建匹配给定输入与此模式的匹配器。
+        Matcher matcher = pattern.matcher(str);
+        //查找字符串中是否有符合的子字符串
+        return matcher.find();
+    }
+}

+ 187 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/AsyncPlayer.java

@@ -0,0 +1,187 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.content.Context;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.LinkedList;
+
+/**
+ * 响铃相关类
+ */
+public class AsyncPlayer {
+    private static final int PLAY = 1;
+    private static final int STOP = 2;
+    private AudioManager audioManager;
+
+    private static final class Command {
+        int code;
+        Context context;
+        int resId;
+        boolean looping;
+        int stream;
+        long requestTime;
+
+        public String toString() {
+            return "{ code=" + code + " looping=" + looping + " stream=" + stream + " resId=" + resId + " }";
+        }
+    }
+
+    private final LinkedList mCmdQueue = new LinkedList();
+
+    private void startSound(Command cmd) {
+
+        try {
+            //MediaPlayer player = new MediaPlayer();
+            MediaPlayer player = MediaPlayer.create(cmd.context, cmd.resId);
+            player.setAudioStreamType(cmd.stream);
+            //player.setDataSource(cmd.context, cmd.uri);
+            player.setLooping(cmd.looping);
+            player.setVolume(1.0f, 1.0f);
+            //player.prepare();
+            player.start();
+            if (mPlayer != null) {
+                mPlayer.release();
+            }
+            mPlayer = player;
+            Log.w(mTag, "start sound " + cmd.resId);
+        } catch (Exception e) {
+            Log.w(mTag, "error loading sound for " + cmd.resId, e);
+        }
+    }
+
+    private final class Thread extends java.lang.Thread {
+        Thread() {
+            super("AsyncPlayer-" + mTag);
+        }
+
+        public void run() {
+            while (true) {
+                Command cmd = null;
+
+                synchronized (mCmdQueue) {
+
+                    cmd = (Command) mCmdQueue.removeFirst();
+                }
+
+                switch (cmd.code) {
+                    case PLAY:
+                        startSound(cmd);
+                        break;
+                    case STOP:
+
+                        if (mPlayer != null) {
+                            mPlayer.stop();
+                            mPlayer.reset();
+                            mPlayer.release();
+                            mPlayer = null;
+                        } else {
+                            Log.w(mTag, "STOP command without a player");
+                        }
+                        break;
+                }
+
+                synchronized (mCmdQueue) {
+                    if (mCmdQueue.size() == 0) {
+
+                        mThread = null;
+                        releaseWakeLock();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    private String mTag;
+    private Thread mThread;
+    private MediaPlayer mPlayer;
+    private PowerManager.WakeLock mWakeLock;
+
+    private int mState = STOP;
+
+    public AsyncPlayer(String tag) {
+        if (tag != null) {
+            mTag = tag;
+        } else {
+            mTag = "AsyncPlayer";
+        }
+    }
+
+    public void play(Context context, int res, boolean looping, int stream) {
+        Command cmd = new Command();
+        cmd.requestTime = SystemClock.uptimeMillis();
+        cmd.code = PLAY;
+        cmd.context = context;
+        cmd.resId = res;
+        cmd.looping = looping;
+        cmd.stream = stream;
+        synchronized (mCmdQueue) {
+            enqueueLocked(cmd);
+            mState = PLAY;
+        }
+    }
+
+    public void stop() {
+        synchronized (mCmdQueue) {
+            if (mState != STOP) {
+                Command cmd = new Command();
+                cmd.requestTime = SystemClock.uptimeMillis();
+                cmd.code = STOP;
+                enqueueLocked(cmd);
+                mState = STOP;
+            }
+        }
+    }
+
+    private void enqueueLocked(Command cmd) {
+        mCmdQueue.add(cmd);
+        if (mThread == null) {
+            acquireWakeLock();
+            mThread = new Thread();
+            mThread.start();
+        }
+    }
+
+    public void setUsesWakeLock(Context context) {
+        if (mWakeLock != null || mThread != null) {
+            throw new RuntimeException("assertion failed mWakeLock=" + mWakeLock + " mThread=" + mThread);
+        }
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mTag);
+    }
+
+    private void acquireWakeLock() {
+        if (mWakeLock != null) {
+            mWakeLock.acquire();
+        }
+    }
+
+    private void releaseWakeLock() {
+        if (mWakeLock != null) {
+            mWakeLock.release();
+        }
+    }
+
+    private boolean isHeadphonesPlugged(Context context) {
+        if (audioManager == null) {
+            audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        }
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+            AudioDeviceInfo[] audioDevices = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+            for (AudioDeviceInfo deviceInfo : audioDevices) {
+                if (deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
+                        || deviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
+                    return true;
+                }
+            }
+            return false;
+        } else {
+            return audioManager.isWiredHeadsetOn();
+        }
+    }
+}

+ 257 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/DeviceIdUtils.java

@@ -0,0 +1,257 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.annotation.SuppressLint;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.wdkl.ncs.keepbackground.utils.RandomUtil;
+
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+
+/**
+ * jason
+ */
+public final class DeviceIdUtils {
+    private static final String TAG = DeviceIdUtils.class.getSimpleName();
+
+    private static final String TEMP_DIR = "system_config";
+    private static final String TEMP_FILE_NAME = "system_file";
+    private static final String TEMP_FILE_NAME_MIME_TYPE = "application/octet-stream";
+    private static final String SP_NAME = "device_info";
+    private static final String SP_KEY_DEVICE_ID = "device_id";
+
+    public static String getDeviceId(Context context) {
+        /*SharedPreferences sharedPreferences = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
+        String deviceId = sharedPreferences.getString(SP_KEY_DEVICE_ID, null);
+        if (!TextUtils.isEmpty(deviceId)) {
+            return deviceId;
+        }*/
+
+        String deviceId = getIMEI(context);
+        if (TextUtils.isEmpty(deviceId)) {
+            deviceId = createUUID(context);
+        }
+
+        /*sharedPreferences.edit()
+                .putString(SP_KEY_DEVICE_ID, deviceId)
+                .apply();*/
+        return deviceId;
+    }
+
+    private static String createUUID(Context context) {
+        //String uuid = UUID.randomUUID().toString().replace("-", "");
+        String uuid = RandomUtil.getRandomNumbersAndLetters(16);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            Uri externalContentUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
+            ContentResolver contentResolver = context.getContentResolver();
+            String[] projection = new String[]{
+                    MediaStore.Downloads._ID
+            };
+            String selection = MediaStore.Downloads.TITLE + "=?";
+            String[] args = new String[]{
+                    TEMP_FILE_NAME
+            };
+            Cursor query = contentResolver.query(externalContentUri, projection, selection, args, null);
+            if (query != null && query.moveToFirst()) {
+                Uri uri = ContentUris.withAppendedId(externalContentUri, query.getLong(0));
+                query.close();
+
+                InputStream inputStream = null;
+                BufferedReader bufferedReader = null;
+                try {
+                    inputStream = contentResolver.openInputStream(uri);
+                    if (inputStream != null) {
+                        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
+                        uuid = bufferedReader.readLine();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    if (bufferedReader != null) {
+                        try {
+                            bufferedReader.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                    if (inputStream != null) {
+                        try {
+                            inputStream.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            } else {
+                ContentValues contentValues = new ContentValues();
+                contentValues.put(MediaStore.Downloads.TITLE, TEMP_FILE_NAME);
+                contentValues.put(MediaStore.Downloads.MIME_TYPE, TEMP_FILE_NAME_MIME_TYPE);
+                contentValues.put(MediaStore.Downloads.DISPLAY_NAME, TEMP_FILE_NAME);
+                contentValues.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + File.separator + TEMP_DIR);
+
+                Uri insert = contentResolver.insert(externalContentUri, contentValues);
+                if (insert != null) {
+                    OutputStream outputStream = null;
+                    try {
+                        outputStream = contentResolver.openOutputStream(insert);
+                        if (outputStream == null) {
+                            return uuid;
+                        }
+                        outputStream.write(uuid.getBytes());
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        if (outputStream != null) {
+                            try {
+                                outputStream.close();
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+            }
+        } else {
+            File externalDownloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
+            File applicationFileDir = new File(externalDownloadsDir, TEMP_DIR);
+            if (!applicationFileDir.exists()) {
+                if (!applicationFileDir.mkdirs()) {
+                    Log.e(TAG, "文件夹创建失败: " + applicationFileDir.getPath());
+                }
+            }
+            File file = new File(applicationFileDir, TEMP_FILE_NAME);
+            if (!file.exists()) {
+                FileWriter fileWriter = null;
+                try {
+                    if (file.createNewFile()) {
+                        fileWriter = new FileWriter(file, false);
+                        fileWriter.write(uuid);
+                    } else {
+                        Log.e(TAG, "文件创建失败:" + file.getPath());
+                    }
+                } catch (IOException e) {
+                    Log.e(TAG, "文件创建失败:" + file.getPath());
+                    e.printStackTrace();
+                } finally {
+                    if (fileWriter != null) {
+                        try {
+                            fileWriter.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            } else {
+                FileReader fileReader = null;
+                BufferedReader bufferedReader = null;
+                try {
+                    fileReader = new FileReader(file);
+                    bufferedReader = new BufferedReader(fileReader);
+                    uuid = bufferedReader.readLine();
+
+                    bufferedReader.close();
+                    fileReader.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                } finally {
+                    if (bufferedReader != null) {
+                        try {
+                            bufferedReader.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+
+                    if (fileReader != null) {
+                        try {
+                            fileReader.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            }
+        }
+
+        return uuid;
+    }
+
+    private static String getIMEI(Context context) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+            return null;
+        }
+        try {
+            TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+            if (telephonyManager == null) {
+                return null;
+            }
+            @SuppressLint({"MissingPermission", "HardwareIds"}) String imei = telephonyManager.getDeviceId();
+            return imei;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public static String execRootCmd(String cmd) {
+        String result = "";
+        DataOutputStream dos = null;
+        DataInputStream dis = null;
+        try {
+            Process p = Runtime.getRuntime().exec("su");// 经过Root处理的android系统即有su命令
+            dos = new DataOutputStream(p.getOutputStream());
+            dis = new DataInputStream(p.getInputStream());
+            Log.i(TAG, cmd);
+            dos.writeBytes(cmd + "\n");
+            dos.flush();
+            dos.writeBytes("exit\n");
+            dos.flush();
+            String line;
+            BufferedReader br = new BufferedReader(new InputStreamReader(dis));
+            while ((line = br.readLine()) != null) {
+                Log.d(TAG, "result:" + line);
+                result += line;
+            }
+            p.waitFor();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (dos != null) {
+                try {
+                    dos.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+
+            if (dis != null) {
+                try {
+                    dis.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        return result;
+    }
+}

+ 14 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/EthernetWifiCallBack.java

@@ -0,0 +1,14 @@
+package com.wdkl.ncs.android.component.home.util;
+
+/**
+ * ======================以太网和wifi状态接口=====================
+ * Created by dengzhe on 2018/2/7.
+ */
+
+public interface EthernetWifiCallBack {
+
+    boolean ethernetStatus(boolean status);//以太网状态
+
+    boolean wifiStatus(boolean status);//wifi状态
+
+}

+ 64 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/HandleTcpConnect.kt

@@ -0,0 +1,64 @@
+package com.wdkl.ncs.android.component.home.util
+
+import android.annotation.SuppressLint
+import android.util.Log
+import com.wdkl.ncs.android.middleware.common.Constants
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.lib.vo.MessageEvent
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+import org.greenrobot.eventbus.EventBus
+
+class HandleTcpConnect {
+    val TAG = HandleTcpConnect::class.simpleName
+
+    /*伴生对象*/
+    companion object {
+        var instance = HandleTcpConnect()
+    }
+
+    @SuppressLint("CheckResult")
+    fun tcpInitConnect(){
+        Log.d(TAG, "TCP.开始初始连接")
+        NettyClient.instance.connect(Constants.tcpServer, Constants.tcpPort, Constants.heartBeat.toLong()).subscribe {
+            if (it){
+                Log.d(TAG, "TCP.连接成功")
+                EventBus.getDefault().post(MessageEvent("tcp connected", Constants.EVENT_TCP_CONNECTED))
+            } else {
+                Log.e(TAG, "TCP.连接失败")
+                EventBus.getDefault().post(MessageEvent("tcp connected", Constants.EVENT_TCP_BREAK))
+            }
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    fun tcpReConnect(){
+        Log.d(TAG, "TCP.开始重新连接")
+        NettyClient.instance.reConnect().doOnError {
+            it.message?.let { it1 -> Log.e(TAG, it1) }
+        }.subscribe {
+            if (it){
+                Log.d(TAG, "TCP.重连成功")
+            } else {
+                Log.e(TAG, "TCP.重连失败")
+            }
+        }
+    }
+
+    @SuppressLint("CheckResult")
+    fun tcpReConnectWithMsgShow(){
+        Log.d(TAG, "TCP.开始重新连接")
+        //showMessage("服务连接失败,正在重连")
+        NettyClient.instance.reConnect().doOnError {
+            it.message?.let { it1 -> Log.e(TAG, it1) }
+        }.subscribe{
+            if (it){
+                Log.d(TAG, "服务重连成功")
+                //EventBus.getDefault().post(MessageEvent("tcp connected", Constants.EVENT_TCP_CONNECTED))
+                //showMessage("重连成功")
+            } else {
+                Log.d(TAG, "服务重连失败")
+                //showMessage("重连失败,请尝试重启APP")
+            }
+        }
+    }
+}

+ 90 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/HomeWatcher.java

@@ -0,0 +1,90 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+public class HomeWatcher {
+    private static final String TAG = "HomeWatcher";
+
+    public interface OnHomePressedListener {
+        void onHomePressed();
+        void onRecentAppPressed();
+    }
+
+    /** 上下文 */
+    private Context mContext;
+    /** 过滤器 */
+    private IntentFilter mFilter;
+    /** 接口 */
+    private OnHomePressedListener mListener;
+    /** 广播接收者 */
+    private HomeReceiver mRecevier;
+
+    public HomeWatcher(Context context) {
+        mContext = context;
+        mRecevier = new HomeReceiver();
+        mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+    }
+
+    /**
+     * 设置监听
+     *
+     * @param listener
+     */
+    public void setOnHomePressedListener(OnHomePressedListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * 开始监听,注册广播
+     */
+
+    public void startWatch() {
+        if (mRecevier != null) {
+            mContext.registerReceiver(mRecevier, mFilter);
+        }
+    }
+
+    /**
+     * 停止监听,注销广播
+     */
+    public void stopWatch() {
+        if (mRecevier != null) {
+            mContext.unregisterReceiver(mRecevier);
+        }
+    }
+
+    /**
+     * 广播接收者
+     */
+    private class HomeReceiver extends BroadcastReceiver {
+        final String SYSTEM_DIALOG_REASON_KEY = "reason";
+        final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
+        final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+        final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
+        final String SYSTEM_DIALOG_REASON_FS_GESTURE = "fs_gesture";  //小米手势
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
+                String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
+                if (reason != null) {
+                    Log.i(TAG, "action:" + action + ",reason:" + reason);
+                    if (mListener != null) {
+                        if (reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY) || reason.equals(SYSTEM_DIALOG_REASON_FS_GESTURE)) {
+                            //home按键
+                            mListener.onHomePressed();
+                        } else if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
+                            //recent按键
+                            mListener.onRecentAppPressed();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 63 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/IMDialogHelper.java

@@ -0,0 +1,63 @@
+package com.wdkl.ncs.android.component.home.util;
+
+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.ncs.android.component.home.R;
+
+
+public class IMDialogHelper {
+
+    private static AlertDialog imDialog;
+
+    public static void showIMDialog(Activity activity) {
+        if (imDialog != null && imDialog.isShowing()) {
+            return;
+        }
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.im_dialog_lay, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+
+        Button confirm = contentView.findViewById(R.id.btn_im_confirm);
+        confirm.setOnClickListener(v -> {
+            RecordHelper.getInstance().stopCancelRecordByOther(false);
+            dismissIMDialog();
+        });
+
+
+        imDialog = builder.create();
+        imDialog.setCanceledOnTouchOutside(false);
+        imDialog.setCancelable(false);
+
+        //设置dialog宽高及位置
+        try {
+            Window window = imDialog.getWindow();
+            window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+            imDialog.show();
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.gravity = Gravity.CENTER;
+            //lp.alpha = 0.8f;//设置透明度
+            window.setAttributes(lp);
+
+            window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void dismissIMDialog() {
+        if (imDialog != null) {
+            imDialog.dismiss();
+            imDialog = null;
+        }
+    }
+}

+ 6 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/ISpeechCallback.java

@@ -0,0 +1,6 @@
+package com.wdkl.ncs.android.component.home.util;
+
+public interface ISpeechCallback {
+    void initSuccess();
+    void initFailed();
+}

+ 82 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/ImPlayDialogHelper.java

@@ -0,0 +1,82 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.SystemClock;
+import android.util.Log;
+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 android.widget.Chronometer;
+import android.widget.TextView;
+
+import com.wdkl.ncs.android.component.home.R;
+
+public class ImPlayDialogHelper {
+
+    private static AlertDialog imPlayDialog;
+
+    public static void showImPlayDialog(Activity activity, String text, String time) {
+        if (imPlayDialog != null && imPlayDialog.isShowing()) {
+            return;
+        }
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.im_play_dialog_lay, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+
+        Button confirm = contentView.findViewById(R.id.btn_im_stop);
+        confirm.setOnClickListener(v -> {
+            MediaPlayHelper.getInstance().stopMusic(false);
+            dismissIMDialog();
+        });
+        TextView textView = contentView.findViewById(R.id.tv_im_play_name);
+        textView.setText(text);
+        TextView timeView = contentView.findViewById(R.id.tv_im_time);
+        timeView.setText(time);
+        Chronometer chronometer = contentView.findViewById(R.id.im_play_time);
+        chronometer.setBase(SystemClock.elapsedRealtime());
+        chronometer.start();
+
+
+        imPlayDialog = builder.create();
+        imPlayDialog.setCanceledOnTouchOutside(false);
+        imPlayDialog.setCancelable(false);
+        imPlayDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+            @Override
+            public void onDismiss(DialogInterface dialog) {
+                if (chronometer != null) {
+                    chronometer.stop();
+                }
+            }
+        });
+
+        //设置dialog宽高及位置
+        try {
+            Window window = imPlayDialog.getWindow();
+            window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+            imPlayDialog.show();
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.gravity = Gravity.CENTER;
+            //lp.alpha = 0.8f;//设置透明度
+            window.setAttributes(lp);
+
+            window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void dismissIMDialog() {
+        if (imPlayDialog != null) {
+            imPlayDialog.dismiss();
+            imPlayDialog = null;
+        }
+    }
+}

+ 140 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/LanguageSetDialogHelper.java

@@ -0,0 +1,140 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+
+
+public class LanguageSetDialogHelper {
+
+    private static AlertDialog dialog;
+    private static int selectIndex;
+
+    public static void showDialog(final Activity activity) {
+        if (dialog != null && dialog.isShowing()) {
+            return;
+        }
+
+        final int originIndex = LocaleMangerUtils.getCurrentLocaleIndex();
+        selectIndex = originIndex;
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.language_set_dialog, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+
+        RadioButton btnYes = contentView.findViewById(R.id.rb_language_yes);
+        RadioButton btnNo = contentView.findViewById(R.id.rb_language_no);
+        final int mode = SettingConfig.getLanguageMode(activity);
+        if (mode == 0) {
+            btnYes.setChecked(true);
+        } else {
+            btnNo.setChecked(true);
+        }
+
+        Button buttonCancel = contentView.findViewById(R.id.cancel_button);
+        Button buttonConfirm = contentView.findViewById(R.id.confirm_button);
+        Spinner spinner = contentView.findViewById(R.id.spinner_language_select);
+        ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(activity,
+                R.array.language_list, R.layout.spinner_item);
+        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spinner.setAdapter(adapter);
+        spinner.setSelection(originIndex);
+        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                Log.d("languageId", "pos: " + position + ", originIndex: " + originIndex);
+                selectIndex = position;
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+
+            }
+        });
+
+        RadioGroup languageGroup = contentView.findViewById(R.id.group_language_mode);
+        languageGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
+            @Override
+            public void onCheckedChanged(RadioGroup group, int checkedId) {
+                if (checkedId == R.id.rb_language_yes) {
+                    SettingConfig.setLanguageMode(activity, 0);
+                } else {
+                    SettingConfig.setLanguageMode(activity, 1);
+                }
+            }
+        });
+
+        buttonCancel.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (dialog != null) {
+                    dialog.dismiss();
+                }
+            }
+        });
+
+        buttonConfirm.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                SettingConfig.setLanguageId(activity, selectIndex);
+
+                if (SettingConfig.getLanguageMode(activity) == 1) {
+                    if (selectIndex != originIndex) {
+                        Toast.makeText(activity, "restart now...", Toast.LENGTH_LONG).show();
+                        new Handler().postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                AppUtils.restartApp();
+                            }
+                        }, 3000);
+                    }
+                } else {
+                    Toast.makeText(activity, "restart now...", Toast.LENGTH_LONG).show();
+                    new Handler().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            AppUtils.restartApp();
+                        }
+                    }, 3000);
+                }
+
+                if (dialog != null) {
+                    dialog.dismiss();
+                }
+            }
+        });
+
+        dialog = builder.create();
+        //dialog.setCanceledOnTouchOutside(false);
+        //dialog.setCancelable(false);
+        dialog.show();
+
+        //设置dialog宽高及位置
+        try {
+            Window window = dialog.getWindow();
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.gravity = Gravity.CENTER;
+            window.setAttributes(lp);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 83 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/LocaleMangerUtils.java

@@ -0,0 +1,83 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import androidx.annotation.RequiresApi;
+
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+
+import java.util.Locale;
+
+public class LocaleMangerUtils {
+
+    public static Locale getSystemLocale() {
+        return Locale.getDefault();
+    }
+
+    //获取当前语言id: 0--auto, 1--English, 2--中文, 3--西班牙语, 4--俄语
+    public static int getCurrentLocaleIndex() {
+        int languageSize = BaseApplication.appContext.getResources().getStringArray(R.array.language_list).length;
+        int index = SettingConfig.getLanguageId(BaseApplication.appContext);
+        //Log.d("wzl", "current language index: " + index);
+        if (index >= 0 && index <languageSize) {
+            return index;
+        } else {
+            return 0;
+        }
+    }
+
+    public static void setApplicationLanguageByIndex(Context context, int index) {
+        Locale locale = Locale.getDefault();
+        switch (index) {
+            case 0:
+                //
+                break;
+            case 1:
+                locale = Locale.ENGLISH;
+                break;
+            case 2:
+                locale = Locale.CHINESE;
+                break;
+            case 3:
+                locale = new Locale("es");
+                break;
+            case 4:
+                locale = new Locale("ru");
+                break;
+        }
+
+        setApplicationLanguage(context, locale);
+    }
+
+    public static void setApplicationLanguage(Context context, Locale locale) {
+        Log.d("locale", "set locale language: " + locale.getLanguage());
+        Configuration configuration = context.getResources().getConfiguration();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            configuration.setLocale(locale);
+        } else {
+            configuration.locale = locale;
+        }
+        // 更新context中的语言设置
+        Resources resources = context.getResources();
+        DisplayMetrics dm = resources.getDisplayMetrics();
+        resources.updateConfiguration(configuration, dm);
+    }
+
+    @RequiresApi(api = Build.VERSION_CODES.N)
+    public static Locale getApplicationLocale() {
+        Configuration config = BaseApplication.appContext.getResources().getConfiguration();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            return config.locale;
+        } else {
+            return config.getLocales().get(0);
+        }
+    }
+
+}

+ 52 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/LogUpload.java

@@ -0,0 +1,52 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.util.Log;
+
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.api.UrlManager;
+import com.wdkl.rtc.util.ILogUpload;
+
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.FormBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+public class LogUpload implements ILogUpload {
+    private final static String TAG = LogUpload.class.getSimpleName();
+    private static OkHttpClient okHttpClient = new OkHttpClient();
+    private static UrlManager urlManager = UrlManager.Companion.build();
+
+    public void errorLog(String class_name, String err_msg, String method_name, String stack_trace) {
+
+        FormBody.Builder formBody = new FormBody.Builder();
+        formBody.add("device_id", ""+ Constants.Companion.getDeviceId());
+        formBody.add("class_name",class_name);
+        formBody.add("method_name",method_name);
+        formBody.add("exception_name",TAG);
+        formBody.add("err_msg",err_msg);
+        formBody.add("stack_trace",stack_trace);
+        Log.w(TAG,urlManager.getDevice_url());
+        Request request  = new Request.Builder()
+                .url(urlManager.getDevice_url() + "device/error_log")
+                .post(formBody.build())
+                .build();
+
+        okHttpClient.newCall(request).enqueue(new Callback() {
+            @Override
+            public void onFailure(Call call, IOException e) {
+                Log.e(TAG,"错误日志上传失败"+e.getMessage());
+            }
+
+            @Override
+            public void onResponse(Call call, Response response) throws IOException {
+                Log.d(TAG,"错误日志上传成功");
+                String data = response.body().string();
+                Log.d(TAG,"错误日志数据 data "+data);
+            }
+        });
+    }
+}

+ 303 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/MediaPlayHelper.java

@@ -0,0 +1,303 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.lib.vo.MessageEvent;
+import com.wdkl.rtc.rtc.AudioFocusManager;
+
+import org.greenrobot.eventbus.EventBus;
+
+public class MediaPlayHelper {
+    private static String TAG = MediaPlayHelper.class.getCanonicalName();
+
+    private static MediaPlayHelper sInstance = null;
+    private MediaPlayer mediaPlayer;
+    private HandlerThread playHandlerThread;
+    private Handler playHandler;
+    private MediaSessionCompat mediaSessionCompat;
+    private AudioFocusManager audioFocusManager;
+
+    private int mResId;
+    private String mUrl;
+    private float mVolume;
+    private boolean mLoop;
+    private boolean play;
+
+    /**
+     * 播放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();
+        audioFocusManager = new AudioFocusManager(BaseApplication.appContext);
+    }
+
+    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(boolean dismiss) {
+        playHandler.sendEmptyMessage(STOP);
+        if (dismiss) {
+            ImPlayDialogHelper.dismissIMDialog();
+        }
+    }
+
+    public void releaseMusic() {
+        playHandler.sendEmptyMessage(RELEASE);
+    }
+
+    //播放本地res音频资源
+    private void playResMusicNow() {
+        if (mediaPlayer != null) {
+            stopMusicNow();
+        }
+        mediaPlayer = MediaPlayer.create(BaseApplication.appContext, mResId);
+        try {
+            mediaPlayer.setLooping(mLoop);
+            mediaPlayer.setVolume(mVolume, mVolume);
+            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mediaPlayer.start();
+            play = true;
+            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer player) {
+//                    playMusicComplete();
+                    stopMusicNow();
+                    play = false;
+                }
+            });
+            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer player, int what, int extra) {
+                    //playMusicError();
+                    stopMusicNow();
+                    releaseMediaSession();
+                    play = false;
+                    return false;
+                }
+            });
+//            initMediaSession();
+        } catch (Exception e) {
+            play = false;
+            //playMusicError();
+            e.printStackTrace();
+        }
+    }
+
+    //播放远程或本地存储音频资源
+    private void playUrlMusicNow() {
+        if (mediaPlayer != null) {
+            mediaPlayer.stop();
+        }
+        mediaPlayer = new MediaPlayer();
+        try {
+            mediaPlayer.reset();
+            mediaPlayer.setDataSource(mUrl);
+            mediaPlayer.setLooping(mLoop);
+            mediaPlayer.setVolume(mVolume, mVolume);
+            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            play = true;
+            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();
+                    play = false;
+                    EventBus.getDefault().post(new MessageEvent("im_done", Constants.EVENT_IM_PLAY_DONE));
+                }
+            });
+            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer player, int what, int extra) {
+                    Log.e(TAG, "media play error...");
+                    //playMusicError();
+                    stopMusicNow();
+                    play = false;
+                    EventBus.getDefault().post(new MessageEvent("im_done", Constants.EVENT_IM_PLAY_DONE));
+                    return false;
+                }
+            });
+            mediaPlayer.prepareAsync();
+        } catch (Exception e) {
+            play = false;
+            //playMusicError();
+            e.printStackTrace();
+        }
+    }
+
+    private void stopMusicNow() {
+        if (mediaPlayer != null) {
+            mediaPlayer.setOnPreparedListener(null);
+            mediaPlayer.setOnCompletionListener(null);
+            try {
+                if (mediaPlayer.isPlaying()) {
+                    mediaPlayer.stop();
+                }
+                mediaPlayer.reset();
+                mediaPlayer.release();
+            } catch (IllegalStateException e) {
+                e.printStackTrace();
+            }
+        }
+        play = false;
+        mediaPlayer = null;
+        releaseMediaSession();
+    }
+
+    private void releaseMediaNow() {
+        if (mediaPlayer != null) {
+            mediaPlayer.setOnPreparedListener(null);
+            mediaPlayer.setOnCompletionListener(null);
+            try {
+                mediaPlayer.stop();
+                mediaPlayer.reset();
+                mediaPlayer.release();
+            } catch (IllegalStateException e) {
+                e.printStackTrace();
+            }
+        }
+        play = false;
+        mediaPlayer = null;
+        releaseMediaSession();
+    }
+
+    public boolean isMediaPlaying() {
+        if (mediaPlayer != null) {
+            return mediaPlayer.isPlaying() || play;
+        }
+        return false;
+    }
+
+    private void initMediaSession(){
+        if (mediaSessionCompat!=null){
+            releaseMediaSession();
+        }
+
+        audioFocusManager.requestFocus();
+
+        mediaSessionCompat = new MediaSessionCompat(BaseApplication.appContext,TAG);
+        mediaSessionCompat.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
+        mediaSessionCompat.setActive(true);
+        mediaSessionCompat.setCallback(new MediaSessionCompat.Callback(){
+            @Override
+            public boolean onMediaButtonEvent(Intent intent) {
+                String action = intent.getAction();
+                if (action != null) {
+                    if (TextUtils.equals(action, Intent.ACTION_MEDIA_BUTTON)) {
+                        KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+                        if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+                            int keyCode = keyEvent.getKeyCode();
+                            Log.i(TAG, "mediasession keycode " + keyCode);
+                            switch (keyCode) {
+                                case KeyEvent.KEYCODE_MEDIA_PLAY:
+                                case KeyEvent.KEYCODE_MEDIA_PAUSE:
+                                    EventBus.getDefault().post(new MessageEvent(1, Constants.EVENT_BLUETOOTH_ACCEPT_CALL));
+                                    break;
+                                default:
+                                    break;
+                            }
+                        }
+                    }
+                }
+                return super.onMediaButtonEvent(intent);
+            }
+        });
+    }
+
+    private void releaseMediaSession(){
+        if (mediaSessionCompat!=null){
+            mediaSessionCompat.setCallback(null);
+            mediaSessionCompat.setActive(false);
+            mediaSessionCompat.release();
+            mediaSessionCompat = null;
+
+            audioFocusManager.releaseAudioFocus();
+        }
+    }
+}

+ 115 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/MediaPlayer.kt

@@ -0,0 +1,115 @@
+package com.wdkl.ncs.android.component.nursehome.util
+
+import android.media.AudioManager
+import android.media.MediaPlayer
+import android.util.Log
+import com.umeng.weixin.umengwx.e
+import java.io.IOException
+import java.lang.IllegalStateException
+
+class MediaPlayer {
+    private val TAG = MediaPlayer::class.java.getSimpleName()
+
+    var mMediaPlayer: MediaPlayer? = null
+
+    init {
+        Log.i(TAG, "初始化播放器")
+        mMediaPlayer = MediaPlayer()
+        //这样设置 不能控制本音乐的音量大小 也就是setVolume无效
+//        mMediaPlayer!!.setAudioStreamType(AudioManager.STREAM_MUSIC)
+        mMediaPlayer!!.setAudioStreamType(AudioManager.STREAM_VOICE_CALL)
+        mMediaPlayer!!.setOnCompletionListener {
+            Log.i(TAG, "播放完成")
+        }
+
+        mMediaPlayer!!.setOnPreparedListener {
+            Log.i(TAG, "加载完长")
+            mMediaPlayer!!.start()
+        }
+        mMediaPlayer!!.setOnErrorListener { mp, what, extra ->
+            Log.e(TAG, "异常")
+            false
+        }
+
+    }
+
+    //启动播放
+    fun startMediaPlayer(url: String) {
+        if ((mMediaPlayer != null && mMediaPlayer!!.isPlaying())) {
+            mMediaPlayer!!.stop()
+        }
+        mMediaPlayer!!.reset()
+        //设置音频文件到MediaPlayer对象中
+        //测试地址 "http://file.kuyinyun.com/group1/M00/90/B7/rBBGdFPXJNeAM-nhABeMElAM6bY151.mp3"
+        try {
+            mMediaPlayer!!.setDataSource(url)
+            //让MediaPlayer对象准备,用这个方法防止加载时耗时导致anr
+            mMediaPlayer!!.prepareAsync()
+        } catch (e: IOException) {
+            e.printStackTrace()
+            Log.e(TAG, "播放异常")
+        }catch (e:IllegalStateException) {
+            e.message
+            Log.e(TAG, "IllegalStateException+播放异常");
+        }
+
+    }
+
+    /**
+     * 暂停播放
+     */
+    fun pauseMediaPlayer() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer!!.pause()
+        }
+
+    }
+
+    /**
+     * 继续播放
+     */
+    fun continueToPlayMediaPlayer() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer!!.start()
+        }
+
+    }
+
+    /**
+     * 停止播放
+     */
+    fun stopMediaPlayer() {
+        if (mMediaPlayer != null) {
+            mMediaPlayer!!.stop()
+        }
+    }
+
+    /**
+     * 释放播放器资源
+     */
+    fun releaseMediaPlayer() {
+        if (mMediaPlayer != null) {
+            try {
+                mMediaPlayer!!.stop()
+                mMediaPlayer!!.release()
+            } catch (e: IllegalStateException) {
+
+            }
+
+        }
+        mMediaPlayer = null
+    }
+
+    /**
+     * 音量设置
+     *setDataSource()方法之后设置才有效
+     * @param volume
+     */
+    fun setVolume(volume: Float) {
+        if (mMediaPlayer != null) {
+            mMediaPlayer!!.setVolume(volume, volume)
+        }
+    }
+
+
+}

+ 600 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/NetHelper.java

@@ -0,0 +1,600 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.wdkl.ncs.android.lib.base.BaseApplication;
+
+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;
+
+
+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 = 20000;
+    public static boolean NetConn = false;
+
+    public static final int NETWORK_NONE = -1;
+    public static final int NETWORK_WIFI = 1;
+    public static final int NETWORK_2G = 2;
+    public static final int NETWORK_3G = 3;
+    public static final int NETWORK_4G = 4;
+    public static final int NETWORK_MOBILE = 5;
+
+    /**
+     * 以太网是否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);
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * 得到MAC
+     *
+     * @return String
+     */
+    public String getMacAddress() {
+        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N){//小于安卓7 走这里
+            String mac = "";
+            try {
+//                mac = getLocalMacAddressFromIp();
+//                if (TextUtils.isEmpty(mac)) {
+//                    mac = getNetworkMac();
+//                }
+                if (TextUtils.isEmpty(mac)) {
+                    mac = tryGetWifiMac();
+                }
+            } catch (Exception e) {
+            }
+            return mac;
+        }else {//大于等于安卓7走这里
+            List<NetworkInterface> interfaces = null;
+            try {
+                interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+                for (NetworkInterface networkInterface : interfaces) {
+                    if (networkInterface != null && TextUtils.isEmpty(networkInterface.getName()) == false) {
+                        if ("wlan0".equalsIgnoreCase(networkInterface.getName())) {
+                            byte[] macBytes = networkInterface.getHardwareAddress();
+                            if (macBytes != null && macBytes.length > 0) {
+                                StringBuilder str = new StringBuilder();
+                                for (byte b : macBytes) {
+                                    str.append(String.format("%02X:", b));
+                                }
+                                if (str.length() > 0) {
+                                    str.deleteCharAt(str.length() - 1);
+                                }
+                                return str.toString();
+                            }
+                        }
+                    }
+                }
+            } catch (SocketException e) {
+                e.printStackTrace();
+            }
+            return "unknown";
+
+        }
+    }
+
+    /**
+     * 兼容安卓5-10 获取Mac地址
+     * @return
+     */
+    public String getMacAddress2() {
+        List<NetworkInterface> interfaces = null;
+        try {
+            interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+            for (NetworkInterface networkInterface : interfaces) {
+                if (networkInterface != null && TextUtils.isEmpty(networkInterface.getName()) == false) {
+                    if ("wlan0".equalsIgnoreCase(networkInterface.getName())) {
+                        byte[] macBytes = networkInterface.getHardwareAddress();
+                        if (macBytes != null && macBytes.length > 0) {
+                            StringBuilder str = new StringBuilder();
+                            for (byte b : macBytes) {
+                                str.append(String.format("%02X:", b));
+                            }
+                            if (str.length() > 0) {
+                                str.deleteCharAt(str.length() - 1);
+                            }
+                            return str.toString();
+                        }
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+        return "unknown";
+    }
+
+
+    /**
+     * 通过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;
+    }
+
+    //获取本地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;
+    }
+
+    public int getNetworkType() {
+        if (connManager != null && connManager.getActiveNetworkInfo() != null) {
+            return connManager.getActiveNetworkInfo().getType();
+        }
+
+        return -1;
+    }
+
+    /**
+     * 获取当前网络连接的类型
+     *
+     * @param context context
+     * @return int
+     */
+    public int getNetworkState(Context context) {
+        ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); // 获取网络服务
+        if (null == connManager) { // 为空则认为无网络
+            return NETWORK_NONE;
+        }
+        // 获取网络类型,如果为空,返回无网络
+        NetworkInfo activeNetInfo = connManager.getActiveNetworkInfo();
+        if (activeNetInfo == null || !activeNetInfo.isAvailable()) {
+            return NETWORK_NONE;
+        }
+        // 判断是否为WIFI
+        NetworkInfo wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+        if (null != wifiInfo) {
+            NetworkInfo.State state = wifiInfo.getState();
+            if (null != state) {
+                if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
+                    return NETWORK_WIFI;
+                }
+            }
+        }
+        // 若不是WIFI,则去判断是2G、3G、4G网
+        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        int networkType = telephonyManager.getNetworkType();
+        switch (networkType) {
+            /*
+             GPRS : 2G(2.5) General Packet Radia Service 114kbps
+             EDGE : 2G(2.75G) Enhanced Data Rate for GSM Evolution 384kbps
+             UMTS : 3G WCDMA 联通3G Universal Mobile Telecommunication System 完整的3G移动通信技术标准
+             CDMA : 2G 电信 Code Division Multiple Access 码分多址
+             EVDO_0 : 3G (EVDO 全程 CDMA2000 1xEV-DO) Evolution - Data Only (Data Optimized) 153.6kps - 2.4mbps 属于3G
+             EVDO_A : 3G 1.8mbps - 3.1mbps 属于3G过渡,3.5G
+             1xRTT : 2G CDMA2000 1xRTT (RTT - 无线电传输技术) 144kbps 2G的过渡,
+             HSDPA : 3.5G 高速下行分组接入 3.5G WCDMA High Speed Downlink Packet Access 14.4mbps
+             HSUPA : 3.5G High Speed Uplink Packet Access 高速上行链路分组接入 1.4 - 5.8 mbps
+             HSPA : 3G (分HSDPA,HSUPA) High Speed Packet Access
+             IDEN : 2G Integrated Dispatch Enhanced Networks 集成数字增强型网络 (属于2G,来自维基百科)
+             EVDO_B : 3G EV-DO Rev.B 14.7Mbps 下行 3.5G
+             LTE : 4G Long Term Evolution FDD-LTE 和 TDD-LTE , 3G过渡,升级版 LTE Advanced 才是4G
+             EHRPD : 3G CDMA2000向LTE 4G的中间产物 Evolved High Rate Packet Data HRPD的升级
+             HSPAP : 3G HSPAP 比 HSDPA 快些
+             */
+            // 2G网络
+            case TelephonyManager.NETWORK_TYPE_GPRS:
+            case TelephonyManager.NETWORK_TYPE_CDMA:
+            case TelephonyManager.NETWORK_TYPE_EDGE:
+            case TelephonyManager.NETWORK_TYPE_1xRTT:
+            case TelephonyManager.NETWORK_TYPE_IDEN:
+                return NETWORK_2G;
+            // 3G网络
+            case TelephonyManager.NETWORK_TYPE_EVDO_A:
+            case TelephonyManager.NETWORK_TYPE_UMTS:
+            case TelephonyManager.NETWORK_TYPE_EVDO_0:
+            case TelephonyManager.NETWORK_TYPE_HSDPA:
+            case TelephonyManager.NETWORK_TYPE_HSUPA:
+            case TelephonyManager.NETWORK_TYPE_HSPA:
+            case TelephonyManager.NETWORK_TYPE_EVDO_B:
+            case TelephonyManager.NETWORK_TYPE_EHRPD:
+            case TelephonyManager.NETWORK_TYPE_HSPAP:
+                return NETWORK_3G;
+            // 4G网络
+            case TelephonyManager.NETWORK_TYPE_LTE:
+                return NETWORK_4G;
+            default:
+                return NETWORK_MOBILE;
+        }
+    }
+}

+ 72 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/NetWorkChangeReceiver.kt

@@ -0,0 +1,72 @@
+package com.wdkl.ncs.android.component.home.util
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.Network
+import android.net.NetworkInfo
+import android.os.Build
+import android.util.Log
+import com.wdkl.ncs.android.component.home.service.WdKeepAliveService
+import com.wdkl.ncs.android.middleware.tcp.NettyClient
+
+class NetWorkChangeReceiver : BroadcastReceiver() {
+    val TAG = NetWorkChangeReceiver::class.simpleName
+
+    override fun onReceive(context: Context?, intent: Intent?) {
+        // API版本23以上使用
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            // 链接管理器(获取系统链接服务)
+            val manager: ConnectivityManager = context?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+            //获取所有网络连接的信息
+            val networks: Array<Network> = manager.allNetworks
+            var networkInfo: NetworkInfo? = null
+            //通过循环将网络信息逐个取出来
+            for (network in networks) {
+                networkInfo = manager.getNetworkInfo(network)
+            }
+
+            // 判断网络是否可用
+            if (null != networkInfo && networkInfo.isAvailable) {
+                //当前网络可用
+            }
+            // 判断网络是否链接
+            if (null != networkInfo && networkInfo.isConnected) {
+                //当前网络已经链接
+                if (!NettyClient.instance.isConnect() && WdKeepAliveService.instanceCreated){
+                    Log.w(TAG,"TCP.进入重新连接")
+                    HandleTcpConnect.instance.tcpReConnect()
+                }
+            }
+            // 判断网络是否正在链接
+            if (null != networkInfo && networkInfo.isConnectedOrConnecting) {
+                //当前网络正在链接
+            }
+            // 判断网络链接失败
+            if (null != networkInfo && networkInfo.isFailover) {
+                //当前网络链接失败
+            }
+            // 判断网络是否赞漫游
+            if (null != networkInfo && networkInfo.isRoaming) {
+                //当前处于漫游网络
+            }
+        } else { // API版本23以下使用
+            // 链接管理器(获取系统链接服务)
+            val manager: ConnectivityManager = context?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+
+            //获取ConnectivityManager对象对应的NetworkInfo对象
+            //获取WIFI连接的信息
+            val wifiNetworkInfo: NetworkInfo? = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI)
+            //获取移动数据连接的信息
+            val dataNetworkInfo: NetworkInfo? = manager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
+            if ((wifiNetworkInfo!=null && wifiNetworkInfo.isConnected) || (dataNetworkInfo!=null&&dataNetworkInfo.isConnected)) {
+                //WIFI已连接 或 移动数据已连接
+                Log.w(TAG,"TCP.进入重新连接")
+                if (!NettyClient.instance.isConnect()){
+                    HandleTcpConnect.instance.tcpReConnect()
+                }
+            }
+        }
+    }
+}

+ 127 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/PasswordDialogHelper.java

@@ -0,0 +1,127 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.GridView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.adapter.NumAdapter;
+
+
+public class PasswordDialogHelper {
+
+    private static AlertDialog dialog;
+    private static String pwd = "";
+
+    public static void showPasswordDialog(final Activity activity, final ClickListener listener) {
+        if (dialog != null && dialog.isShowing()) {
+            return;
+        }
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.password_dialog_lay, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+
+        final String[] numbers = {"1","2","3","4","5","6","7","8","9"};
+        final TextView password = contentView.findViewById(R.id.tv_psw_view);
+        GridView gridView = contentView.findViewById(R.id.grid_psw);
+        NumAdapter adapter = new NumAdapter(numbers, activity);
+        gridView.setAdapter(adapter);
+        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                if (pwd.length() <= 2) {
+                    pwd = pwd + numbers[position];
+                    password.setText(pwd);
+                }
+                Log.d("password", "input password len: " + pwd.length() + "--" + pwd);
+            }
+        });
+
+        TextView delete = contentView.findViewById(R.id.btn_delete);
+        TextView cancel = contentView.findViewById(R.id.btn_cancel);
+        TextView confirm = contentView.findViewById(R.id.btn_confirm);
+        delete.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Log.d("password", "delete password len: " + pwd.length() + "--" + pwd);
+                if (pwd.length() > 1) {
+                    pwd = pwd.substring(0, pwd.length()-1);
+                    password.setText(pwd);
+                } else {
+                    pwd = "";
+                    password.setText(pwd);
+                    password.setHint(R.string.input_password);
+                }
+            }
+        });
+
+        cancel.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                dismissCallDialog();
+            }
+        });
+
+        confirm.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if ("888".equals(pwd)) {
+                    if (listener != null) {
+                        listener.onConfirm();
+                    }
+                    dismissCallDialog();
+                } else {
+                    Toast.makeText(activity, R.string.invalid_password, Toast.LENGTH_SHORT).show();
+                }
+            }
+        });
+
+        dialog = builder.create();
+        dialog.setCanceledOnTouchOutside(false);
+        dialog.setCancelable(false);
+        dialog.show();
+
+        //设置dialog宽高及位置
+        try {
+            Window window = dialog.getWindow();
+            window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.gravity = Gravity.CENTER;
+            window.setAttributes(lp);
+
+            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_FULLSCREEN);
+            window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void dismissCallDialog() {
+        pwd = "";
+        if (dialog != null) {
+            dialog.dismiss();
+            dialog = null;
+        }
+    }
+
+    public interface ClickListener {
+        void onConfirm();
+    }
+}

+ 112 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/PhoneCallUtil.java

@@ -0,0 +1,112 @@
+package com.wdkl.ncs.android.component.home.util;
+
+
+import android.Manifest;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import androidx.core.app.ActivityCompat;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.reflect.Method;
+
+public class PhoneCallUtil {
+    private static final String TAG = "PhoneUtil";
+
+    /**
+     * 挂断电话
+     *
+     * @param context
+     * @return
+     */
+    public static boolean endCall(Context context) {
+        boolean callSuccess = false;
+        try {
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
+                // >=Android 9,需打开 Manifest.permission.ANSWER_PHONE_CALLS 权限
+                TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
+                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ANSWER_PHONE_CALLS) != PackageManager.PERMISSION_GRANTED) {
+                    // TODO: Consider calling
+                    //    ActivityCompat#requestPermissions
+                    // here to request the missing permissions, and then overriding
+                    //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
+                    //                                          int[] grantResults)
+                    // to handle the case where the user grants the permission. See the documentation
+                    // for ActivityCompat#requestPermissions for more details.
+                    return false;
+                }
+                telecomManager.endCall();
+                callSuccess = true;
+                Log.d(TAG, "telecomManager.endCall() finish");
+            } else {
+                // 1.获取TelephonyManager
+                // 2.获取TelephonyManager.class
+                // 3.反射调用TelephonyManager的 getITelephony方法获取ITelephony
+                // 4.挂断电话ITelephony.endCall
+                TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+                Class c = Class.forName(tm.getClass().getName());
+                Method m = c.getDeclaredMethod("getITelephony");
+                m.setAccessible(true);
+                ITelephony telephonyService = (ITelephony) m.invoke(tm);
+                if (telephonyService != null) {
+                    callSuccess = telephonyService.endCall();
+                    Log.d(TAG, " telephonyService.endCall finish");
+                }
+            }
+        } catch (Exception e) {
+            Log.d(TAG, "Exception   error: " + e.getMessage());
+            callSuccess = disconnectCall();
+            ;
+            e.printStackTrace();
+        }
+        return callSuccess;
+    }
+
+    // 删除通话记录
+    public static void deleteCallLog(Context context, String incomingNumber) {
+        // 1.获取内容解析者
+        final ContentResolver resolver = context.getContentResolver();
+        // 2.获取内容提供者地址 call_log calls表的地址:calls
+        // 3.获取执行操作路径
+        final Uri uri = Uri.parse("content://call_log/calls");
+        // 4.删除操作
+        // 通过内容观察者观察内容提供者内容,如果变化,就去执行删除操作
+        // notifyForDescendents : 匹配规则,true : 精确匹配 false:模糊匹配
+        resolver.registerContentObserver(uri, true, new ContentObserver(new Handler()) {
+            // 内容提供者内容变化的时候调用
+            @Override
+            public void onChange(boolean selfChange) {
+                super.onChange(selfChange);
+                try {
+                    // 删除通话记录
+                    resolver.delete(uri, "number=?", new String[]{incomingNumber});
+                    // 注销内容观察者
+                    resolver.unregisterContentObserver(this);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        });
+    }
+
+    private static boolean disconnectCall() {
+        try {
+            Log.d(TAG, "input keyevent " + KeyEvent.KEYCODE_ENDCALL);
+            Runtime.getRuntime().exec("input keyevent " + KeyEvent.KEYCODE_ENDCALL); //KEYCODE_HEADSETHOOK
+        } catch (Exception exc) {
+            Log.d(TAG, "exc.printStackTrace");
+            exc.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+}

+ 133 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/PhoneNumberDialogHelper.java

@@ -0,0 +1,133 @@
+package com.wdkl.ncs.android.component.home.util;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.telephony.SmsManager;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.wdkl.ncs.android.component.home.R;
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig;
+import com.wdkl.ncs.android.lib.utils.ExtendMethodsKt;
+import com.wdkl.ncs.android.middleware.common.Constants;
+import com.wdkl.ncs.android.middleware.utils.ContactHelper;
+import com.wdkl.ncs.android.middleware.utils.StringUtil;
+
+
+public class PhoneNumberDialogHelper {
+
+    private static AlertDialog dialog;
+
+    public static void showDialog(final Activity activity) {
+        if (dialog != null && dialog.isShowing()) {
+            return;
+        }
+
+        View contentView = LayoutInflater.from(activity).inflate(R.layout.phone_num_dialog_lay, null);
+        AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+        builder.setView(contentView);
+
+        TextView phoneNum = contentView.findViewById(R.id.tv_my_phone_number);
+        EditText operator = contentView.findViewById(R.id.edit_operator_number);
+        EditText smsCmd = contentView.findViewById(R.id.edit_sms_cmd);
+        TextView cancel = contentView.findViewById(R.id.btn_cancel_send);
+        TextView send = contentView.findViewById(R.id.btn_send);
+        TextView call = contentView.findViewById(R.id.btn_call);
+
+        phoneNum.setText(StringUtil.getResString(R.string.phone_number_title) + ": " + ContactHelper.getPhoneNumber());
+        operator.setText(SettingConfig.getOperatorNumber(activity));
+        smsCmd.setText(SettingConfig.getSmsCommand(activity));
+
+        cancel.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                dismissCallDialog();
+            }
+        });
+
+        send.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                String number = operator.getText().toString();
+                String cmd = smsCmd.getText().toString();
+                //发送号码查询短信
+                if (!TextUtils.isEmpty(number) && !TextUtils.isEmpty(cmd)) {
+                    SettingConfig.setOperatorNumber(activity, number);
+                    SettingConfig.setSmsCommand(activity, cmd);
+
+                    try {
+                        SmsManager smsManager = SmsManager.getDefault();
+                        smsManager.sendTextMessage(number, null, cmd, null, null);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        ExtendMethodsKt.showMessage("send sms error");
+                    }
+
+                    Constants.Companion.setSmsCheck(true);
+
+                    dismissCallDialog();
+                } else {
+                    ExtendMethodsKt.showMessage(R.string.send_sms_number_empty);
+                }
+            }
+        });
+
+        call.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                String number = operator.getText().toString();
+                if (!TextUtils.isEmpty(number)) {
+                    SettingConfig.setOperatorNumber(activity, number);
+
+                    Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + number));
+                    activity.startActivity(intent);
+
+                    Constants.Companion.setSmsCheck(true);
+                    dismissCallDialog();
+                }
+            }
+        });
+
+
+        dialog = builder.create();
+        dialog.setCanceledOnTouchOutside(false);
+        dialog.setCancelable(false);
+        dialog.show();
+
+        //设置dialog宽高及位置
+        try {
+            Window window = dialog.getWindow();
+            window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+            WindowManager.LayoutParams lp = window.getAttributes();
+            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
+            lp.gravity = Gravity.CENTER;
+            window.setAttributes(lp);
+
+            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_FULLSCREEN);
+            window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void dismissCallDialog() {
+        if (dialog != null) {
+            dialog.dismiss();
+            dialog = null;
+        }
+    }
+}

+ 0 - 0
android_mobile/src/main/yd_w_xiaomi_2/code/com/wdkl/ncs/android/component/home/util/RecordHelper.java


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