34.
挂断电话--实现黑名单拦截
挂断电话需要用到系统的ServiceManager类,但是该类不能直接得到,所以需要用
反射机制
调用该类。
Android没有对外公开结束通话的API,如果需要结束通话,必须使用AIDL与电话管理服务进行通信,并调用服务中的API实现结束通话,方法如下:
1.从Android的源代码中
拷贝
以下文件到项目中:
com.android.internal.telephony包下的
ITelephony.aidl
android.telephony包下的
NeighboringCellInfo.aidl
注意:需要在项目中创建对应的包名存放上面两个aidl文件。
2.
调用ITelephony.endCall()
结束通话:
// 加载ServiceManager的字节码
Class clazz = 当前类名.class.getClassLoader().loadClass("android.os.ServiceManager");
Method method = clazz.getDeclaredMethod("getService", String.class);
IBinder iBinder = (IBinder) method.invoke(null, TELEPHONY_SERVICE);
ITelephony.Stub.asInterface(iBinder).endCall();
挂断电话需要添加权限:CALL_PHONE
。
35.
删除呼叫记录
负责存放呼叫记录的内容提供者源码在ContactsProvider项目下:
源码路径
:
com\android\providers\contacts\CallLogProvider.java
使用到的
数据库
在:
/data/data/com.android.providers.contacts/databases/contacts2.db
表名:calls
呼叫记录的uri是:content://call_log/calls
呼叫记录有三种
类型
:
来电:CallLog.Calls.INCOMING_TYPE (常量值:1)
外拔:CallLog.Calls.OUTGOING_TYPE(常量值:2)
未接:CallLog.Calls.MISSED_TYPE(常量值:3)
需要添加权限:READ_CONTACTS、WRITE_CONTACTS
。
删除呼叫记录最好在内容观察者中删除,因为挂断电话是调用远程服务,如果直接在挂断电话后面写删除呼叫记录,可能会发生这样的情况:电话还未挂断即呼叫记录还未产生,但删除呼叫记录的函数已经执行,当挂断电话后,呼叫记录还是存在的。
36
.内容提供者uri查找方法
首先uri都是以content://开始,然后查看清单文件中对应内容提供者的主机名,然后再到内容提供者.java文件中查找对应的addURI的uri。
37.
接口和回调函数
备份短信时,进度可以是进度条或者进度条对话框显示,如果写备份短信方法的程序员和调用该方法的程序员不是同一个人,那么就会出现耦合,如程序员A写方法,程序员B调用该方法,当程序员B想以进度条的形式显示进度时,程序员A就要把进度条传入备份函数,以便设置进度条的最大值和进度条的进度;但是当程序员B又想以进度条对话框的形式显示进度时,程序员A就要更改自己所写方法的参数了,把传入备份函数的进度条改为进度条对话框,这样耦合就出现了。想要解耦可以利用接口和回调函数:
进度条和进度条对话框都只是需要设置进度条最大值和进度条的进度,程序员A可以在写方法的时候,把设置进度条最大值和进度条进度的方法
抽取到一个接口中(如:BackupCallBack)
,然后在写备份短信的方法时,只需要传入该接口即可,设置进度条最大值和进度条进度时直接就可以用接口中的方法设置了。然后B程序员需要用什么样的进度条实现进度时就由自己完成了,
调用工具类的备份短信方法时,实现接口中的设置方法即可
。程序员A设置进度条最大值和进度时并没有写设置方法的真正实现,真正实现还是由程序员B自己写的。
38.
分页分批加载数据
给ListView注册一个滚动监听器
<span style="font-size:18px;">lv_callsms_safe.setOnScrollListener(new OnScrollListener() {
// 当ListView滚动的状态发生改变时调用此方法
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:// 空闲状态
//判断当前ListView滚动的位置
//获得最后一个可见条目在集合中的位置
int lastposition = lv_callsms_safe.getLastVisiblePosition();
if(lastposition == (infos.size()-1)){
//当前条目是集合中的最后一条记录,需要加载更多的数据
offset += maxnumber;
fillData();
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:// 手指触摸滚动状态
break;
case OnScrollListener.SCROLL_STATE_FLING:// 惯性滑动状态
break;
// 当ListView滚动时调用此方法
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
});</span>
分页加载数据代码:
<span style="font-size:18px;">private void fillData() {
ll_loading.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
@Override
public void run() {
infos = dao.findPart(offset, maxnumber);
runOnUiThread(new Runnable() {
@Override
public void run() {
ll_loading.setVisibility(View.INVISIBLE);
adapter = new myAdapter();
lv_callsms_safe.setAdapter(adapter);
}).start();
}</span>
分批加载数据代码:
分页加载数据,加载新的数据后,之前的数据不会显示在当前的ListView中,相对于分页加载数据,分批加载数据需要添加一些判断,如:新加载的数据要追加到之前数据集合的后面,更新ListView时,也要判断adapter是否已经存在,若不判断,则每次加载新的数据后都会回到数据的顶部,用户体验不好。
<span style="font-size:18px;">private void fillData() {
ll_loading.setVisibility(View.VISIBLE);
new Thread(new Runnable() {
@Override
public void run() {
if(infos == null){
infos = dao.findPart(offset, maxnumber);
}else{
infos.addAll(dao.findPart(offset, maxnumber));
runOnUiThread(new Runnable() {
@Override
public void run() {
ll_loading.setVisibility(View.INVISIBLE);
if(adapter == null){
adapter = new myAdapter();
lv_callsms_safe.setAdapter(adapter);
}else{
adapter.notifyDataSetChanged();
}).start();
}</span>
查找部分数据的sql语句是
:
select number,mode from blacknumber order by _id desc
limit ? offset ?
第一个问号是查询数据的个数,第二个问号是从哪个位置开始查找。limit语句要放到sql语句的最后。
来电:CallLog.Calls.INCOMING_TYPE (常量值:1)
已拨:CallLog.Calls.OUTGOING_TYPE(常量值:2)
未接:CallLog.Calls.MISSED_TYPE(常量值:3)
查看源码中的声明:
android:name="CallLogProvider"
android:authorities="c
public class MyPhoneReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_PHONE_STATE_CHANGED)) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
//
电话
响铃
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d("MyPhoneReceiver", "Incoming call from " + incomingNumber);
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
//
电话
挂断
Log.d("MyPhoneReceiver", "Call ended");
需要在 AndroidManifest.xml 文件中注册广播接收器:
```xml
<receiver android:name=".MyPhoneReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
这样就可以接收来电和
挂断
电话
的广播了。