android 通话状态监听(自定义接听挂断按钮与通话界面,根据公司的业务逻辑可以实现自己的来电秀功能)...

news/2024/11/15 3:55:05/

前言:

因为公司需求,要自定义一款来电秀的app当做周边产品来配合主营的app业务。 之前因为赶项目,没时间整理这块,现在项目告一段落了,现在回头看看感觉这个功能还是挺有意思的,比较有针对性。电话呼入或者呼出的时候,结合公司的业务显示出对应的界面还有挺nice的。然而网上关于这方面的文章比较少,还是挺有必要整理一下。 来电秀主要功能是监听通话(包括呼出和呼入电话)来实现自己想要的界面效果

正文:

** 实现手机电话状态的监听,主要依靠两个类:TelephoneManger和PhoneStateListener。 ** 今天主要用到的是PhoneStateListener来实现通话状态监听的功能,使用起来比较简单,步骤也比较明了

第一步:当然是要添加权限

    <uses-permission Android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
复制代码

第二步:自定义一个类,并继承PhoneStateListener类。

	class MyPhoneStateListener extends PhoneStateListener {@Overridepublic void onCallStateChanged(int state, String incomingNumber) {super.onCallStateChanged(state, incomingNumber);}}}
复制代码

他的state值有如下几种

    TelephonyManager.CALL_STATE_IDLE:  空闲状态TelephonyManager.CALL_STATE_RINGING:  响铃状态TelephonyManager.CALL_STATE_OFFHOOK:  通话状态
复制代码

操蛋的是,state只有通话状态(CALL_STATE_OFFHOOK),但是并没有区分是呼入,还是呼出的。而且空闲状态(CALL_STATE_IDLE)也并没有区分是挂断空闲,还是没有通话的空闲。不过,还是有解决方法滴,这个稍后在说。

第三步:定义一个service,并将MyPhoneStateListener这个类放到service中,分别在onCreat和onDestroy调用开启监听和关闭监听的方法。

懒癌又犯了,直接上代码了,代码中的注释比较清晰,如果要是有什么不明白的,可以给我留言。^^ /** * 获取来电号码服务 / public class CallService extends Service { /* * 电话服务管理器 / private TelephonyManager telephonyManager; /* * 电话状态监听器 */ private MyPhoneStateListener myPhoneStateListener;

    	@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 获取来电号码LogUtils.e("state","开启服务");getIncomingCall();}@Overridepublic void onDestroy() {super.onDestroy();// 不获取来电号码LogUtils.e("state","关闭服务");getIncomingCallCancel();}/*** 获取来电号码*/private void getIncomingCall() {// 获取电话系统服务telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);myPhoneStateListener = new MyPhoneStateListener();telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);}/*** 不获取来电号码*/private void getIncomingCallCancel() {telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_NONE);}/*** 电话状态监听器*/class MyPhoneStateListener extends PhoneStateListener {@Overridepublic void onCallStateChanged(int state, String incomingNumber) {super.onCallStateChanged(state, incomingNumber);incomingNumberFinal=incomingNumber;switch (state) {case TelephonyManager.CALL_STATE_RINGING://响铃break;case TelephonyManager.CALL_STATE_OFFHOOK://通话状态break;case TelephonyManager.CALL_STATE_IDLE://空闲状态break;default:break;}}}}
复制代码
  • 接下来,我们来区分通话状态是呼入还是呼出的,以及空闲状态是挂断空闲,还是没有通话的空闲

    1.思考:要想区分呼入和呼出的状态,就要考虑呼入和呼出的状态的区别是什么。 2.回答:呼入和呼出的状态的区别就是一个是别人打进来的,一个是自己主动拨出去的(废话),那么呼入状态是不是就要有响铃呢,呼出状态没有响铃的呢。 **3.豁然开朗:**那么是不是区分出是否有响铃就能区分呼入和呼出呢,区分是否有响铃,只需要记录上一次的状态就可以了。 **4.联想:(不是广告)**空闲状态是挂断空闲还是没有通话的空闲,也可以判断上一次状态是不是空闲状态来区分。

    思考到此结束了,还是直接奉上代码,如果要是有什么不明白的,可以给我留言。

      /*** 获取来电号码服务*/public class IncomingCallService extends Service {/*** 记录上一个电话状态*/private int lastCallState  = TelephonyManager.CALL_STATE_IDLE;/*** 电话服务管理器*/private TelephonyManager telephonyManager;/*** 电话状态监听器*/private MyPhoneStateListener myPhoneStateListener;@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();// 获取来电号码LogUtils.e("state","开启服务");getIncomingCall();}@Overridepublic void onDestroy() {super.onDestroy();// 不获取来电号码LogUtils.e("state","关闭服务");getIncomingCallCancel();}/*** 获取来电号码*/private void getIncomingCall() {// 获取电话系统服务telephonyManager = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);myPhoneStateListener = new MyPhoneStateListener();telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);}/*** 不获取来电号码*/private void getIncomingCallCancel() {telephonyManager.listen(myPhoneStateListener, PhoneStateListener.LISTEN_NONE);}/*** 电话状态监听器*/class MyPhoneStateListener extends PhoneStateListener {@Overridepublic void onCallStateChanged(int state, String incomingNumber) {super.onCallStateChanged(state, incomingNumber);incomingNumberFinal=incomingNumber;switch (state) {case TelephonyManager.CALL_STATE_RINGING://响铃//之前这里写的有误感谢@Bug_liu的细心阅读并指出问题lastCallState=TelephonyManager.CALL_STATE_RINGING//自定义来电界面break;case TelephonyManager.CALL_STATE_OFFHOOK://通话状态//呼入电话if (lastCallState==TelephonyManager.CALL_STATE_RINGING){//自定义呼入界面}//呼出电话else{//自定义呼出界面}lastCallState = TelephonyManager.CALL_STATE_OFFHOOK;break;case TelephonyManager.CALL_STATE_IDLE://无状态//无通话if (lastCallState==TelephonyManager.CALL_STATE_IDLE){}//通话挂断else{lastCallState = TelephonyManager.CALL_STATE_IDLE;//自定义通话挂断界面}break;default:break;}}}}
    复制代码

第四步:挂断电话和接听电话

因为android4.0以上版本把接听电话事件设置为系统权限,所以原来接听电话的方法已经不管用了 系统挂断电话的方法需要ITelephony这个类里的方法,但是这个方法是系统私有的,不能直接调用,所以在自己的工程里创建一个ITelephony.aidl文件(直接上网下载一个就好),放到com.android.internal.telephony这个包下(自己创建一个包)。ITelephony需要依赖NeighboringCellInfo这个类,同样的方式创建一个NeighboringCellInfo.aidl文件(网上下载),放到android.telephony这个包下(自己创建一个包)。 准备工作做好了

首先就是挂断电话功能:

/*** 拒绝接听*/
public static void rejectCall(Context context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {try {Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);IBinder binder = (IBinder) method.invoke(null, new Object[]{Context.TELEPHONY_SERVICE});ITelephony telephony = ITelephony.Stub.asInterface(binder);telephony.endCall();} catch (NoSuchMethodException e) {Log.d("TAG", "", e);} catch (ClassNotFoundException e) {Log.d("TAG", "", e);} catch (Exception e) {}}else{TelephonyManager mTelMgr = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);Class<TelephonyManager> c = TelephonyManager.class;try {Method getITelephonyMethod = c.getDeclaredMethod("getITelephony", (Class[]) null);getITelephonyMethod.setAccessible(true);ITelephony iTelephony = null;System.out.println("End call.");iTelephony = (ITelephony) getITelephonyMethod.invoke(mTelMgr, (Object[]) null);iTelephony.endCall();} catch (Exception e) {e.printStackTrace();System.out.println("Fail to answer ring call.");}}
}
复制代码

接下来就是接听电话功能,因为android4.0以上版本把接听电话事件设置为系统权限,但是4.0以上版本有个方法,那就是可以通过耳机来接听电话的方法没有设置为系统权限(不知道算不算是一个bug)。所以可以模拟耳机接听电话的方式来接通电话,代码如下 注:部分手机(例如小米)用此方法无效,因为手机厂商修改了耳机接听电话的功能权限,比如小米手机,只能用小米特有的耳机接听,如果谁有解决的方法,请在下面留言,万万万分感谢^^

/*** 接听电话*/
public static void acceptCall(Context context) {try {Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);IBinder binder = (IBinder) method.invoke(null, new Object[]{Context.TELEPHONY_SERVICE});ITelephony telephony = ITelephony.Stub.asInterface(binder);telephony.answerRingingCall();} catch (Exception e) {LogUtils.e("TAG", "for version 4.1 or larger");acceptCall_4_1(context);}
}/*** 4.1版本以上接听电话*/
public static void acceptCall_4_1(Context context) {AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);;//模拟无线耳机的按键来接听电话// for HTC devices we need to broadcast a connected headsetboolean broadcastConnected = MANUFACTURER_HTC.equalsIgnoreCase(Build.MANUFACTURER)&& !audioManager.isWiredHeadsetOn();if (broadcastConnected) {broadcastHeadsetConnected(context, false);}try {try {Runtime.getRuntime().exec("input keyevent " +Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));} catch (IOException e) {// Runtime.exec(String) had an I/O problem, try to fall backString enforcedPerm = "android.permission.CALL_PRIVILEGED";Intent btnDown = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK));Intent btnUp = new Intent(Intent.ACTION_MEDIA_BUTTON).putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP,KeyEvent.KEYCODE_HEADSETHOOK));context.sendOrderedBroadcast(btnDown, enforcedPerm);context.sendOrderedBroadcast(btnUp, enforcedPerm);}} finally {if (broadcastConnected) {broadcastHeadsetConnected(context, false);}}
}private static void broadcastHeadsetConnected(Context context, boolean connected) {Intent i = new Intent(Intent.ACTION_HEADSET_PLUG);i.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);i.putExtra("state", connected ? 1 : 0);i.putExtra("name", "mysms");try {context.sendOrderedBroadcast(i, null);} catch (Exception e) {}
}
复制代码

第五步:打开/关闭扬声器功能

android系统扬声器需要使用到音频管理器(AudioManager),具体实现方式如下:

添加权限:<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
复制代码

代码如下(工作项目中没有用到扬声器,但是亲测一下,是有效的)

/*** 打开扬声器*/
public static void openSpeaker(Context context) {try {AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);audioManager.setMode(AudioManager.ROUTE_SPEAKER);currVolume = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);if (!audioManager.isSpeakerphoneOn()) {audioManager.setMode(AudioManager.MODE_IN_CALL);audioManager.setSpeakerphoneOn(true);audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL),AudioManager.STREAM_VOICE_CALL);}} catch (Exception e) {e.printStackTrace();}
}public static void toggleSpeaker(Context context) {AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);am.setMode(AudioManager.MODE_IN_CALL);am.setSpeakerphoneOn(!am.isSpeakerphoneOn());
}/*** 关闭扬声器*/
public static void closeSpeaker(Context context) {try {AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);if (audioManager != null) {if (audioManager.isSpeakerphoneOn()) {audioManager.setSpeakerphoneOn(false);audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, currVolume,AudioManager.STREAM_VOICE_CALL);}}} catch (Exception e) {e.printStackTrace();}
}
复制代码

结束语

谢谢大家的阅读,如果有不足之处请大家指出,斧正。


http://www.ppmy.cn/news/271058.html

相关文章

无人值守安装

自动化安装Linux 一、PXE1.1PXE概念1.2协议分类1.3 优点1.4 批量安装的前置条件1.5 PXE实现的过程详解 二、配置PXE远程安装服务器2.1 思路2.2 配置步骤2.3 配置应答文件总结&#xff1a; 引言&#xff1a; 学会自动安装&#xff0c;安逸的很 一、PXE 1.1PXE概念 PXE 是由 In…

出租车不需要司机了?带你体验无人驾驶出租车!|『智能产品家』第三期

消费者真的了解什么是自动驾驶吗&#xff1f; 不同级别的自动驾驶汽车有什么区别&#xff1f; 自动驾驶系统开发商及汽车制造商真正了解消费者的现实需要吗&#xff1f; 正是这一系列的问题&#xff0c;促使自动驾驶标准制定组织SAE与上海国际汽车城、联合中智行和Velodyne Lid…

电话来电,接听解读

OutgoingCallBroadcaster将在往外拨打电话时被调用&#xff0c;当然执行其onCreate()方法 首先&#xff1a; mPhone PhoneApp.getInstance().phone; Intent intent getIntent(); if (LOGV) Log.v(TAG, "onResume: Got intent " intent "."); S…

无人驾驶入门1:无人驾驶概览

最近给大家分享了《百度Apollo无人驾驶入门课程下载》&#xff0c;我也学习了一些&#xff0c;把我做的笔记分享给大家。 第一课&#xff1a;无人驾驶概览 课程主要介绍了无人驾驶车的关键部分以及Apollo的架构&#xff0c;带大家入门无人驾驶技术。 1.欢迎学习Apollo课程 …

Android拨打、接听、挂断电话操作

Android2.3之前的系统可以通过反射机制调用ITelephone的方法来挂断电话&#xff0c;因为Android2.3以后增加了对permission android.permission.MODIFY_PHONE_STATE 的限制&#xff0c;之前的反射的方法不能用了&#xff0c;我们可以通过发送广播的方式来接听电话。 示例代码…

python安装使用Flask框架(Vscode)

编译器&#xff1a;VsCode&#xff0c;python3.** 首先安装&#xff0c;在终端输入 pip install flask安装成功后新建文件&#xff0c;app.python&#xff0c;创建一个简单的Web应用。 from flask import Flaskapp Flask(__name__)app.route(/) def hello():return Hello, …

Android中自动接听电话的功能

最近&#xff0c;需要完成一个自动化测试工具&#xff0c;来测试一些模块的功能&#xff0c;其中有些功能需要接通电话后才可以使用&#xff0c;因为这个工具是完全自动化的&#xff0c;所以需要处理自动接听电话 在4.1以前Android是提供了接口可以直接调用接听电话的&#xff…

android 监听电话状态 来电 接听 挂断

如果想要监听手机的来电状态 需要接收手机的电话广播 首先是静态注册 <receiver android:name".PhoneReceiver"android:exported"true"android:enabled"true"><intent-filter><action android:name"android.intent.actio…