為了更好地理解這個Demo,我先向大家介紹一下需求與功能。
需求:
每天都會有很多無聊的電話,比如推銷商品等,占用我們大量時間不說,有時候還會打亂我們的思路,擾亂我們的正常生活。所以實現一個對某些號碼(比如陌生號碼,指定號碼/黑名單等)進行攔截以避免受到騷擾,是很有現實用途的。
為了避免程序過分復雜,造成不易學習的麻煩我在這里只實現“如果來電號碼沒在聯系人中,則進行掛斷,並存入xml文件(SharedPreferences)中,並在首頁顯示”,以期達到拋磚引玉的效果。
其實在android在1.1版本后就已經把Phone類的相關API給隱藏起來了,想要用代碼實現掛斷電話的功能,就必須通過AIDL才行,然后利用反射來使用其方法。
第一步:在程序中新建一個包,包名必須為:com.android.internal.telephony,因為要使用aidl。
第二步:在這個包里面新建一個名為ITelephony.aidl的文件,然后在文件里面寫入代碼:
- package com.android.internal.telephony;
- interface ITelephony{
- boolean endCall();
- void answerRingingCall();
- }
然后是要監聽電話狀態,當來電時,檢測來電號碼是否符合攔截標准(這個攔截標准是我們自己定的,你可以攔截指定號碼如實現一個黑名單的功能,我們在這里攔截所有不在聯系人里的號碼,並把此號碼存入文件,以方便在首頁顯示),代碼如下:
- import java.lang.reflect.Method;
- import java.util.ArrayList;
- import com.android.internal.telephony.ITelephony;
- import android.app.Service;
- import android.content.BroadcastReceiver;
- import android.content.ContentResolver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.SharedPreferences;
- import android.content.SharedPreferences.Editor;
- import android.database.Cursor;
- import android.provider.ContactsContract;
- import android.telephony.TelephonyManager;
- import android.util.Log;
- public class PhoneStatReceiver extends BroadcastReceiver{
- String TAG = "tag";
- TelephonyManager telMgr;
- @Override
- public void onReceive(Context context, Intent intent) {
- telMgr = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
- switch (telMgr.getCallState()) {
- case TelephonyManager.CALL_STATE_RINGING:
- String number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
- Log.v(TAG,"number:"+number);
- if (!getPhoneNum(context).contains(number)) {
- SharedPreferences phonenumSP = context.getSharedPreferences("in_phone_num", Context.MODE_PRIVATE);
- Editor editor = phonenumSP.edit();
- editor.putString(number,number);
- editor.commit();
- endCall();
- }
- break;
- case TelephonyManager.CALL_STATE_OFFHOOK:
- break;
- case TelephonyManager.CALL_STATE_IDLE:
- break;
- }
- }
- /**
- * 掛斷電話
- */
- private void endCall()
- {
- Class<TelephonyManager> c = TelephonyManager.class;
- try
- {
- Method getITelephonyMethod = c.getDeclaredMethod("getITelephony", (Class[]) null);
- getITelephonyMethod.setAccessible(true);
- ITelephony iTelephony = null;
- Log.e(TAG, "End call.");
- iTelephony = (ITelephony) getITelephonyMethod.invoke(telMgr, (Object[]) null);
- iTelephony.endCall();
- }
- catch (Exception e)
- {
- Log.e(TAG, "Fail to answer ring call.", e);
- }
- }
- private ArrayList<String> getPhoneNum(Context context) {
- ArrayList<String> numList = new ArrayList<String>();
- //得到ContentResolver對象
- ContentResolver cr = context.getContentResolver();
- //取得電話本中開始一項的光標
- Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
- while (cursor.moveToNext())
- {
- // 取得聯系人ID
- String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
- Cursor phone = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
- ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
- // 取得電話號碼(可能存在多個號碼)
- while (phone.moveToNext())
- {
- String strPhoneNumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
- numList.add(strPhoneNumber);
- Log.v("tag","strPhoneNumber:"+strPhoneNumber);
- }
- phone.close();
- }
- cursor.close();
- return numList;
- }
- }
這里我們要注意以下幾點:
1.PhoneStatReceiver一定要在清單文件(AndroidManifest.xml)中注冊。
2.一定要添加權限
AndroidManifest文件如下:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.xxxx.xxxx"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk android:minSdkVersion="8" />
- <!-- 掛斷手機的權限 -->
- <uses-permission android:name="android.permission.CALL_PHONE"/>
- <!-- 讀取手機狀態的權限 -->
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <!-- 讀content的權限 -->
- <uses-permission android:name="android.permission.READ_CONTACTS" />
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- <activity
- android:name=".MainActivity"
- android:label="@string/app_name" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <!-- 注冊監聽手機狀態 -->
- <receiver android:name=".PhoneStatReceiver">
- <intent-filter android:priority="1000" >
- <action android:name="android.intent.action.PHONE_STATE" />
- </intent-filter>
- </receiver>
- </application>
- </manifest>
其實最到這里,整個攔截功能就已經實現了,但是呢,我們的首頁也不能讓它光禿禿的顯示個Hello World!吧。所以,在MainActivity中,再給大家加點料,就是在listView中顯示所有已經被攔截的電話號碼,代碼如下:
- import java.util.Map;
- import android.app.ListActivity;
- import android.content.Context;
- import android.content.SharedPreferences;
- import android.os.Bundle;
- import android.util.Log;
- import android.widget.ArrayAdapter;
- public class MainActivity extends ListActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- SharedPreferences phonenumSP = getSharedPreferences("in_phone_num", Context.MODE_PRIVATE);
- Map map = phonenumSP.getAll();
- Object[] array = map.keySet().toArray();
- Log.v("tag",map.toString()+map.size());
- ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1,array);
- setListAdapter(adapter);
- }
- }
好了,整個項目就完成了,我們可以攔截騷擾電話了,這只是一個小例子,你可以添加一些控制功能以更加人性化,比如開啟和關閉攔截,可選的攔截時間段,給ListView添加點擊事件使用戶可以把攔截到的電話添加到通訊錄等功能。
不過,這里只是完成了對來電的攔截功能,未對外撥電話進行攔截,下篇博客再寫吧!
參考:http://blog.163.com/wu_zefeng/blog/static/1826291752011312114420975/