android系统提供了电话相关的接口供调用,比如获取电话状态,获取手机服务等,也包括获取电话状态。因此可以根据电话状态的不同做不同操作。
本例主要分析一个来电自动接电话的代码,代码是由别人写的,拿来一起学习:
要想监听电话状态,一般的做法是写一个广播接收器监听电话状态的改变,配置文件如下:
<receiver android:name=".AutoAnswerReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
这个时候如果电话状态改变,比如来电等,就可以进入代码,代码如下:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.media.AudioManager;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.TelephonyManager;
public class AutoAnswerReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Load preferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// Check phone state
String phone_state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if (phone_state.equals(TelephonyManager.EXTRA_STATE_RINGING) && prefs.getBoolean("enabled", false)) {
// Check for "second call" restriction
if (prefs.getBoolean("no_second_call", false)) {
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
if (am.getMode() == AudioManager.MODE_IN_CALL) {
return;
}
}
// Check for contact restrictions
String which_contacts = prefs.getString("which_contacts", "all");
if (!which_contacts.equals("all")) {
int is_starred = isStarred(context, number);
if (which_contacts.equals("contacts") && is_starred < 0) {
return;
}
else if (which_contacts.equals("starred") && is_starred < 1) {
return;
}
}
// Call a service, since this could take a few seconds
context.startService(new Intent(context, AutoAnswerIntentService.class));
}
}
// returns -1 if not in contact list, 0 if not starred, 1 if starred
private int isStarred(Context context, String number) {
int starred = -1;
Cursor c = context.getContentResolver().query(
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, number),
new String[] {PhoneLookup.STARRED},
null, null, null);
if (c != null) {
if (c.moveToFirst()) {
starred = c.getInt(0);
}
c.close();
}
return starred;
}
}
暂且不管其他代码,因为其他的代码主要是对设置选项分别作判断,如果电话状态改变,则启动自动接听服务,即:context.startService(new Intent(context, AutoAnswerIntentService.class));
这样就启动了自动接听服务,在这个代码中,使用到了蓝牙接听电话的逻辑,也大概做一介绍:
先看代码如下:
protected void onHandleIntent(Intent intent) {
Context context = getBaseContext();
// Load preferences
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
BluetoothHeadset bh = null;
if (prefs.getBoolean("headset_only", false)) {
bh = new BluetoothHeadset(this, null);
}
// Let the phone ring for a set delay
try {
Thread.sleep(Integer.parseInt(prefs.getString("delay", "2")) * 1000);
} catch (InterruptedException e) {
// We don't really care
}
// Check headset status right before picking up the call
if (prefs.getBoolean("headset_only", false) && bh != null) {
if (bh.getState() != BluetoothHeadset.STATE_CONNECTED) {
bh.close();
return;
}
bh.close();
}
// Make sure the phone is still ringing
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (tm.getCallState() != TelephonyManager.CALL_STATE_RINGING) {
return;
}
// Answer the phone
try {
answerPhoneAidl(context);
}
catch (Exception e) {
e.printStackTrace();
Log.d("AutoAnswer","Error trying to answer using telephony service. Falling back to headset.");
answerPhoneHeadsethook(context);
}
// Enable the speakerphone
if (prefs.getBoolean("use_speakerphone", false)) {
enableSpeakerPhone(context);
}
return;
}
先判断是否设置为蓝牙耳机接电话,如果不是则使用普通方式接听,当然先确保此时电话还在响铃,因为有时候电话也可能只响一声。然后就自动接电话,见answerPhoneAidl(context); ,其中接电话的这段代码如下:
// Answer the phone
try {
answerPhoneAidl(context);
}
catch (Exception e) {
e.printStackTrace();
Log.d("AutoAnswer","Error trying to answer using telephony service. Falling back to headset.");
answerPhoneHeadsethook(context);
}
先使用一般的aidl方式接听电话,如果出现异常,则模拟一个按键来接听电话,两段代码见下:
private void answerPhoneHeadsethook(Context context) {
// Simulate a press of the headset button to pick up the call
Intent buttonDown = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonDown.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonDown, "android.permission.CALL_PRIVILEGED");
// froyo and beyond trigger on buttonUp instead of buttonDown
Intent buttonUp = new Intent(Intent.ACTION_MEDIA_BUTTON);
buttonUp.putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HEADSETHOOK));
context.sendOrderedBroadcast(buttonUp, "android.permission.CALL_PRIVILEGED");
}
@SuppressWarnings("unchecked")
private void answerPhoneAidl(Context context) throws Exception {
// Set up communication with the telephony service (thanks to Tedd's Droid Tools!)
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
Class c = Class.forName(tm.getClass().getName());
Method m = c.getDeclaredMethod("getITelephony");
m.setAccessible(true);
ITelephony telephonyService;
telephonyService = (ITelephony)m.invoke(tm);
// Silence the ringer and answer the call!
telephonyService.silenceRinger();
telephonyService.answerRingingCall();
}
一般由上述代码就可以接电话了,或许在某些手机上,如果本身更改了framework代码实现,可能会接不起来,需要另作修改。
本文开始提到的设置选项就是该应用启动界面,是用PreferenceActivity写的,见下:
public class AutoAnswerPreferenceActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener
然后加载xml,用户可以设置很多选项参数。
运行界面见下:
另外,本例中还有一个开机启动类,配置文件见下:
<receiver android:name=".AutoAnswerBootReceiver" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
可以设置notification提示用户
public void onReceive(Context context, Intent intent) {
AutoAnswerNotifier notifier = new AutoAnswerNotifier(context);
notifier.updateNotification();
}
AutoAnswerNotifier主要方法就是可以设置提示或者取消提示:
private void enableNotification() {
// Intent to call to turn off AutoAnswer
Intent notificationIntent = new Intent(mContext, AutoAnswerPreferenceActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
// Create the notification
Notification n = new Notification(R.drawable.stat_sys_autoanswer, null, 0);
n.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
n.setLatestEventInfo(mContext, mContext.getString(R.string.notification_title), mContext.getString(R.string.notification_text), pendingIntent);
mNotificationManager.notify(NOTIFICATION_ID, n);
}
private void disableNotification() {
mNotificationManager.cancel(NOTIFICATION_ID);
}
最后,别忘记添加权限,因为操作电话等相关服务需要相关权限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />