由于公司需求,要自定义一款来电秀的app当作周边产品来配合主营的app业务。 以前由于赶项目,没时间整理这块,如今项目告一段落了,如今回头看看感受这个功能仍是挺有意思的,比较有针对性。电话呼入或者呼出的时候,结合公司的业务显示出对应的界面还有挺nice的。然而网上关于这方面的文章比较少,仍是挺有必要整理一下。 来电秀主要功能是监听通话(包括呼出和呼入电话)来实现本身想要的界面效果android
** 实现手机电话状态的监听,主要依靠两个类:TelephoneManger和PhoneStateListener。 ** 今天主要用到的是PhoneStateListener来实现通话状态监听的功能,使用起来比较简单,步骤也比较明了app
<uses-permission Android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
复制代码
class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
}
}
}
复制代码
他的state值有以下几种ide
TelephonyManager.CALL_STATE_IDLE: 空闲状态
TelephonyManager.CALL_STATE_RINGING: 响铃状态
TelephonyManager.CALL_STATE_OFFHOOK: 通话状态
复制代码
操蛋的是,state只有通话状态(CALL_STATE_OFFHOOK),可是并无区分是呼入,仍是呼出的。并且空闲状态(CALL_STATE_IDLE)也并无区分是挂断空闲,仍是没有通话的空闲。不过,仍是有解决方法滴,这个稍后在说。ui
懒癌又犯了,直接上代码了,代码中的注释比较清晰,若是要是有什么不明白的,能够给我留言。^^ /** * 获取来电号码服务 / public class CallService extends Service { /* * 电话服务管理器 / private TelephonyManager telephonyManager; /* * 电话状态监听器 */ private MyPhoneStateListener myPhoneStateListener;spa
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 获取来电号码
LogUtils.e("state","开启服务");
getIncomingCall();
}
@Override
public 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 {
@Override
public 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;
}
}
}
}
复制代码
接下来,咱们来区分通话状态是呼入仍是呼出的,以及空闲状态是挂断空闲,仍是没有通话的空闲。code
1.思考:要想区分呼入和呼出的状态,就要考虑呼入和呼出的状态的区别是什么。 2.回答:呼入和呼出的状态的区别就是一个是别人打进来的,一个是本身主动拨出去的(废话),那么呼入状态是否是就要有响铃呢,呼出状态是没有响铃的呢。 **3.豁然开朗:**那么是否是区分出是否有响铃就能区分呼入和呼出呢,区分是否有响铃,只须要记录上一次的状态就能够了。 **4.联想:(不是广告)**空闲状态是挂断空闲仍是没有通话的空闲,也能够判断上一次状态是否是空闲状态来区分。继承
思考到此结束了,仍是直接奉上代码,若是要是有什么不明白的,能够给我留言。事件
/**
* 获取来电号码服务
*/
public class IncomingCallService extends Service {
/**
* 记录上一个电话状态
*/
private int lastCallState = TelephonyManager.CALL_STATE_IDLE;
/**
* 电话服务管理器
*/
private TelephonyManager telephonyManager;
/**
* 电话状态监听器
*/
private MyPhoneStateListener myPhoneStateListener;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
// 获取来电号码
LogUtils.e("state","开启服务");
getIncomingCall();
}
@Override
public 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 {
@Override
public 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这个包下(本身建立一个包)。 准备工做作好了get
首先就是挂断电话功能:input
/**
* 拒绝接听
*/
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 headset
boolean 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 back
String 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();
}
}
复制代码
谢谢你们的阅读,若是有不足之处请你们指出,斧正。