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


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(mHandlerthis, SMSContentObserver.MSG_SMS_WHAT);
            getContentResolver().registerContentObserver(Uri.parse("content://sms"), truesmsContentObserver);
            // boolean notifyForDescendents(后裔):若为true,则监视所有以指定的Uri开头的Uri;若为false,则只精确的监视指定的URI
            break;
        case 1:
            smsContentObserver = new SMSContentObserver(mHandlerthis, SMSContentObserver.MSG_SMS_INBOX_WHAT);
            getContentResolver().registerContentObserver(Uri.parse("content://sms/inbox"), truesmsContentObserver);
            break;
        case 2:
            callLogObserver = new CallLogObserver(mHandlerthis, CallLogObserver.MSG_CALLLOG_DELETE_WHAT);
            getContentResolver().registerContentObserver(Uri.parse("content://call_log/calls"), truecallLogObserver);
            break;
        case 3:
            callLogObserver = new CallLogObserver(mHandlerthis, CallLogObserver.MSG_CALLLOG_QUERY_WHAT);
            getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URItruecallLogObserver);//等价于【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(myReceivernew 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(mHandlerMSG_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(urinullnullnull"_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(mHandlerMSG_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(mHandlerMSG_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>





附件列表

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值