51_黑名单号码数据库的创建&曾删改查_40
1、演示金山卫士的拦截效果;
2、创建手机防盗页面CallSmsSafeActivity,并在功能清单文件注册。
3、添加跳转逻辑,并实现布局文件,头部采用相对布局;
4、在com.itheima.mobile.db目录下创建数据库打开帮助类BlackNumberDBOpenHelper,它是继承SQLiteOpenHelper的;
5、讲解构造方法的参数和OnCreate方法什么时候执行;
6、创建数据库的表结构
//创建表 blacknumber 主键_id自增长 ,number黑名单号码,mode拦截模式:1电话拦截 2短信拦截 3全部拦截
db.execSQL("create table blacknumber (_id integer primary key autoincrement,number varchar(20),mode varchar(2))");
7、创建新包com.itheima.mobilesafe.test并创建测试数据类TestBlackNumberDB 继承AndroidTestCase
8、创建数据库方法testCreateDB()
public void testCreateDb(){
BlackNumberDBOpenHelper helper = new BlackNumberDBOpenHelper(getContext());
helper.getWritableDatabase();
}
9、测试报错后,添加测试框架相关参数;
测试框架(放在manifest根节点)
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.itheima.mobilesafe" />
依赖库(放在application里面)
<uses-library android:name="android.test.runner" />
运行测试代码,查看是否已经成功创建数据库,导出用工具打开查看列表信息;
10、数据的增、删、改、查的实现 com.itheima.mobilesafe.db.dao
创建类BlackNumberDao 在里面实现增删改查;
A:在构造方法里创建数据库;
helper = new BlackNumberDBOpenHelper(context);
B:数据库的添加add(String number,String mode)
SQLiteDatabase db= helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("number", number);
values.put("mode", mode);
//第二个参数:当内容为空时
db.insert("blacknumber", null, values);
db.close();
nullColumnHack
当values参数为空或者里面没有内容的时候,insert是会失败的(底层数据库不允许插入一个空行),为了防止这种情况,要在这里指定一个列名,到时候如果发现将要插入的行为空行时,就会将你指定的这个列名的值设为null,然后再向数据库中插入。
通过观察源码的insertWithOnConflict方法可以看到当ContentValues类型的数据initialValues为null或size<=0时,就会在sql语句中添加nullColumnHack的设置。
C:数据库的删除delete(String number)
SQLiteDatabase db= helper.getWritableDatabase();
db.delete("blacknumber", "number=?", new String[]{number});
db.close();
D:数据的修改update(String number ,String newMode):
SQLiteDatabase db= helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("mode", newMode);
db.update("blacknumber", values, "number=?", new String[]{number});
db.close();
E:查询黑名单是否存在该号码find(String number)
boolean result = false;
SQLiteDatabase db= helper.getWritableDatabase();
Cursor cursor = db.query("blacknumber", null, "number=?", new String[]{number}, null, null, null);
if(cursor.moveToNext()){
result = true;
}
cursor.close();
db.close();
return result;
F:查询拦截模式findMode(String number)
String mode = null;
SQLiteDatabase db= helper.getWritableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"mode"}, "number=?", new String[]{number}, null, null, null);
if(cursor.moveToNext()){
mode = cursor.getString(0);
}
cursor.close();
db.close();
return mode;
11、写测试代码
public void testadd() {
BlackNumberDao blackNumberDao = new BlackNumberDao(getContext());
blackNumberDao.add("119", "1");
}
public void testdelete() {
BlackNumberDao blackNumberDao = new BlackNumberDao(getContext());
blackNumberDao.delete("119");
}
public void testupdate() {
BlackNumberDao blackNumberDao = new BlackNumberDao(getContext());
blackNumberDao.update("119", "2");
}
public void testfind() {
BlackNumberDao blackNumberDao = new BlackNumberDao(getContext());
boolean find = blackNumberDao.find("119");
assertEquals(true, find);
}
public void testfindMode() {
BlackNumberDao blackNumberDao = new BlackNumberDao(getContext());
String mode = blackNumberDao.findMode("119");
System.out.println("拦截模式:" + mode);
}
知识拓展
assertEquals:用于判断实际值和期望值是否相同
assertSame:判断实际值和期望值是否为同一个对象
12.进入模拟器查看数据
A:列出两个模拟器命令:adb devices
B: 进入指导模拟器命令:adb -s emulator-5554 shell
C:进入到data/data/com.ithiema.mobilesafe/databases目录下
D:打开数据库命令:sqlite3 blacknumber.db
F: 查询表内容SQL语句:select * from blacknumber;
知识拓展:
Android使用getWritableDatabase()和getReadableDatabase()方法都可以获取一个用于操作数据库的SQLiteDatabase实例。(getReadableDatabase()方法中会调用getWritableDatabase()方法)
其中getWritableDatabase() 方法以读写方式打开数据库,一旦数据库的磁盘空间满了,数据库就只能读而不能写,倘若使用的是getWritableDatabase() 方法就会出错。
getReadableDatabase()方法则是先以读写方式打开数据库,如果数据库的磁盘空间满了,就会打开失败,当打开失败后会继续尝试以只读方式打开数据库。如果该问题成功解决,则只读数据库对象就会关闭,然后返回一个可读写的数据库对象。
52_黑名单号码界面的展现_10
1、便于展示先添加100条数据
Random random = new Random();
//13512340001
for(int i = 0 ;i< 100;i++ ){
blackNumberDao.add("1351234000"+i, String.valueOf(random.nextInt(3)+1));
}
2.增加到所有黑名单数据的接口List<BlackNumberInfo> findAll()
List<BlackNumberInfo> infos = new ArrayList<BlackNumberInfo>();
SQLiteDatabase database = helper.getWritableDatabase();
Cursor cursor = database.query("blacknumber", new String[]{"number","mode"}, null, null, null, null, null);
while(cursor.moveToNext()){
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
String mode = cursor.getString(1);
info.setMode(mode);
info.setNumber(number);
infos.add(info);
}
cursor.close();
database.close();
return infos;
3.初始化ListView 显示数据
初始化数据
dao= new BlackNumberDao(this);
list = dao.findAll();
数据显示:
public int getCount() {
// TODO Auto-generated method stub
return list.size();
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = new TextView(getApplicationContext());
view.setText(list.get(position).toString());
return view;
}
运行演示测试效果
53_ListView的简单优化_35
1、自定义每条的样式创建相对布局文件list_callsmssafe_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginTop="5dip"
android:text="5554"
android:textColor="#000000"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_number"
android:layout_marginLeft="5dip"
android:layout_marginTop="1dip"
android:text="拦截模式"
android:textColor="#99000000"
android:textSize="16sp" />
<ImageView
android:layout_centerVertical="true"
android:src="@drawable/delete_button"
android:layout_alignParentRight="true"
android:layout_width="40dip"
android:layout_height="40dip" />
</RelativeLayou
2、在getView里面实现显示代码
if("1".equals(info.getMode())){
tv_mode.setText("拦截电话");
}else if("2".equals(info.getMode())){
tv_mode.setText("拦截短信");
}else if("3".equals(info.getMode())){
tv_mode.setText("拦截电话+短信");
}
3、ListView的简单优化
View view;
if(convertView != null){
view = convertView;
Log.i(TAG, "使用历史缓存的显示数据"+position);
}else{
Log.i(TAG, "创建新的View的显示数据"+position);
view =View.inflate(CallSmsSafeActivity.this, R.layout.list_callsmssafe_item, null);
}
4.进一步优化
view.findViewById(R.id.tv_number);该代码查找孩子的时候比较耗时,消耗资源,画图分析原理如下;
5、代码实现
A:定义容器
static class ViewHolder{
TextView tv_number;
TextView tv_mode;
}
B:当创建View的时候实例化容器;
ViewHolder holder;
if(convertView != null){
view = convertView;
holder = (ViewHolder) view.getTag();
}else{
//初始化容器
holder = new ViewHolder();
设置与该视图相关联的标记。一个标签可以用来标记一个视图层次结构和层次结构内不必是惟一的。标签也可以用来存储数据在一个视图没有求助于另一个数据结构
view.setTag(holder);
}
C:使用
holder.tv_number.setText(info.getNumber());
if("1".equals(info.getMode())){
holder.tv_mode.setText("拦截电话");
}else if("2".equals(info.getMode())){
holder.tv_mode.setText("拦截短信");
}else if("3".equals(info.getMode())){
holder.tv_mode.setText("拦截电话+短信");
}
性能提高百分之五左右。
54_ListView的加载UI效果优化_8
1、解决数据库内容很多的情形;
findAll()在该方法休眠3秒演示效果
2、创建一个线程去读取数据:
new Thread() {
public void run() {
list = dao.findAll();//查询数据库得到数据
handler.sendEmptyMessage(0);//发消息给主线程更新数据
};
}.start();
3、更新数据
//更新数据
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
lv_call_sms_safe.setAdapter(new CallSmsSafeAdapter());
};
};
4、没数据的时候加一个提醒效果,写布局文件
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/lv_call_sms_safe"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<LinearLayout
android:id="@+id/ll_loading"
android:gravity="center"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="给力加载中..."
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</FrameLayout>
5、代码配合处理
ll_loading.setVisibility(View.VISIBLE);//没数据的时候显示
ll_loading.setVisibility(View.INVISIBLE);//数据加载好了隐藏
55_ListView的数据分批加载_41
1、讲解分批加载的好处:不用等待太久、节约流量、慢慢引导用户看感兴趣内容;
2、导出数据库,打开数据库的分批查询,写SQL语句:
select number ,mode from blacknumber limit 10 offset 10 //查询部分的数据
基于findAll()修改
public List<BlackNumberInfo> findPart(int startIndex){
try {
Thread.sleep(600);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
List<BlackNumberInfo> infos = new ArrayList<BlackNumberInfo>();
SQLiteDatabase database = helper.getWritableDatabase();
Cursor cursor = database.rawQuery("select number ,mode from blacknumber limit 20 offset ?", new String[]{startIndex+""});
while(cursor.moveToNext()){
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
String mode = cursor.getString(1);
info.setMode(mode);
info.setNumber(number);
infos.add(info);
}
cursor.close();
database.close();
return infos;
}
然后使用,运行演示;
3、监听拖动到末尾
lv_call_sms_safe.setOnScrollListener(new OnScrollListener() {
/**
* 当滚动状态发生改变的时候调用这个方法
* 静止--->滚动
* 滚动--->静止
* 手指触摸滑动--->惯性滚动
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE://空闲状态
int position = lv_call_sms_safe.getLastVisiblePosition();//19 最后一条显示的位置
int total = list.size();//20总的数据有多少
if(position == (total-1)){
//最后一条了,该去加数据了
Toast.makeText(getApplicationContext(), "加载更多数据", 0).show();
}
break;
case OnScrollListener.SCROLL_STATE_FLING://惯性滑动
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL://触摸滚动
default:
break;
}
}
/**
* 当滚动的时候调用该方法
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});
4、加载数据,抽取线程代码为方法fillData()、指定加载位置定义成类的成员变量startIndex,然后在监听到末尾处加载数据。
定义类的成员变量:
private int startIndex = 0;
加载数据:
if(position == (total-1)){
//最后一条了,该去加重数据了
Toast.makeText(getApplicationContext(), “加载”, 0).show();
//startIndex = startIndex +20;
ll_loading.setVisibility(View.VISIBLE);//显示加载
startIndex += 20;
fillData();
}
5、处理数据被覆盖的问题
在fillData()方法里面代码修改:
if(list == null){
list = dao.findPart(startIndex);//查询数据库得到数据
}else{
//已经有数据了
list.addAll(dao.findPart(startIndex));
}
6、让数据继续停留在当前位置
两种实现方式
第一种:lv_call_sms_safe.setSelection(startIndex);//不推荐
第二种:重复利用适配器,数据变化通知一下就行了
private CallSmsSafeAdapter adapter;
Handler里面修改成:
if(adapter == null){
adapter = new CallSmsSafeAdapter();
lv_call_sms_safe.setAdapter(adapter);
}else{
//通知数据适配器更新一下界面
adapter.notifyDataSetChanged();
}
7、防止重复加载
定义成员变量
private boolean isLoading = false;
在加载的过程中
if(isLoading){
return;
}
isLoading = true;
加载好后 handler里面处理
isLoading = false;
8、处理拖动到所有数据的最后一条时的处理
得到数据库一共有多少条数据
/**得到数据的总数
* @return
*/
public int getTotalCount(){
int count = 0;
SQLiteDatabase database = helper.getWritableDatabase();
Cursor cursor = database.rawQuery("select count(*) from blacknumber", null);
if(cursor.moveToNext()){
count = cursor.getInt(0);
}
cursor.close();
database.close();
return count;
}
代码处理
if(startIndex >= total){
Toast.makeText(getApplicationContext(), "已经最后一条了", 0).show();
return;
}
总结分批处理:
分批处理 解决的时候时间等待的问题
不能解决内存占用的问题。
要想解决内存占用问题,可以采用分页方式;
56_黑名单号码的添加和删除_32
1、画图添加黑名单号码对话框
2、创建布局文件dialog_add_blacknumber.xml(添加黑名单号码)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:layout_width="300dip"
android:layout_height="50dip"
android:background="#66ff6600"
android:gravity="center"
android:text="添加黑名单号码"
android:textColor="#000000"
android:textSize="22sp" />
<EditText
android:layout_width="280dip"
android:layout_height="wrap_content"
android:hint="请输入电话号码"
android:inputType="phone" />
<RadioGroup
android:id="@+id/radioGroup1"
android:layout_width="280dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/radio0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="电话" />
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="短信" />
<RadioButton
android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="全部" />
</RadioGroup>
<LinearLayout
android:layout_width="280dip"
android:layout_height="wrap_content" >
<Button
android:layout_width="140dip"
android:layout_height="wrap_content"
android:background="@drawable/button_bg"
android:text="确定" />
<Button
android:layout_width="140dip"
android:layout_height="wrap_content"
android:background="@drawable/button_bg"
android:text="取消" />
</LinearLayout>
</LinearLayout>
3、添加点击事件addBlackNumber
public void addBlackNumber(View view){
AlertDialog.Builder builder = new Builder(this);
AlertDialog dialog = builder.create();
View contentView = View.inflate(this, R.layout.dialog_add_blacknumber, null);
dialog.setView(contentView, 0, 0, 0, 0);
dialog.show();
}
运行演示
4、处理按钮点击事件
定义id :et_blacknumber、rg_mode、rb_all、rb_phone、rb_sms、ok、cancel
点击事件确定
// 判断是否有号码
String number = et_blacknumber.getText().toString().trim();
if (TextUtils.isEmpty(number)) {
Toast.makeText(CallSmsSafeActivity.this, "号码为空", 0).show();
return;
}
int id = rg_mode.getCheckedRadioButtonId();
String mode = "3";//拦截模式
switch (id) {
case R.id.rb_all:
mode = "3";
break;
case R.id.rb_phone:
mode = "1";
break;
case R.id.rb_sms:
mode = "2";
break;
}
dao.add(number, mode);
dialog.dismiss();
totalCount = dao.getTotalCount();//得到总数
5、倒序显示数据
修改SQL语句:
select number ,mode from blacknumber order by _id desc limit 20 offset ?
刷新界面
直接把当前对象添加到当前集合列表里面
BlackNumberInfo info = new BlackNumberInfo();
info.setMode(mode);
info.setNumber(number);
//直接加到当前的列表里面去
list.add(0, info);
adapter.notifyDataSetChanged();//更新数据
6、删除数据
listView的item默认是有点击效果的
画图回顾事件传递过程
在getView()方法里点击删除的实现
holder.iv_delete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("点击位置:"+position);
BlackNumberInfo blackNumberInfo = list.get(position);
String number = blackNumberInfo.getNumber();
//删除该号码数据库里面的黑名单信息
dao.delete(number);
//把要删除的黑名单在当前列表移除
list.remove(blackNumberInfo);
//更新界面
adapter.notifyDataSetChanged();
}
});
优化建议:写对话框去提示是否确定删除;
57_拦截短信_22
1、回顾之前做过的短信拦截
2、为了支持手动开启和关闭拦截短信,在服务(CallSmsSafeService)里面创建广播接收者(InnerSmsReceiver),并在功能清单文件注册;
public class CallSmsSafeService extends Service {
public static final String TAG = "CallSmsSafeService";
private BlackNumberDao dao;
private InnerSmsReceiver receiver;
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
class InnerSmsReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "内部类的广播接收者--收到一条短信了");
//接收短信
Object [] objs = (Object[]) intent.getExtras().get("pdus");
for(Object obj : objs){
SmsMessage sms = SmsMessage.createFromPdu((byte[]) obj);
String sender = sms.getOriginatingAddress();//得到一个电话号码
//看一看这个电话号码是否是黑名单里面的
String mode = dao.findMode(sender);
if("2".equals(mode)||"3".equals(mode)){
abortBroadcast();//把这个广播终止掉
}
}
}
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
dao = new BlackNumberDao(this);
//用代码注册广播接收者
receiver = new InnerSmsReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(receiver, filter);
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
unregisterReceiver(receiver);
receiver = null;
}
}
3、在设置中心里添加开启和关闭短信拦截服务的功能
在activity_setting.xml里增加开启拦截服务的勾选框
<com.itheima.mobilesafe.ui.SettingItemView
android:id="@+id/siv_callsmssafe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
itheima:desc_off="设置黑名单拦截已经关闭"
itheima:desc_on="设置黑名单拦截已经开启"
itheima:title="设置黑名单拦截" >
</com.itheima.mobilesafe.ui.SettingItemView>
在SettingActvity代码处理点击事件,开启服务和关闭服务
运行演示
4、智能拦截模式原理简介
打开金山手机卫士,查看智能拦截模式;
解压金山手机卫士APK ,进入assets目录 ,找到数据库firewall_sys_rules.db
拦截短信内容带广告的
String body = sms.getMessageBody();
//查询数据库
if(body.contains("fapiao")){
Log.i(TAG, "拦截到卖发票的短信");
abortBroadcast();
}
拦截到的短信如何处理?
一般会用数据库保存
A:便于再次查看;
B:有些人喜欢看垃圾短信;
C:防止拦截误判 例如--你看我头发票不漂亮;luncene分词检索框架
5、讲解功能清单文件注册接收短信和代码注册接收短信谁优先收到;
A、打日志演示,优先级一样高的情况下代码注册的比功能清单文件的快;
B、根据系统漏掉设置最大Int值
filter.setPriority(Integer.MAX_VALUE);
C、在布局文件也加上2147483647;
一般手机卫士类似软件,一开机就启动服务把广播注册好。
58_拦截电话的原理_13
在arm类型的手机里演示电话拦截;
1、电话拦截原理
当电话来的时候,立刻挂掉并把来电记录清除掉;如果手机速度快的话无法看到这个效果,就达到拦截的效果了。
2、监听当前电话呼叫的状态(TelephonyManager)
监听来电代码
tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
listener = new MyPhoneListener();
tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
具体监听实现代码
switch (state) {
case TelephonyManager.CALL_STATE_RINGING://电话铃声响起来了
String mode = dao.findMode(incomingNumber);
if("1".equals(mode) || "3".equals(mode)){
Log.i(TAG, "黑名单的电话号码,马上挂断");
}
break;
default:
break;
}
取消监听来电代码
tm.listen(listener, PhoneStateListener.LISTEN_NONE);
listener = null;
59_利用反射调用系统隐藏API挂断电话_35
1、挂断电话的API早期版本endCall()是可以使用的,现在不可以用了;但本身挂断电话这个功能是存在的。
2、读getSystemService()源代码;
3、如何查看真面目?到内存中去看,运行起来看,打断点。例如
Context context = getApplication();//看他里面的---mBase---ContextImpl.java
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
搜索:ContextImpl.java,看看源代码
ContextImpl继承了Context----> 搜索getSystemService
--->看看各种服务
在基础课的时候我们也可以写一个服务在后台运行,调用服务的时候可以获取远程服务的代理对象(Ibinder),得到代理对象后就可以调用里面的方法了。
4、重新启动模拟器看日志
Zygote-->sysem_process---->batteryService---->SensorService....
开机过程其实就是各种服务加载的过程。
getSystemService()得到的服务把相关API隐藏了,只提供常用的方法,那么想要得到原生的TelephonyManager的方法就得绕开使用这个方法;
5、挂断电话具体实现步骤
A:创建endCall()方法,里面代码实现如下;
public void endCall() {
//ServiceManager.getService(TELEPHONY_SERVICE);
try {
//得到ServiceManager的字节码
Class clazz = CallSmsSafeService.class.getClassLoader().loadClass("android.os.ServiceManager");
//得到字节码的方法
Method method =clazz.getDeclaredMethod("getService", String.class);
//调用方法得到远程服务代理类
IBinder b = (IBinder) method.invoke(null, TELEPHONY_SERVICE);
//获取到原生未经包装的系统电话的管理服务
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
远程服务代理对象IBinder 需要一个.aidl文件去生成方法去管理服务
ITelephony.aidl
接口用于与手机交互。主要使用的
TelephonyManager类。一些地方仍然是直接使用这个。
请清理他们如果可能的话,使用TelephonyManager insteadl。
把ITelephony.aidl拷贝到com.android.internal.telephony
把NeighboringCellInfo.aidl拷贝到android.telephony
不报错后看一下gen目录下
在endCall()方法加上如下代码
ITelephony telephony = ITelephony.Stub.asInterface(b);
telephony.endCall();//挂断电话
需要加上权限
<uses-permission android:name="android.permission.CALL_PHONE"/>
知识拓展
60_利用内容观察者和内容提供者删除呼叫记录_25
1、增加方法deleteCalllog();
导出呼叫记录数据库data/data/com.android.proveders.contacts/databases/contacts2.db
数据看里面的内容 number 电话号码 、 date时间、 type 1 打进来 2 打出去 3 未接
2、使用内容解析者去删除电话记录
ContentResolver resolver = getContentResolver();
Uri url = Uri.parse("content://call_log/calls");
resolver.delete(url, "number=?", new String[]{incomingNumber});
Uri 路径如何写可以参照源代码
运行演示,会报错;
3、需要加两个权限
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
4、解释删除有时成功,有时不成功的情况;
立刻把电话挂断了,但呼叫的生成并不是同步的代码;它是一个异步的代码。
5、用观察者去监听日志产生后再去删除
注册监听
getContentResolver().registerContentObserver(url, true, new MyContentObserver(new Handler(), incomingNumber));
自定义内容观察者
private class MyContentObserver extends ContentObserver{
private String incomingNumber;
public MyContentObserver(Handler handler,String incomingNumber) {
super(handler);
this.incomingNumber = incomingNumber;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//删除呼叫记录
deleteCalllog(incomingNumber);
//取消注册内容观察者
getContentResolver().unregisterContentObserver(this);
}
}
补充Android2.3模拟器上需要多加权限
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>