Browse Source

添加通讯读取接口,更新手机通讯录.通讯号码白名单过滤消除+86的可能

wuyunfeng 3 years ago
parent
commit
6371509375

+ 5 - 0
home/src/main/AndroidManifest.xml

@@ -16,6 +16,11 @@
     <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.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"/>
 
     <application
         android:allowBackup="true"

+ 5 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchEventDetailActivity.kt

@@ -35,6 +35,7 @@ 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.channel.DeviceChannel
 import com.wdkl.ncs.android.middleware.tcp.channel.EventUtil
@@ -293,6 +294,10 @@ class WatchEventDetailActivity : BaseActivity<WatchHomeActivityPresenter, WatchA
     override fun setPhoneNumberWhiteList(phoneList: ArrayList<String>) {
     }
 
+    override fun setContact(contactsVOs: List<WatchContactVO>) {
+
+    }
+
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun onMoonEvent(messageEvent: MessageEvent) {
         if (messageEvent.tag == 4) {

+ 107 - 86
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchHome2Activity.kt

@@ -46,7 +46,10 @@ import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
 import com.wdkl.ncs.android.middleware.tcp.NettyClient
 import com.wdkl.ncs.android.lib.vo.MessageEvent
 import com.wdkl.ncs.android.middleware.model.dos.ChannelDO
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactVO
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactsVO
 import com.wdkl.ncs.android.middleware.tcp.TcpClientHandler
+import com.wdkl.ncs.android.middleware.utils.ContactHelper
 import com.wdkl.ncs.keepbackground.utils.SpManager
 import com.wdkl.ncs.keepbackground.work.DaemonEnv
 import io.reactivex.Observable
@@ -56,9 +59,10 @@ import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
 
 //@Router(path = "/watch/home")
-class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivityHome2Binding>(), WatchHomeActivityContract.View, View.OnClickListener{
+class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivityHome2Binding>(), WatchHomeActivityContract.View, View.OnClickListener {
 
     var TAG = WatchHome2Activity::class.java.getSimpleName()
+
     //监听网络变化
     lateinit var teleManager: TelephonyManager
     private var netType: Int = -1
@@ -72,14 +76,14 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
 
     private var clickTime: Long = 0
 
-    private lateinit var netWorkChangeReceiver:NetWorkChangeReceiver
+    private lateinit var netWorkChangeReceiver: NetWorkChangeReceiver
 
     override fun onCreate(savedInstanceState: Bundle?) {
-        if (!isTaskRoot){
+        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)
+        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)
     }
@@ -95,7 +99,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
     //初始化
     override fun init() {
 //        DaemonEnv.sendStopWorkBroadcast(this)
-        AnrFcExceptionUtil.getInstance(application).initFCException()
+//        AnrFcExceptionUtil.getInstance(application).initFCException()
         //注册广播
         regReceiver()
 
@@ -123,7 +127,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
             if (teleManager != null) {
                 teleManager.listen(object : PhoneStateListener() {
                     override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
-                        if (NetHelper.getInstance().getNetworkState(applicationContext) == NetHelper.NETWORK_NONE){
+                        if (NetHelper.getInstance().getNetworkState(applicationContext) == NetHelper.NETWORK_NONE) {
                             tv_signal_strength.text = "网络断开"
                         } else {
                             val signalinfo = signalStrength.toString()
@@ -163,8 +167,8 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         if (Build.VERSION.SDK_INT >= 23) {
             if (!Settings.canDrawOverlays(this)) {
                 val intent = Intent(
-                    Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
-                    Uri.parse("package:$packageName"))
+                        Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+                        Uri.parse("package:$packageName"))
                 startActivityForResult(intent, 10)
             }
         }
@@ -201,12 +205,12 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         registerReceiver(batteryBroadcastReceiver, intentFilter2)
     }
 
-    private fun releaseReceiver(){
+    private fun releaseReceiver() {
         if (batteryBroadcastReceiver != null) {
             unregisterReceiver(batteryBroadcastReceiver)
         }
 
-        if (netWorkChangeReceiver!=null){
+        if (netWorkChangeReceiver != null) {
             unregisterReceiver(netWorkChangeReceiver)
         }
 //        if (myMediaButtonReceiver != null ){
@@ -218,16 +222,16 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
      * 返回的tcp信息
      */
     override fun setTcpServerHost(tcpSeverDTO: TcpSeverDTO) {
-        Log.d(TAG,"获取服务器IP完成")
-        if(tcpSeverDTO.publicIp!= null && tcpSeverDTO.tcpPort != null && tcpSeverDTO.readerIdleTime != null){
+        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")
+            if (DaemonEnv.app == null && !Strings.isNullOrEmpty(Constants.tcpServer) && !WdKeepAliveService.instanceCreated) {
+                Log.d(TAG, "开始 WdKeepAliveService")
                 //保活守护进程
                 DaemonEnv.init(this)
                 DaemonEnv.startServiceSafely(this, WdKeepAliveService::class.java)
@@ -237,63 +241,67 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         }
     }
 
-    private fun requestPermissions(){
+    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
-                        )
-                )
-                .subscribe {
-                    if (!it) {
-                        showMessage("请重新授权,进入App")
-                        requestPermissions()
-                    } else {
-                        permissionGranted()
-                    }
-                }.joinManager(disposableManager)
+                    .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
+                                    )
+                    )
+                    .subscribe {
+                        if (!it) {
+                            showMessage("请重新授权,进入App")
+                            requestPermissions()
+                        } else {
+                            permissionGranted()
+                        }
+                    }.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
-                        )
-                )
-                .subscribe {
-                    if (!it) {
-                        showMessage("请重新授权,进入App")
-                        requestPermissions()
-                    } else {
-                        permissionGranted()
-                    }
-                }.joinManager(disposableManager)
+                    .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
+                                    )
+                    )
+                    .subscribe {
+                        if (!it) {
+                            showMessage("请重新授权,进入App")
+                            requestPermissions()
+                        } else {
+                            permissionGranted()
+                        }
+                    }.joinManager(disposableManager)
         }
     }
 
     override fun handleAppVersion(appInfo: AppVersionDO) {
-        if (BuildConfig.VERSION_CODE < appInfo.versionNo && !AppUpdateActivity.opened){
+        if (BuildConfig.VERSION_CODE < appInfo.versionNo && !AppUpdateActivity.opened) {
             showMessage("有新版本")
             val intent = Intent()
             intent.setClass(this, AppUpdateActivity::class.java)
@@ -305,7 +313,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
      * 返回的设备信息
      */
     override fun setDeviceDo(data: DeviceVO) {
-        if (data.id == null || data.id<=0){
+        if (data.id == null || data.id <= 0) {
             loadingDialog.dismiss()
             showMessage("本机未注册,请将识别码发给管理员")
             tv_register_status.text = "本机未注册,请将识别码发给管理员。管理员注册本机后请点击注册完成进入"
@@ -340,8 +348,9 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         Constants.userRoleName = data.roleName
 
         presenter.getAppVersion(Constants.partId, 7)
+        presenter.getWatchContacts(Constants.deviceId)
 
-        if (Constants.deviceId<=0 || TextUtils.isEmpty(Constants.sipId)) {
+        if (Constants.deviceId <= 0 || TextUtils.isEmpty(Constants.sipId)) {
             showMessage("初始化数据时服务器返回数据不全,请联系管理员")
             tv_feedback_device_info.text = "初始化数据时服务器返回数据不全,请联系管理员"
             return
@@ -359,12 +368,12 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
 
         //onTcpConnectSuccess()
 
-        if (Strings.isNullOrEmpty(Constants.userName)){
+        if (Strings.isNullOrEmpty(Constants.userName)) {
             watch_name_tv.text = "重载"
             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{
+            watch_name_tv.setOnClickListener {
                 loadingDialog.show()
                 presenter.getDeviceVO(Constants.imei)
                 return@setOnClickListener
@@ -416,13 +425,24 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         Constants.phoneWhiteList.addAll(phoneList)
     }
 
+    //写手机通讯录
+    override fun setContact(contactVOs: List<WatchContactVO>) {
+        if (contactVOs != null && contactVOs.size > 0) {
+            for (contactVO in contactVOs) {
+                if (!Strings.isNullOrEmpty(contactVO.name) && !Strings.isNullOrEmpty(contactVO.phoneNumber)) {
+                    ContactHelper.setContact(this, contactVO.name, contactVO.phoneNumber)
+                }
+            }
+        }
+    }
+
     /**
      * 设置设备数据
      */
     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){
+        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)
@@ -468,11 +488,11 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
 
         //等30秒
         var waitSeconds = 0;
-        Thread{
-            while (!NettyClient.instance.isConnect()){
+        Thread {
+            while (!NettyClient.instance.isConnect()) {
                 Log.w(TAG, "无网络" + waitSeconds)
                 waitSeconds++
-                if (waitSeconds>30){
+                if (waitSeconds > 30) {
                     break
                 }
                 Thread.sleep(5000)
@@ -492,7 +512,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         other_linyout.setOnClickListener(this)
         channel_im_layout.setOnClickListener(this)
         tv_register_ok.setOnClickListener({
-            Log.d(TAG,"注册完成")
+            Log.d(TAG, "注册完成")
             initTcp()
 
             //presenter.getDeviceVO(Constants.imei)
@@ -504,15 +524,15 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         return false
     }
 
-    fun initTcp(){
-        if (!Strings.isNullOrEmpty(Constants.tcpServer)){    //有网且得到了服务器IP
+    fun initTcp() {
+        if (!Strings.isNullOrEmpty(Constants.tcpServer)) {    //有网且得到了服务器IP
             presenter.getTcpServerHost()
             return
         }
 
         var count = 30
         Thread(Runnable {
-            while (isUnRegister && count>0 && Strings.isNullOrEmpty(Constants.tcpServer)) {
+            while (isUnRegister && count > 0 && Strings.isNullOrEmpty(Constants.tcpServer)) {
                 //DaemonEnv.sendStopWorkBroadcast(this)
                 Log.i(TAG, "获取TCP服务器IP和端口")
                 presenter.getTcpServerHost()
@@ -536,6 +556,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         countDownTimer = object : CountDownTimer(1 * 1000L, 1000) {
             override fun onTick(millisUntilFinished: Long) {
             }
+
             override fun onFinish() {
                 tv_register_ok.isEnabled = true
                 //loadingDialog.dismiss()
@@ -578,7 +599,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
         //停止服务
         //DaemonEnv.sendStopWorkBroadcast(this)
 
-        if(countDownTimer != null){
+        if (countDownTimer != null) {
             countDownTimer.cancel()
         }
     }
@@ -598,7 +619,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
                 startActivity(intent)
             }
             R.id.user_name_linlyout -> {
-                if (Constants.userName==null){
+                if (Constants.userName == null) {
                     var intent = Intent(this, TakeoverActivity().javaClass)
                     startActivity(intent)
                 } else {
@@ -612,7 +633,7 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
                 //群留言
                 if (Constants.channelId == -1) {
                     showMessage("请先建立频道")
-                } else if (TextUtils.isEmpty(Constants.userName)){
+                } else if (TextUtils.isEmpty(Constants.userName)) {
                     showMessage("设备未绑定")
                 } else {
                     var intent = Intent(this, ChannelImActivity::class.java)
@@ -659,10 +680,10 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
 
     override fun onDestroy() {
         super.onDestroy()
-        if (loadingDialog!=null){
+        if (loadingDialog != null) {
             loadingDialog.dismiss()
         }
-        if (netOffLoadingDialog!=null){
+        if (netOffLoadingDialog != null) {
             netOffLoadingDialog.dismiss()
         }
     }
@@ -688,16 +709,16 @@ class WatchHome2Activity : BaseActivity<WatchHomeActivityPresenter, WatchActivit
                 battery_warning_tv.visibility = View.GONE
                 electric_quantity_tv.text = "" + message
             }
-        } else if (messageEvent.tag == Constants.EVENT_RTC_STATE){
+        } 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){
+        } else if (messageEvent.tag == Constants.EVENT_TCP_BREAK) {
             onTcpConnectFailed()
-        } else if (messageEvent.tag== Constants.EVENT_TCP_CONNECTED){
+        } else if (messageEvent.tag == Constants.EVENT_TCP_CONNECTED) {
             onTcpConnectSuccess()
         }
     }

+ 13 - 7
home/src/main/code/com/wdkl/ncs/android/component/home/service/PhoneStateReceiver.java

@@ -14,6 +14,7 @@ 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.rtc.util.Constant;
 
 import static com.wdkl.ncs.android.lib.utils.ExtendMethodsKt.showMessage;
 
@@ -55,17 +56,22 @@ public class PhoneStateReceiver extends BroadcastReceiver {
                 Constants.Companion.setPhoneState(Constants.PHONE_IDLE);
             } else if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING) && extraIncomingNumber != null) {
                 //非白名单号码拦截并删除记录
-                if (!Constants.Companion.getPhoneWhiteList().contains(extraIncomingNumber)) {
+                if (Constants.Companion.getPhoneWhiteList() != null) {
+                    for (String s : Constants.Companion.getPhoneWhiteList()) {
+                        if (s.contains(extraIncomingNumber)) { //号码在白名单内修改通话状态,并退出方法
+                            //Log.d(TAG, "PhoneStateReceiver incoming call: " + extraIncomingNumber);
+                            DeviceChannel.calling = true;
+                            Constants.Companion.setPhoneState(Constants.PHONE_INCOMING);
+                            //来电时取消留言
+                            RecordHelper.getInstance().stopCancelRecordByOther(true);
+                            return;
+                        }
+                    }
+                    //循环结束,说明来电号码不在白名单内,挂断来电
                     showMessage("非法来电:" + extraIncomingNumber);
                     boolean res = PhoneCallUtil.endCall(context);
                     Log.d(TAG, "end call result: " + res);
                     PhoneCallUtil.deleteCallLog(context, extraIncomingNumber);
-                } else {
-                    //Log.d(TAG, "PhoneStateReceiver incoming call: " + extraIncomingNumber);
-                    DeviceChannel.calling = true;
-                    Constants.Companion.setPhoneState(Constants.PHONE_INCOMING);
-                    //来电时取消留言
-                    RecordHelper.getInstance().stopCancelRecordByOther(true);
                 }
             } else if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK) && extraIncomingNumber != null) {
                 //通话中

+ 50 - 12
home/src/main/code/com/wdkl/ncs/android/component/home/service/WdKeepAliveService.kt

@@ -13,6 +13,7 @@ import com.google.common.base.Strings
 import com.google.gson.FieldNamingPolicy
 import com.google.gson.Gson
 import com.google.gson.GsonBuilder
+import com.google.gson.reflect.TypeToken
 import com.wdkl.ncs.android.component.home.R
 import com.wdkl.ncs.android.component.home.activity.AppUpdateActivity
 import com.wdkl.ncs.android.component.home.activity.NewEventListActivity
@@ -29,6 +30,7 @@ import com.wdkl.ncs.android.middleware.common.Constants
 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.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.channel.ChannelImUtil
 import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
@@ -36,6 +38,7 @@ 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 com.wdkl.ncs.android.middleware.utils.ContactHelper
 import com.wdkl.ncs.keepbackground.work.AbsWorkService
 import com.wdkl.rtc.rtc.AudioFocusManager
 import org.greenrobot.eventbus.EventBus
@@ -274,12 +277,11 @@ class WdKeepAliveService : AbsWorkService() {
                         EventBus.getDefault().post(MessageEvent(tcpModel, 1))
                     }
                 } else if (tcpModel.getAction() == TcpAction.VoiceAction.ACCEPT //我方呼出,对方接受
-                    || tcpModel.getAction() == TcpAction.VoiceAction.REJECT //我方呼出,对方拒绝
-                    || tcpModel.getAction() == TcpAction.VoiceAction.CALLING //我方呼出,对方通话中
-                    || tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF
-                    || tcpModel.getAction() == TcpAction.VoiceAction.FAILED //我方呼出,对方不在线,设备离线或其它错误
-                    || tcpModel.getAction() == TcpAction.VoiceAction.CANCEL)
-                {
+                        || tcpModel.getAction() == TcpAction.VoiceAction.REJECT //我方呼出,对方拒绝
+                        || tcpModel.getAction() == TcpAction.VoiceAction.CALLING //我方呼出,对方通话中
+                        || tcpModel.getAction() == TcpAction.VoiceAction.HANDOFF
+                        || tcpModel.getAction() == TcpAction.VoiceAction.FAILED //我方呼出,对方不在线,设备离线或其它错误
+                        || tcpModel.getAction() == TcpAction.VoiceAction.CANCEL) {
                     EventBus.getDefault().post(MessageEvent(tcpModel, 2))
                 }
             }
@@ -404,6 +406,42 @@ class WdKeepAliveService : AbsWorkService() {
                 }
             }
 
+            4 -> { //更新通讯录,更新通话白名单列表
+                Thread(Runnable {
+                    var jsonStr = ""
+                    val contactVos = ApiManager.API_WatchManageDevice.getWatchContactList(Constants.deviceId).map {
+                        jsonStr = it.getJsonString()
+                        val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                        val contactList: List<WatchContactVO> = gson.fromJson(jsonStr, object : TypeToken<List<WatchContactVO>>() {}.type)
+                        return@map contactList
+                    }.blockingSingle()
+
+                    if (contactVos != null && contactVos.size > 0) {
+                        for (contactVO in contactVos) {
+                            if (!Strings.isNullOrEmpty(contactVO.name) && !Strings.isNullOrEmpty(contactVO.phoneNumber)) {
+                                ContactHelper.setContact(this, contactVO.name, contactVO.phoneNumber)
+                            }
+                        }
+                    }
+
+                    //更新白名单列表
+                    val whiteList = ApiManager.API_WatchManageDevice.getPhoneWhiteList(Constants.deviceId).map {
+                        jsonStr = it.getJsonString()
+                        val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                        val whiteList: List<String> = gson.fromJson(jsonStr, object : TypeToken<List<String>>() {}.type)
+                        return@map whiteList
+                    }.blockingSingle()
+
+
+                    if (whiteList != null && whiteList.size > 0) {
+                        Constants.phoneWhiteList.clear()
+                        Constants.phoneWhiteList.addAll(whiteList)
+                    }
+
+                }).start()
+
+
+            }
 
             //SOS通道
             999 -> {
@@ -591,22 +629,22 @@ class WdKeepAliveService : AbsWorkService() {
                 //MediaPlayHelper.getInstance().playResMusic(R.raw.sos, 1.0f, true)
                 RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.sos, true)
             }
-        }else if(model.type == TcpType.EVENT && model.action == TcpAction.EventAction.CANCEL){ // 事件取消
-            val eventStr = Util.appendSpace(interactionVO.fromFrameFullName.replace("-", ",")) + ",已取消 " + interactionVO.data+"事件"
+        } else if (model.type == TcpType.EVENT && model.action == TcpAction.EventAction.CANCEL) { // 事件取消
+            val eventStr = Util.appendSpace(interactionVO.fromFrameFullName.replace("-", ",")) + ",已取消 " + interactionVO.data + "事件"
 
             if (Constants.ttsState == 2) {
-                SpeechUtil.getInstance().newSpeech( eventStr, false)
+                SpeechUtil.getInstance().newSpeech(eventStr, false)
             } else {
                 AppTool.Time.delay(500) {
                     //MediaPlayHelper.getInstance().playResMusic(R.raw.new_event, 1.0f, false)
                     RingPlayHelper.playRingTone(BaseApplication.appContext, R.raw.new_event, false)
                 }
             }
-        }else if(model.type == TcpType.EVENT && model.action == TcpAction.EventAction.COMPLETED){ //事件完成
-            val eventStr = Util.appendSpace(interactionVO.fromFrameFullName.replace("-", ",")) + ", " + interactionVO.data+"事件已完成"
+        } else if (model.type == TcpType.EVENT && model.action == TcpAction.EventAction.COMPLETED) { //事件完成
+            val eventStr = Util.appendSpace(interactionVO.fromFrameFullName.replace("-", ",")) + ", " + interactionVO.data + "事件已完成"
 
             if (Constants.ttsState == 2) {
-                SpeechUtil.getInstance().newSpeech( eventStr, false)
+                SpeechUtil.getInstance().newSpeech(eventStr, false)
             } else {
                 AppTool.Time.delay(500) {
                     //MediaPlayHelper.getInstance().playResMusic(R.raw.new_event, 1.0f, false)

+ 4 - 1
middleware/src/main/AndroidManifest.xml

@@ -1,2 +1,5 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.wdkl.ncs.android.middleware"/>
+          package="com.wdkl.ncs.android.middleware">
+
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+</manifest>

+ 4 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/api/WatchManageDeviceApi.kt

@@ -27,4 +27,8 @@ interface WatchManageDeviceApi {
 
     @GET("/watch/getPhoneNumberWhiteList/{deviceId}")
     fun getPhoneWhiteList(@Path("deviceId") deviceId:Int): Observable<ResponseBody>
+
+    //获取通讯录
+    @GET("/watch/getWatchContacts/{deviceId}")
+    fun getWatchContactList(@Path("deviceId") deviceId:Int): Observable<ResponseBody>
 }

+ 5 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/contract/home/WatchHomeActivityContract.kt

@@ -7,6 +7,7 @@ import com.wdkl.ncs.android.middleware.model.dos.DeviceDO
 import com.wdkl.ncs.android.middleware.model.dos.PartSettingDO
 import com.wdkl.ncs.android.middleware.model.dto.TcpSeverDTO
 import com.wdkl.ncs.android.middleware.model.vo.DeviceVO
+import com.wdkl.ncs.android.middleware.model.vo.WatchContactVO
 import com.wdkl.ncs.android.middleware.model.vo.WatchContactsVO
 
 interface WatchHomeActivityContract{
@@ -23,6 +24,8 @@ interface WatchHomeActivityContract{
         fun setDeviceVoiceChannel(channelList: ArrayList<ChannelDO>)
         //手机号码白名单
         fun setPhoneNumberWhiteList(phoneList: ArrayList<String>)
+        //设置通讯录
+        fun setContact(contactsVOs: List<WatchContactVO>)
     }
     interface Presenter : BaseContract.BasePresenter{
         //获取版本信息
@@ -37,5 +40,7 @@ interface WatchHomeActivityContract{
         fun getDeviceVoiceChannel(memberId: Int)
         //获取手机号码白名单
         fun getPhoneNumberWhiteList(deviceId: Int)
+        //获取通讯录
+        fun getWatchContacts(deviceId: Int)
     }
 }

+ 44 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/logic/presenter/home/WatchHomeActivityPresenter.kt

@@ -22,6 +22,8 @@ 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.model.vo.WatchContactsVO
 import io.reactivex.disposables.Disposable
 import javax.inject.Inject
 
@@ -125,6 +127,34 @@ class WatchHomeActivityPresenter @Inject constructor(): RxPresenter<WatchHomeAct
         }
     }
 
+    private val observerContact = object : ConnectionObserver<Any>(){
+        override fun onStartWithConnection() {
+            providerView()
+        }
+
+        override fun onNextWithConnection(result: Any, connectionQuality: ConnectionQuality) {
+            providerView().complete()
+            when (result) {
+                is ArrayList<*> -> {
+                    providerView().setContact(result as ArrayList<WatchContactVO>)
+                }
+            }
+
+        }
+
+        override fun onErrorWithConnection(error: ExceptionHandle.ResponeThrowable, connectionQuality: ConnectionQuality) {
+            providerView().onError(error.customMessage)
+        }
+
+        override fun attachSubscribe(var1: Disposable) {
+            addDisposable(var1)
+        }
+
+        override fun onNoneNet() {
+            getEventCenter().post(NetStateEvent(NetState.NONE))
+        }
+    }
+
     override fun getAppVersion(partId: Int, deviceType: Int) {
         watchManageDeviceApi.getAppVersion(partId, deviceType)
                 .map {
@@ -206,4 +236,18 @@ class WatchHomeActivityPresenter @Inject constructor(): RxPresenter<WatchHomeAct
             .compose(ThreadFromUtils.defaultSchedulers())
             .subscribe(observerPhone)
     }
+
+    override fun getWatchContacts(deviceId: Int) {
+        watchManageDeviceApi.getWatchContactList(deviceId)
+                .map {
+                    val data = it.getJsonString()
+                    val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create()
+                    val contactList: List<WatchContactVO> = gson.fromJson(data, object : TypeToken<List<WatchContactVO>>() {}.type)
+                    
+
+                    return@map contactList
+                }
+                .compose(ThreadFromUtils.defaultSchedulers())
+                .subscribe(observerContact)
+    }
 }

+ 32 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/model/vo/WatchContactVO.java

@@ -0,0 +1,32 @@
+package com.wdkl.ncs.android.middleware.model.vo;
+
+import java.io.Serializable;
+
+/**
+ * @author wuyunfeng
+ * 2021-12-30 11:54
+ * <p>
+ * 手机通讯录
+ */
+public class WatchContactVO implements Serializable {
+
+    private String name;
+
+    private String phoneNumber;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public void setPhoneNumber(String phoneNumber) {
+        this.phoneNumber = phoneNumber;
+    }
+}

+ 220 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/utils/ContactHelper.java

@@ -0,0 +1,220 @@
+package com.wdkl.ncs.android.middleware.utils;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class ContactHelper {
+
+    /**
+     * 插入手机号
+     *
+     * @param context
+     * @param name
+     * @param phoneNumber
+     */
+    public static void insertContact(Context context, String name, String phoneNumber) {
+
+        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
+        ContentResolver resolver = context.getContentResolver();
+
+        ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+        // 操作1.添加Google账号,这里值为null,表示不添加
+        ContentProviderOperation operation = ContentProviderOperation.newInsert(uri)
+                .withValue("account_name", null)// account_name:Google账号
+                .build();
+        // 操作2.添加data表中name字段
+        uri = Uri.parse("content://com.android.contacts/data");
+        ContentProviderOperation operation2 = ContentProviderOperation.newInsert(uri)
+                // 第二个参数int previousResult:表示上一个操作的位于operations的第0个索引,
+                // 所以能够将上一个操作返回的raw_contact_id作为该方法的参数
+                .withValueBackReference("raw_contact_id", 0)
+                .withValue("mimetype", "vnd.android.cursor.item/name")
+                .withValue("data2", name)
+                .build();
+
+        // 操作3.添加data表中phone字段
+        uri = Uri.parse("content://com.android.contacts/data");
+        ContentProviderOperation operation3 = ContentProviderOperation.newInsert(uri)
+                .withValueBackReference("raw_contact_id", 0)
+                .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+                //.withValue("mimetype", "vnd.android.cursor.item/phone_v2")
+                .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
+//                    .withValue("data2", "2")
+                .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber)
+                .build();
+
+        operations.add(operation);
+        operations.add(operation2);
+        operations.add(operation3);
+        try {
+            resolver.applyBatch("com.android.contacts", operations);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     * 判断某个手机号是否存在
+     */
+    public static Integer isPhoneNumbExist(Context context, String phoneNum) {
+        //uri=  content://com.android.contacts/data/phones/filter/#
+        Cursor cursor = null;
+        try {
+            Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/" + phoneNum);
+            ContentResolver resolver = context.getContentResolver();
+            cursor = resolver.query(uri, new String[]{ContactsContract.Data.CONTACT_ID},
+                    null, null, null); //从raw_contact表中返回display_name
+            if (cursor.moveToFirst()) {
+                int contact_id = cursor.getInt(0);
+                Log.i("联系人ID", contact_id + "");
+                cursor.close();
+                return contact_id;
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        return -1;
+    }
+
+
+    /**
+     * 根据姓名查找Contact_id
+     *
+     * @param context
+     * @param name
+     * @return
+     */
+    public static Integer queryRawContactIdByName(Context context, String name) {
+        //根据姓名求id
+        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
+        ContentResolver resolver = context.getContentResolver();
+        Cursor cursor = resolver.query(uri, new String[]{ContactsContract.Data._ID}, "display_name=?", new String[]{name}, null);
+        if (cursor.moveToFirst()) {
+            int id = cursor.getInt(0);
+            cursor.close();
+            return id;
+        }
+        return null;
+    }
+
+    /**
+     * 更新通讯录,需要传入Contact_id
+     *
+     * @param context
+     * @param contact_id
+     * @param phoneNum
+     */
+    public static void updateContact(Context context, Integer contact_id, String phoneNum) {
+        try {
+            Uri uri = Uri.parse("content://com.android.contacts/data");//对data表的所有数据操作
+//
+////        ContentValues values = new ContentValues();
+////        values.put("data", phoneNum);
+////        resolver.update(uri, values, "mimetype=? and raw_contact_id=?", new String[]{"vnd.android.cursor.item/phone_v2",contact_id+""});
+//        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
+//        ContentValues values = new ContentValues();
+//        values.put("data1", phoneNum);
+//        resolver.update(uri, values, "mimetype=? and raw_contact_id=?", new String[]{"vnd.android.cursor.item/phone_v2", contact_id + ""});
+
+            ContentResolver resolver = context.getContentResolver();
+            ContentValues values = new ContentValues();
+            values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNum);
+            values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
+
+            String Where = ContactsContract.Data.RAW_CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
+            String[] WhereParams = new String[]{contact_id + "", ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,};
+
+            resolver.update(uri, values, Where, WhereParams);
+
+
+//        try {
+//            Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
+//            ContentResolver resolver = context.getContentResolver();
+//            Cursor cursor = resolver.query(uri, new String[]{ ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3}, "raw_contact_id=?", new String[]{contact_id + ""}, null);
+//            if (cursor.moveToFirst()) {
+//                int id = cursor.getInt(0);
+//                String data1 = cursor.getString(1);
+//                String data2 = cursor.getString(2);
+//                Log.i("通讯录", "updateContact: id=" + id + ",data1=" + data1 + ",data2=" + data2);
+//            }
+//            cursor.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void setContact(Context context, String name, String phoneNumber) {
+
+        Integer contact_id = queryRawContactIdByName(context, name);
+        Integer contact_id2 = isPhoneNumbExist(context, phoneNumber);
+        Log.i("ContactId", "setContact: " + contact_id2 + "");
+        if (contact_id != null && contact_id > 0) {
+            if (!(contact_id2 != null && contact_id2 > 0 && contact_id.equals(contact_id2))) { //非 通讯录中原有号码和姓名匹配的情况
+                if (contact_id2 != null && contact_id2 > 0) { //号码已经存在了
+                    deleteContact(context, contact_id2); //删除号码
+                }
+                updateContact(context, contact_id, phoneNumber); //更新通讯录
+            }//通讯录姓名和号码没有变化,不用操作
+        } else { //这个名字不存在通讯录中
+            insertContact(context, name, phoneNumber);
+            if(contact_id2!=null&&contact_id2>0){ //电话号码确在通讯录中
+                deleteContact(context, contact_id2); //删除原来号码通讯录
+            }
+
+        }
+
+
+    }
+
+
+    /**
+     * 删除联系人
+     */
+    public static void deleteContact(Context context, String name) {
+
+        //根据姓名求id
+        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
+        ContentResolver resolver = context.getContentResolver();
+        Cursor cursor = resolver.query(uri, new String[]{ContactsContract.Data._ID}, "display_name=?", new String[]{name}, null);
+        if (cursor == null)
+            return;
+
+        if (cursor.moveToFirst()) {
+            int id = cursor.getInt(0);
+            //根据id删除data中的相应数据
+            resolver.delete(uri, "display_name=?", new String[]{name});
+            uri = Uri.parse("content://com.android.contacts/data");
+            resolver.delete(uri, "raw_contact_id=?", new String[]{id + ""});
+        }
+        cursor.close();
+    }
+
+    /**
+     * 删除联系人
+     */
+    public static void deleteContact(Context context, Integer contact_id) {
+        //根据姓名求id
+        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
+        ContentResolver resolver = context.getContentResolver();
+        //根据id删除data中的相应数据
+        resolver.delete(uri, "_id=?", new String[]{contact_id + ""});
+        uri = Uri.parse("content://com.android.contacts/data");
+        resolver.delete(uri, "raw_contact_id=?", new String[]{contact_id + ""});
+
+    }
+
+
+}