首页 > 学院 > 开发设计 > 正文

内容观察者 ContentObserver 监听短信、通话记录数据库 挂断来电

2019-11-09 14:58:45
字体:
来源:转载
供稿:网友

Activity复制代码
public class MainActivity extends ListActivity {    PRivate TextView tv_info;    private SMSContentObserver smsContentObserver;    private CallLogObserver callLogObserver;    private PhoneStateReceiver myReceiver;    @SuppressLint("HandlerLeak")    private Handler mHandler = new Handler() {        public void handleMessage(Message msg) {            String msgBody = (String) msg.obj;            tv_info.setText(msg.obj + ":" + msgBody);        }    };    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        String[] array = { "注册短信数据库变化的观察者", "收件箱数据库……", "删除新来电的通话记录", "监听新来电通话记录的详细信息", "取消注册Observer",//                "注册电话状态改变的广播,当有来电时立即挂断电话", "取消注册广播", };        for (int i = 0; i < array.length; i++) {            array[i] = i + "、" + array[i];        }        ListAdapter mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1new ArrayList<String>(Arrays.asList(array)));        tv_info = new TextView(this);// 将内容显示在TextView中        tv_info.setTextColor(Color.BLUE);        tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);        tv_info.setPadding(20, 10, 20, 10);        getListView().addFooterView(tv_info);        setListAdapter(mAdapter);        myReceiver = new PhoneStateReceiver();    }    @Override    protected void onListItemClick(ListView l, View v, int position, long id) {        switch (position) {        case 0:            smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_WHAT);            getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsContentObserver);            // boolean notifyForDescendents(后裔):若为true,则监视所有以指定的Uri开头的Uri;若为false,则只精确的监视指定的URI            break;        case 1:            smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_INBOX_WHAT);            getContentResolver().registerContentObserver(Uri.parse("content://sms/inbox"), true, smsContentObserver);            break;        case 2:            callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_DELETE_WHAT);            getContentResolver().registerContentObserver(Uri.parse("content://call_log/calls"), true, callLogObserver);            break;        case 3:            callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_QUERY_WHAT);            getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URItrue, callLogObserver);//等价于【Uri.parse("content://call_log/calls")】            break;        case 4:            if (smsContentObserver != null) getContentResolver().unregisterContentObserver(smsContentObserver);            if (callLogObserver != null) getContentResolver().unregisterContentObserver(callLogObserver);            break;        case 5:            registerReceiver(myReceiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED));            break;        case 6:            try {                unregisterReceiver(myReceiver);            } catch (Exception e) {            }            break;        }    }    /**     * 利用aidl及反射自动挂断来电。注意,不能通过ContentResolver监听通话记录数据库来挂断电话,估计是因为电话已接通,不能再挂掉了     */    public void endCall() {        //        IBinder iBinder = ServiceManager.getService(TELEPHONY_SERVICE);//希望调用的方法,但此方法被系统隐藏了        try {            Class<?> clazz = Class.forName("android.os.ServiceManager");//利用反射拿到其字节码文件            Method method = clazz.getDeclaredMethod("getService", String.class);//获取ServiceManager类的getService(String s)方法            IBinder ibinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);//参数为:调用此方法的对象,此方法的参数            ITelephony telephony = ITelephony.Stub.asInterface(ibinder);//把上面getService(String s)得到的IBinder对象转化成【ITelephony】对象            boolean isSuccess = telephony.endCall();//调用ITelephony挂断电话的方法            mHandler.sendMessage(Message.obtain(mHandler, 5, "是否成功挂断电话:" + isSuccess));        } catch (Exception e) {            mHandler.sendMessage(Message.obtain(mHandler, 5, "异常啦" + e.getMessage()));            e.printStackTrace();        }    }    /**监听来电状态的广播*/    class PhoneStateReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            if (intent != null) {                if (TelephonyManager.EXTRA_STATE_RINGING.equalsIgnoreCase(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {//来电状态                    endCall();                }            }        }    }}复制代码
短信数据库的ContentObserver复制代码
/**监听或获取手机短信内容的两种方式 * 方式一:通过注册广播监听短信 *                 这种方式只对新收到的短消息有效,并且系统的这个广播是有序广播,现在在一些定制的系统或是有安全软件的情况下,往往短消息都被截取到,并被干掉。 * 方法二:通过监听短信数据库的变化获取短信 *                 这种方式可以获取手机上所有的短信,包括已读未读的短信,并且不受其它程序干扰 * ContentObserver的使用类似与设计模式中的观察者模式,ContentObserver是观察者,被观察的ContentProvider是被观察者。 * 当被观察者ContentProvider的数据发生了增删改的变化,就会及时的通知给ContentProvider,ContentObsserver做出相应的处理。*/public class SMSContentObserver extends ContentObserver {    private Handler mHandler;    private Context mContext;    /**观察类型:所有内容或仅收件箱*/    private int observerType;    /**观察所有内容*/    public static final int MSG_SMS_WHAT = 1;    /**仅观察收件箱*/    public static final int MSG_SMS_INBOX_WHAT = 2;    public SMSContentObserver(Handler handler, Context context, int observerType) {        super(handler);        this.mHandler = handler;        this.mContext = context;        this.observerType = observerType;    }    @Override    public void onChange(boolean selfChange) {        super.onChange(selfChange);        if (observerType == MSG_SMS_WHAT) {            Uri uri = Uri.parse("content://sms");            Cursor cursor = mContext.getContentResolver().query(uri, new String[] { "_id", "address", "body", "type", "date" }, nullnull, "date desc");            if (cursor != null) {                if (cursor.moveToFirst()) { //最后收到的短信在第一条. This method will return false if the cursor is empty                    int msgId = cursor.getInt(cursor.getColumnIndex("_id"));                    String msgAddr = cursor.getString(cursor.getColumnIndex("address"));                    String msgBody = cursor.getString(cursor.getColumnIndex("body"));                    String msgType = cursor.getString(cursor.getColumnIndex("type"));                    String msgDate = cursor.getString(cursor.getColumnIndex("date"));                    String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(Long.parseLong(msgDate)));                    String msgObj = "收件箱/nId:" + msgId + "/n号码:" + msgAddr + "/n内容:" + msgBody + "/n类型:" + msgType + "/n时间:" + date + "/n";                    mHandler.sendMessage(Message.obtain(mHandler, MSG_SMS_WHAT, msgObj));                }                cursor.close();            }        } else if (observerType == MSG_SMS_INBOX_WHAT) {            Uri uri = Uri.parse("content://sms/inbox");            Cursor cursor = mContext.getContentResolver().query(uri, null, "read = 0", null, "date desc");//Passing null will return all columns, which is inefficient.            //等价于附加条件 if (cursor.getInt(cursor.getColumnIndex("read")) == 0) //表示短信未读。这种方式不靠谱啊,建议用上面的方式!            if (cursor != null) {                StringBuilder sb = new StringBuilder("未读短信/n");                while (cursor.moveToNext()) {                    String sendNumber = cursor.getString(cursor.getColumnIndex("address"));                    String body = cursor.getString(cursor.getColumnIndex("body"));                    sb.append("号码:" + sendNumber + "/n内容:" + body + "/n");                }                mHandler.obtainMessage(MSG_SMS_INBOX_WHAT, sb.toString()).sendToTarget();                cursor.close();            }        }    }}复制代码
利用反射及aidl调用系统隐藏的方法复制代码
目的:       利用反射及aidl调用系统隐藏的ServiceManager的getService方法,获取ITelephony后调用其挂电话的方法步骤:1、copy android源代码【com.android.internal.telephony】包中的【ITelephony.aidl】到自己的项目        为什么要copy这个文件呢?这是因为接听/挂断电话的方法在接口ITelephony.java里面,而这个接口是隐藏的(@hide),我们没权限调用。2、由于ITelephony.aidl关联了【android.telephony】包下的【NeighboringCellInfo.aidl】,所以也需把它拷贝过来。        上面完成之后,就会在你的gen目录下自动生成 ITelephony.java接口文件    3、然后我们就可以利用反射机制来取得ITelephony对象。        为什么要用反射呢?        因为 ITelephony是一个系统服务,要通过【ServiceManager】来获取,但是ServiceManager同样也是隐藏的。        所以,我们首先要通过反射机制拿到系统隐藏的ServiceManager对象        然后调用ServiceManager的【getService(String)】方法来取得远程的【ITelephony】对象, 最后调用ITelephony的endCall()方法挂掉电话权限:    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <uses-permission android:name="android.permission.CALL_PHONE" />复制代码
通话记录数据库的Observer复制代码
/** * 拨号记录的内容观察者。 */public class CallLogObserver extends ContentObserver {    /**观察到记录改变后的处理方式*/    private int type;    /**删除最近的一条通话记录*/    public static final int MSG_CALLLOG_DELETE_WHAT = 3;    /**查询某一个联系人最近的通话记录*/    public static final int MSG_CALLLOG_QUERY_WHAT = 4;    public static final String NUMBER = "17084143285";    private Handler mHandler;    private Uri uri = CallLog.Calls.CONTENT_URI;//等价于【Uri.parse("content://call_log/calls")】    private ContentResolver resolver;    public CallLogObserver(Handler handler, Context context, int type) {        super(handler);        this.mHandler = handler;        this.type = type;        resolver = context.getContentResolver();    }    @Override    public void onChange(boolean selfChange) {        Cursor cursor;        switch (type) {        case MSG_CALLLOG_DELETE_WHAT://删除最近的一条通话记录            resolver.unregisterContentObserver(this);//注意:增删改通话记录后由于数据库发生变化,所以系统会在修改后再发一条广播,这时会重新回调onChange方法            //最终导致的结果就是:一次来电后删除了多条甚至全部通话记录。为防止这种循环启发,必须在更改前就取消注册!事实上,注册的代码应该放在广播接收者中。            cursor = resolver.query(uri, nullnullnull, "_id desc limit 1");//按_id倒序排序后取第一个,即:查询结果按_id从大到小排序,然后取最上面一个(最近的通话记录)            if (cursor != null) {                if (cursor.moveToFirst()) {                    int num = resolver.delete(uri, "_id=?", new String[] { cursor.getInt(cursor.getColumnIndex("_id")) + "" });                    mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_DELETE_WHAT, "删除的记录数量:" + num));                }                cursor.close();            }            break;        case MSG_CALLLOG_QUERY_WHAT://查询某一个联系人最近的通话记录            String[] projection = new String[] { "_id", CallLog.Calls.TYPE, CallLog.Calls.NUMBER, CallLog.Calls.CACHED_NAME, CallLog.Calls.DATE, CallLog.Calls.DURATION };            String selection = "number=? and (type=1 or type=3)";            String[] selectionArgs = new String[] { NUMBER };            String sortOrder = CallLog.Calls.DEFAULT_SORT_ORDER;//按时间排序【date DESC】            cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);            if (cursor != null) {                if (cursor.moveToFirst()) {                    int _id = cursor.getInt(cursor.getColumnIndex("_id"));                    int type = cursor.getInt(cursor.getColumnIndex("type"));//通话类型,1 来电 .INCOMING_TYPE;2 已拨 .OUTGOING_;3 未接 .MISSED_                    String number = cursor.getString(cursor.getColumnIndex("number"));// 电话号码                    String name = cursor.getString(cursor.getColumnIndex("name"));//联系人                    long date = cursor.getLong(cursor.getColumnIndex("date"));//通话时间,即可以用getString接收,也可以用getLong接收                    String formatDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date(date));                    int duration = cursor.getInt(cursor.getColumnIndex("duration"));//通话时长,单位:秒                    String msgObj = "/nID:" + _id + "/n类型:" + type + "/n号码:" + number + "/n名称:" + name + "/n时间:" + formatDate + "/n时长:" + duration;                    mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_QUERY_WHAT, msgObj));                }                cursor.close();            }            break;        }    }}复制代码
清单文件复制代码
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.itheima.ipdail"    android:versionCode="1"    android:versionName="1.0" >    <uses-permission android:name="android.permission.RECEIVE_SMS" />    <uses-permission android:name="android.permission.READ_SMS" />    <uses-permission android:name="android.permission.READ_CALL_LOG" />    <uses-permission android:name="android.permission.WRITE_CALL_LOG" />    <uses-permission android:name="android.permission.CALL_PHONE" />    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <uses-sdk        android:minSdkVersion="17"        android:targetSdkVersion="17" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/APPTheme" >        <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>    </application></manifest>

 

本文固定链接: http://www.ithtw.com/624.html转载请注明: leehom 2015年01月05日 于 IT十万个为什么 发表

ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地ContentObserver也分为“表“ContentObserver、“行”ContentObserver,当然这是与它所监听的Uri MIME Type有关的。熟悉Content Provider(内容提供者)的应该知道,我们可以通过UriMatcher类注册不同类型的Uri,我们可以通过这些不同的Uri来查询不同的结果。根据Uri返回的结果,Uri Type可以分为:返回多条数据的Uri、返回单条数据的Uri。

注册/取消注册ContentObserver方法,抽象类ContentResolver类中的方法原型如下:public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。参数:uri 需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)notifyForDescendents 为false 表示精确匹配,即只匹配该Uri

观察系统里短消息的数据库变化的ContentObserver派生类,SMSContentObserver.java

双击【CTRL+C】复制
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152importandroid.content.Context;  importandroid.database.ContentObserver;  importandroid.database.Cursor;  importandroid.net.Uri;  importandroid.os.Handler;  importandroid.util.Log;        //用来观察系统里短消息的数据库变化  ”表“内容观察者,只要信息数据库发生变化,都会触发该ContentObserver 派生类  publicclass SMSContentObserver extendsContentObserver {      privatestatic String TAG = "SMSContentObserver"           privateint MSG_OUTBOXCONTENT = 2           privateContext mContext  ;      privateHandler mHandler ;   //更新UI线程            publicSMSContentObserver(Context context,Handler handler) {          super(handler);         mContext = context ;          mHandler = handler ;          /**     * 当所监听的Uri发生改变时,就会回调此方法           * @param selfChange  此值意义不大 一般情况下该回调值false      */     @Override     publicvoid onChange(booleanselfChange){          Log.i(TAG,"the sms table has changed");                    //查询发件箱里的内容              Uri outSMSUri = Uri.parse("content://sms/sent") ;                     Cursor c = mContext.getContentResolver().query(outSMSUri, null,null,null,"date desc");         if(c != null){                            Log.i(TAG,"the number of send is"+c.getCount()) ;                             StringBuilder sb = newStringBuilder() ;              //循环遍历             while(c.moveToNext()){                 sb.append("发件人手机号码: "+c.getInt(c.getColumnIndex("address")))                   .append("信息内容: "+c.getString(c.getColumnIndex("body")))                   .append("/n");                         c.close();                       mHandler.obtainMessage(MSG_OUTBOXCONTENT, sb.toString()).sendToTarget();                             

主工程逻辑为MainActivity.java,对短消息的观察Uri,通过测试我发现只能监听此Uri “content://sms” (等同于"content://sms/"),而不能监听其他的Uri,比如"content://sms/outbox"等。

双击【CTRL+C】复制
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748importandroid.app.Activity;  importandroid.database.Cursor;  importandroid.net.Uri;  importandroid.os.Bundle;  importandroid.os.Handler;  importandroid.os.Message;  importandroid.provider.*;  importandroid.util.Log;  importandroid.widget.EditText;  importandroid.widget.TextView;     publicclass MainActivity extendsActivity {         privateTextView tvAirplane;      privateEditText etSmsoutbox;         privateSMSContentObserver smsContentObserver;         /** Called when the activity is first created. */     @Override     publicvoid onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);         setContentView(R.layout.main);            etSmsoutbox = (EditText) findViewById(R.id.smsoutboxContent);             smsContentObserver = newSMSContentObserver(this, mHandler);                     //注册内容观察者         registerContentObservers() ;             privatevoid registerContentObservers() {          // ”表“内容观察者 ,通过测试我发现只能监听此Uri -----> content://sms          // 监听不到其他的Uri 比如说 content://sms/outbox          Uri smsUri = Uri.parse("content://sms");         getContentResolver().registerContentObserver(smsUri,true,smsContentObserver);            privateHandler mHandler = newHandler() {             publicvoid handleMessage(Message msg) {                  String outbox = (String) msg.obj;                  etSmsoutbox.setText(outbox);                         }; 
Activity复制代码
public class MainActivity extends ListActivity {    private TextView tv_info;    private SMSContentObserver smsContentObserver;    private CallLogObserver callLogObserver;    private PhoneStateReceiver myReceiver;    @SuppressLint("HandlerLeak")    private Handler mHandler = new Handler() {        public void handleMessage(Message msg) {            String msgBody = (String) msg.obj;            tv_info.setText(msg.obj + ":" + msgBody);        }    };    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        String[] array = { "注册短信数据库变化的观察者", "收件箱数据库……", "删除新来电的通话记录", "监听新来电通话记录的详细信息", "取消注册Observer",//                "注册电话状态改变的广播,当有来电时立即挂断电话", "取消注册广播", };        for (int i = 0; i < array.length; i++) {            array[i] = i + "、" + array[i];        }        ListAdapter mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array)));        tv_info = new TextView(this);// 将内容显示在TextView中        tv_info.setTextColor(Color.BLUE);        tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);        tv_info.setPadding(20, 10, 20, 10);        getListView().addFooterView(tv_info);        setListAdapter(mAdapter);        myReceiver = new PhoneStateReceiver();    }    @Override    protected void onListItemClick(ListView l, View v, int position, long id) {        switch (position) {        case 0:            smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_WHAT);            getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsContentObserver);            // boolean notifyForDescendents(后裔):若为true,则监视所有以指定的Uri开头的Uri;若为false,则只精确的监视指定的URI            break;        case 1:            smsContentObserver = new SMSContentObserver(mHandler, this, SMSContentObserver.MSG_SMS_INBOX_WHAT);            getContentResolver().registerContentObserver(Uri.parse("content://sms/inbox"), true, smsContentObserver);            break;        case 2:            callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_DELETE_WHAT);            getContentResolver().registerContentObserver(Uri.parse("content://call_log/calls"), true, callLogObserver);            break;        case 3:            callLogObserver = new CallLogObserver(mHandler, this, CallLogObserver.MSG_CALLLOG_QUERY_WHAT);            getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, callLogObserver);//等价于【Uri.parse("content://call_log/calls")】            break;        case 4:            if (smsContentObserver != null) getContentResolver().unregisterContentObserver(smsContentObserver);            if (callLogObserver != null) getContentResolver().unregisterContentObserver(callLogObserver);            break;        case 5:            registerReceiver(myReceiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED));            break;        case 6:            try {                unregisterReceiver(myReceiver);            } catch (Exception e) {            }            break;        }    }    /**     * 利用aidl及反射自动挂断来电。注意,不能通过ContentResolver监听通话记录数据库来挂断电话,估计是因为电话已接通,不能再挂掉了     */    public void endCall() {        //        IBinder iBinder = ServiceManager.getService(TELEPHONY_SERVICE);//希望调用的方法,但此方法被系统隐藏了        try {            Class<?> clazz = Class.forName("android.os.ServiceManager");//利用反射拿到其字节码文件            Method method = clazz.getDeclaredMethod("getService", String.class);//获取ServiceManager类的getService(String s)方法            IBinder ibinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);//参数为:调用此方法的对象,此方法的参数            ITelephony telephony = ITelephony.Stub.asInterface(ibinder);//把上面getService(String s)得到的IBinder对象转化成【ITelephony】对象            boolean isSuccess = telephony.endCall();//调用ITelephony挂断电话的方法            mHandler.sendMessage(Message.obtain(mHandler, 5, "是否成功挂断电话:" + isSuccess));        } catch (Exception e) {            mHandler.sendMessage(Message.obtain(mHandler, 5, "异常啦" + e.getMessage()));            e.printStackTrace();        }    }    /**监听来电状态的广播*/    class PhoneStateReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {            if (intent != null) {                if (TelephonyManager.EXTRA_STATE_RINGING.equalsIgnoreCase(intent.getStringExtra(TelephonyManager.EXTRA_STATE))) {//来电状态                    endCall();                }            }        }    }}复制代码
短信数据库的ContentObserver复制代码
/**监听或获取手机短信内容的两种方式 * 方式一:通过注册广播监听短信 *                 这种方式只对新收到的短消息有效,并且系统的这个广播是有序广播,现在在一些定制的系统或是有安全软件的情况下,往往短消息都被截取到,并被干掉。 * 方法二:通过监听短信数据库的变化获取短信 *                 这种方式可以获取手机上所有的短信,包括已读未读的短信,并且不受其它程序干扰 * ContentObserver的使用类似与设计模式中的观察者模式,ContentObserver是观察者,被观察的ContentProvider是被观察者。 * 当被观察者ContentProvider的数据发生了增删改的变化,就会及时的通知给ContentProvider,ContentObsserver做出相应的处理。*/public class SMSContentObserver extends ContentObserver {    private Handler mHandler;    private Context mContext;    /**观察类型:所有内容或仅收件箱*/    private int observerType;    /**观察所有内容*/    public static final int MSG_SMS_WHAT = 1;    /**仅观察收件箱*/    public static final int MSG_SMS_INBOX_WHAT = 2;    public SMSContentObserver(Handler handler, Context context, int observerType) {        super(handler);        this.mHandler = handler;        this.mContext = context;        this.observerType = observerType;    }    @Override    public void onChange(boolean selfChange) {        super.onChange(selfChange);        if (observerType == MSG_SMS_WHAT) {            Uri uri = Uri.parse("content://sms");            Cursor cursor = mContext.getContentResolver().query(uri, new String[] { "_id", "address", "body", "type", "date" }, null, null, "date desc");            if (cursor != null) {                if (cursor.moveToFirst()) { //最后收到的短信在第一条. This method will return false if the cursor is empty                    int msgId = cursor.getInt(cursor.getColumnIndex("_id"));                    String msgAddr = cursor.getString(cursor.getColumnIndex("address"));                    String msgBody = cursor.getString(cursor.getColumnIndex("body"));                    String msgType = cursor.getString(cursor.getColumnIndex("type"));                    String msgDate = cursor.getString(cursor.getColumnIndex("date"));                    String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date(Long.parseLong(msgDate)));                    String msgObj = "收件箱/nId:" + msgId + "/n号码:" + msgAddr + "/n内容:" + msgBody + "/n类型:" + msgType + "/n时间:" + date + "/n";                    mHandler.sendMessage(Message.obtain(mHandler, MSG_SMS_WHAT, msgObj));                }                cursor.close();            }        } else if (observerType == MSG_SMS_INBOX_WHAT) {            Uri uri = Uri.parse("content://sms/inbox");            Cursor cursor = mContext.getContentResolver().query(uri, null, "read = 0", null, "date desc");//Passing null will return all columns, which is inefficient.            //等价于附加条件 if (cursor.getInt(cursor.getColumnIndex("read")) == 0) //表示短信未读。这种方式不靠谱啊,建议用上面的方式!            if (cursor != null) {                StringBuilder sb = new StringBuilder("未读短信/n");                while (cursor.moveToNext()) {                    String sendNumber = cursor.getString(cursor.getColumnIndex("address"));                    String body = cursor.getString(cursor.getColumnIndex("body"));                    sb.append("号码:" + sendNumber + "/n内容:" + body + "/n");                }                mHandler.obtainMessage(MSG_SMS_INBOX_WHAT, sb.toString()).sendToTarget();                cursor.close();            }        }    }}复制代码
利用反射及aidl调用系统隐藏的方法复制代码
目的:       利用反射及aidl调用系统隐藏的ServiceManager的getService方法,获取ITelephony后调用其挂电话的方法步骤:1、copy android源代码【com.android.internal.telephony】包中的【ITelephony.aidl】到自己的项目        为什么要copy这个文件呢?这是因为接听/挂断电话的方法在接口ITelephony.java里面,而这个接口是隐藏的(@hide),我们没权限调用。2、由于ITelephony.aidl关联了【android.telephony】包下的【NeighboringCellInfo.aidl】,所以也需把它拷贝过来。        上面完成之后,就会在你的gen目录下自动生成 ITelephony.java接口文件    3、然后我们就可以利用反射机制来取得ITelephony对象。        为什么要用反射呢?        因为 ITelephony是一个系统服务,要通过【ServiceManager】来获取,但是ServiceManager同样也是隐藏的。        所以,我们首先要通过反射机制拿到系统隐藏的ServiceManager对象        然后调用ServiceManager的【getService(String)】方法来取得远程的【ITelephony】对象, 最后调用ITelephony的endCall()方法挂掉电话权限:    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <uses-permission android:name="android.permission.CALL_PHONE" />复制代码
通话记录数据库的Observer复制代码
/** * 拨号记录的内容观察者。 */public class CallLogObserver extends ContentObserver {    /**观察到记录改变后的处理方式*/    private int type;    /**删除最近的一条通话记录*/    public static final int MSG_CALLLOG_DELETE_WHAT = 3;    /**查询某一个联系人最近的通话记录*/    public static final int MSG_CALLLOG_QUERY_WHAT = 4;    public static final String NUMBER = "17084143285";    private Handler mHandler;    private Uri uri = CallLog.Calls.CONTENT_URI;//等价于【Uri.parse("content://call_log/calls")】    private ContentResolver resolver;    public CallLogObserver(Handler handler, Context context, int type) {        super(handler);        this.mHandler = handler;        this.type = type;        resolver = context.getContentResolver();    }    @Override    public void onChange(boolean selfChange) {        Cursor cursor;        switch (type) {        case MSG_CALLLOG_DELETE_WHAT://删除最近的一条通话记录            resolver.unregisterContentObserver(this);//注意:增删改通话记录后由于数据库发生变化,所以系统会在修改后再发一条广播,这时会重新回调onChange方法            //最终导致的结果就是:一次来电后删除了多条甚至全部通话记录。为防止这种循环启发,必须在更改前就取消注册!事实上,注册的代码应该放在广播接收者中。            cursor = resolver.query(uri, null, null, null, "_id desc limit 1");//按_id倒序排序后取第一个,即:查询结果按_id从大到小排序,然后取最上面一个(最近的通话记录)            if (cursor != null) {                if (cursor.moveToFirst()) {                    int num = resolver.delete(uri, "_id=?", new String[] { cursor.getInt(cursor.getColumnIndex("_id")) + "" });                    mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_DELETE_WHAT, "删除的记录数量:" + num));                }                cursor.close();            }            break;        case MSG_CALLLOG_QUERY_WHAT://查询某一个联系人最近的通话记录            String[] projection = new String[] { "_id", CallLog.Calls.TYPE, CallLog.Calls.NUMBER, CallLog.Calls.CACHED_NAME, CallLog.Calls.DATE, CallLog.Calls.DURATION };            String selection = "number=? and (type=1 or type=3)";            String[] selectionArgs = new String[] { NUMBER };            String sortOrder = CallLog.Calls.DEFAULT_SORT_ORDER;//按时间排序【date DESC】            cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);            if (cursor != null) {                if (cursor.moveToFirst()) {                    int _id = cursor.getInt(cursor.getColumnIndex("_id"));                    int type = cursor.getInt(cursor.getColumnIndex("type"));//通话类型,1 来电 .INCOMING_TYPE;2 已拨 .OUTGOING_;3 未接 .MISSED_                    String number = cursor.getString(cursor.getColumnIndex("number"));// 电话号码                    String name = cursor.getString(cursor.getColumnIndex("name"));//联系人                    long date = cursor.getLong(cursor.getColumnIndex("date"));//通话时间,即可以用getString接收,也可以用getLong接收                    String formatDate = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date(date));                    int duration = cursor.getInt(cursor.getColumnIndex("duration"));//通话时长,单位:秒                    String msgObj = "/nID:" + _id + "/n类型:" + type + "/n号码:" + number + "/n名称:" + name + "/n时间:" + formatDate + "/n时长:" + duration;                    mHandler.sendMessage(Message.obtain(mHandler, MSG_CALLLOG_QUERY_WHAT, msgObj));                }                cursor.close();            }            break;        }    }}复制代码
清单文件复制代码
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.itheima.ipdail"    android:versionCode="1"    android:versionName="1.0" >    <uses-permission android:name="android.permission.RECEIVE_SMS" />    <uses-permission android:name="android.permission.READ_SMS" />    <uses-permission android:name="android.permission.READ_CALL_LOG" />    <uses-permission android:name="android.permission.WRITE_CALL_LOG" />    <uses-permission android:name="android.permission.CALL_PHONE" />    <uses-permission android:name="android.permission.READ_PHONE_STATE" />    <uses-sdk        android:minSdkVersion="17"        android:targetSdkVersion="17" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <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>    </application></manifest>
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表