产品提出一个需求:在来电或通话时获取来电号码(因为内部使用的是虚拟号,需要调接口查询对方的身份)并展示相关信息
先上个效果图 解决方案:在前台服务中注册通话状态的监听,在响铃和通话时可以获取到手机号码,做完相关的逻辑处理后,在前台服务中使用对话框显示 声明权限 监听手机的状态肯定是危险权限的 ,需要我们配置清单中声明后再运行中申请
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" />
在activity中申请权限,申请权限使用的是郭霖大大的PermissionX
dependencies {
...
implementation 'com.guolindev.permissionx:permissionx:1.5.1'
}
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
PermissionX.init(this)
.permissions(Manifest.permission.READ_CALL_LOG,Manifest.permission.READ_CALL_LOG,Manifest.permission.SYSTEM_ALERT_WINDOW)
.onExplainRequestReason { scope, deniedList ->
val message = "需要您开启以下权限才能正常使用"
scope.showRequestReasonDialog(deniedList, message, "去开启", "拒绝")
}
.request { allGranted, _, deniedList ->
if (allGranted) {
initEvent()
} else {
toast("您拒绝了如下权限:$deniedList")
}
}
}else{
initEvent()
}
成功获取所有权限后我们就去启动前台服务
private fun initEvent(){
if (!MyService.live){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(Intent(this,MyService::class.java))
} else {
startService(Intent(this,MyService::class.java))
}
}
}
准备一个前台服务MyService 前台服务还是继承Service即可,不像IntentService需要继承IntentService。一个普通的Service只要通过startForeground设置一个常驻在状态栏通知栏的通知,就是前台服务了,他在内存回收机制中有较低的优先级,更不容易被杀死(类似网易云这种音乐app)
private const val NOTIFICATION_ID=9
class MyService : Service() {
companion object{
var live=false
}
override fun onBind(intent: Intent): IBinder? {
return null
}
override fun onCreate() {
super.onCreate()
startForeground(NOTIFICATION_ID, createForegroundNotification())
}
override fun onDestroy() {
super.onDestroy()
live=false
stopForeground(true)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
live=true
return super.onStartCommand(intent, flags, startId)
}
private fun createForegroundNotification(): Notification {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val notificationChannelId = "notification_channel_id_01"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelName = "Foreground Service Notification"
val importance = NotificationManager.IMPORTANCE_HIGH
val notificationChannel =
NotificationChannel(notificationChannelId, channelName, importance)
notificationChannel.description = "Channel description"
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
notificationChannel.vibrationPattern = longArrayOf(0)
notificationChannel.enableVibration(false)
notificationManager?.createNotificationChannel(notificationChannel)
}
val builder = NotificationCompat.Builder(this, notificationChannelId)
builder.setContentTitle("工作台运行中")
builder.setSmallIcon(R.mipmap.sym_def_app_icon)
builder.setDefaults(DEFAULT_SOUND)
builder.priority = NotificationCompat.PRIORITY_HIGH
builder.setWhen(System.currentTimeMillis())
return builder.build()
}
}
加入监听通话状态和来电号码功能
class MyService : Service() {
private lateinit var telephonyManager: TelephonyManager
private lateinit var mPhoneListener: PhoneStateListener
override fun onCreate() {
super.onCreate()
initEvent()
startForeground(NOTIFICATION_ID, createForegroundNotification())
}
private fun initEvent(){
telephonyManager= getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
mPhoneListener=object :PhoneStateListener(){
override fun onCallStateChanged(state: Int, phoneNumber: String?) {
super.onCallStateChanged(state, phoneNumber)
when(state){
TelephonyManager.CALL_STATE_IDLE->{
Log.i(TAG, "onCallStateChanged: 挂断${phoneNumber}")
onCallFinish()
}
TelephonyManager.CALL_STATE_OFFHOOK->{
toast("接听${phoneNumber}")
Log.i(TAG, "onCallStateChanged: 接听${phoneNumber}")
}
TelephonyManager.CALL_STATE_RINGING->{
toast("响铃${phoneNumber}")
Log.i(TAG, "onCallStateChanged: 响铃${phoneNumber}")
onCalling(phoneNumber)
}
}
}
}
telephonyManager.listen(mPhoneListener,PhoneStateListener.LISTEN_CALL_STATE)
}
private fun onCallFinish(){
}
private fun onCalling(phoneNumber:String?){
}
这样就可以获取到通话状态和来电号码了,现在加上dialog service中直接像activity中一样使用dialog的话会抛出异常,需要在调用show方法前把对话框设置为系统对话框
private fun onCalling(phoneNumber:String?){
val dialog = AlertDialog.Builder(this)
.setTitle("标题")
.setMessage(phoneNumber)
.create()
dialog.window?.run {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY)
}else{
setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
}
dialog.show()
}
}
完成,现在运行后,来电时就是开头截图中的效果了
|