您的位置:首页 > 其它

第四天:拦截电话,还原短信,程序管理

2012-12-19 18:42 246 查看
1. 黑名单表
2. 白名单表

if(黑名单拦截){

}eles if(白名单拦截){

}else if(拒接模式){

拦截所有的电话和短信

}

挂断电话:
一、copy两个aidl文件。
二、因为默认API是没有默认给我们暴露出来方法,
所以我们必须通过反射来做。所以通过反射调用挂断电话的方法。AIDL必须会。一共有七步。
try {
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
IBinder iBinder = (IBinder) method.invoke(null, new Object[]{TELEPHONY_SERVICE});
ITelephony iTelephony = ITelephony.Stub.asInterface(iBinder);
iTelephony.endCall();
} catch (Exception e) {
e.printStackTrace();
}
三、要加电话的权限

你发现你把电话拦截以后,还会在通话记录里面有记录,那么我们通过内容观察者
把它给删除。
因为这个记录产生在打电话1秒或者2S之后立生的,所以我们不知道什么时间调用,
所以用一个内容观察者,去观察它的数据库,如果发现数据库有变化,就去执行
相应的回调函数。

////注册一个内容观察者 观察call_log的uri的信息  观察这个CallLog.Calls.CONTENT_URI 并对其子URI也观察true
getContentResolver().registerContentObserver(CallLog.Calls.CONTENT_URI, true, new MyObserver(new Handler(), incomingNumber));

private class MyObserver extends ContentObserver{
private String incomingNumber;
public MyObserver(Handler handler,String incomingNumber) {
super(handler);
this.incomingNumber = incomingNumber;

}
/**
* 发现内容改变的时间调用
*/
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
deleteCallLog(incomingNumber);
//当删除之后 ,我们再把它反注册掉,不能一直观察呀,多费内存呀。
getContentResolver().unregisterContentObserver(this);
}

}

拦截短信的话,应该在短信获取短信的广播接收者里面判断。

一个极为重要的问题你想在service里面开启一个activity,因为service是在任务栈外面的
你在外面想要开启里面的一个东西,这不可能吧。所以在加一句话
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

两个电话:
往外打电话是一个广播setResult(null)
外面往里面打电话用上面的方法。

我们拦截一些电话和短信容易,可是要根据内容来拦截,其实
是有一个大的数据库去匹配而已。

响一声电话主要练习了notifaction的应用。必须要学会notifaction的应用

重构对话框。

短信的备份和还原。
短信在哪个数据库里?在com.android.providers.telephony下面它和电话是在一起的
在databases下面有个。
首先你要研究短信的源码,
第一,去下载
第二,在system/app下全部是它的apk文件,在这里面可以反编译它。

反编译步骤:
第一、apktool d tt.apk 反编译出来资源文件(F:\安卓笔记\八、安卓核心基础\ziliao\apktool-install-windows-r04-brut1)
第二、反编译DEX文件,先解压apk,然后得到.dex文件,然后再说
dex2jar tt.dex    得到一个相应的jar包。(F:\安卓笔记\八、安卓核心基础\ziliao\dex2jar-0.0.7.11-SNAPSHOT)
第三、把那个得到的jar文件从jd-gui.exe打开。

复习ContentProvider看 db 和 other这两个项目。
根据配置文件会写URI。里面的具体内容怎么写,看源码。
它肯定是以这个开头content://
今天你记住,调用任何内容提供者它的Uri一定都是以content://开头的。
我们从得到的编译文件可以知道:
<provider android:name="SmsProvider"
android:readPermission="android.permission.READ_SMS"
android:writePermission="android.permission.WRITE_SMS"
android:multiprocess="true" android:authorities="sms" />
主机名是sms,所以content://sms/
但是具体路径你还不知道看源码得知,
sURLMatcher = new UriMatcher(-1);
sURLMatcher.addURI("sms", null, 0);
sURLMatcher.addURI("sms", "#", 1);
sURLMatcher.addURI("sms", "inbox", 2);
.....
它是这样的。

你想要备份一个东西,时间肯定会比较长,肯定放在一子线程里面,
而activity和广播 的周期又太短,它们两个一旦结束,里面的所有
子线程也会结束,所以要把这个备份的操作放在一个服务里面。

//存储到SD卡上,这个方法应该抽离出来,首先判断SD存在不?然后判断挂载没有,然后再用环境变量得到SD卡目录 。

在子线程中不用handler发送消息也能调用界面UI。
Looper.prepare();
Toast.makeText(getApplicationContext(), "备份成功", 0).show();
Looper.loop();
抽空研究一下轮询器。

自定义内容提供和去调用是我的弱项。

以前用pull解析 XML,现在用XmlSerializer解析。

子线程里面不能执行Toast,因为没有消息队列和Handler。
不过还是可以的。

备份是点击备份后,开启后台一个服务,用户可以去干其它,然后结束时间显示个Toast。

ContentValues是要插入到内容提供者里面的信息的。

在子线程里面显示Toast,有两种做法:
第一、通过Message发送给Handler,让主线线处理
第二、looper .prepare()    loop()

还原进度。一个总值不好传过去,把Progress传过来。

所有的还原短信之前都把它先删除了。再还原。

/**
* 备份 ,还原的业务方法。
* @author chen
*/
public class SmsInfoService {
private Context context;

public SmsInfoService(Context context) {
this.context = context;
}

/**
* 通过内容提供者,获得所有的短信内容
* @return
*/
public List<SmsInfo> getSmsInfos(){
List<SmsInfo> list = new ArrayList<SmsInfo>();
SmsInfo smsInfo = null;
//得到内容解析者
ContentResolver resolver = context.getContentResolver();
Uri uri = Uri.parse("content://sms/");
//projection A list of which columns to return. Passing null will   return all columns, which is inefficient.
Cursor cursor = resolver.query(uri, new String[]{"_id","address","date","type","body"}, null, null, null);
while (cursor.moveToNext()) {
smsInfo = new SmsInfo();
String id = cursor.getString(cursor.getColumnIndex("_id"));
String address = cursor.getString(cursor.getColumnIndex("address"));
String date = cursor.getString(cursor.getColumnIndex("date"));
String type = cursor.getString(cursor.getColumnIndex("type"));
String body = cursor.getString(cursor.getColumnIndex("body"));
smsInfo.setId(id);
smsInfo.setAddress(address);
smsInfo.setBody(body);
smsInfo.setDate(date);
smsInfo.setType(Integer.parseInt(type));
list.add(smsInfo);

smsInfo = null;
}
return list;
}

/**
* 还原短信,为了让它显示进度条,传个PD,把最大值给它设置进行,
* 每次完成一个就在相应的事件里面加1即可。你也可以设置个返回值,为另外
* 一个方法提供结果即可,但是这样绝对是不对的,因为它是要求在方法执行之前把总共有多少
* 条短信传输过去 ,所以我们可以把Pd直接作为方法的参数,在这里面给它设置即可。
* 最重要的是,还原之前先把所有的短信都清空,然后再住里面插入数据(根据提供的URI)。
* 把pd传过来是一个很重要的思路。
* @param path
* @param pd
*/
public void restoreSms(String path,ProgressDialog pd) throws Exception{
File file = new File(path);
ContentValues values = null;
FileInputStream fis = new FileInputStream(file);
XmlPullParser parser = Xml.newPullParser();
parser.setInput(fis, "utf-8");
int type = parser.getEventType();
int currentcount =  0;

while (type != XmlPullParser.END_DOCUMENT) {
switch (type) {
case XmlPullParser.START_TAG:
if ("count".equals(parser.getName())) {
String count = parser.nextText();
pd.setMax(Integer.parseInt(count));
//					continue;//结束这次循环,开始下一次循环。不能加它,因为while是个大循环,直接到下一个case也错过了。
}
if ("sms".equals(parser.getName())) {
values = new ContentValues();
}else if ("address".equals(parser.getName())) {
values.put("address", parser.nextText());
}else if ("date".equals(parser.getName())) {
values.put("date", parser.nextText());
}else if ("type".equals(parser.getName())) {
values.put("type", parser.nextText());
}else if ("body".equals(parser.getName())) {
values.put("body", parser.nextText());
}

break;

case XmlPullParser.END_TAG:
if ("sms".equals(parser.getName())) {
ContentResolver resolver = context.getContentResolver();
System.out.println("有木有");
resolver.insert(Uri.parse("content://sms/"), values);
values = null;
currentcount ++;
pd.setProgress(currentcount);
}
break;
}

type = parser.next();
}

}

}

/**
*  开启一个后台服务去备份,
*/
public class BackupSmsService extends Service{

private SmsInfoService smsInfoService;

@Override
public IBinder onBind(Intent intent) {
return null;
}

/**
* 开启备份短信的一个服务
* 因为一旦开启,onCreate方法就执行一次,
*/
@Override
public void onCreate() {
super.onCreate();
smsInfoService = new SmsInfoService(this);

//开始往XML里面写数据
new Thread(){
public void run() {
try {
List<SmsInfo> list =  smsInfoService.getSmsInfos();
//存储到SD卡上,这个方法应该抽离出来,首先判断SD存在不?然后判断挂载没有,然后再用环境变量得到SD卡目录 。
File file = new File(Environment.getExternalStorageDirectory()+"/smsback.xml");
//This interface will be part of XmlPull 1.2 API.
FileOutputStream fos = new FileOutputStream(file);
XmlSerializer xmlSerializer = Xml.newSerializer();
xmlSerializer.setOutput(fos, "utf-8");
xmlSerializer.startDocument("utf-8", true);
xmlSerializer.startTag(null, "smss");
xmlSerializer.startTag(null, "count");
xmlSerializer.text(list.size()+"");
xmlSerializer.endTag(null, "count");

for (SmsInfo info : list) {
xmlSerializer.startTag(null, "sms");

xmlSerializer.startTag(null, "id");
xmlSerializer.text(info.getId()+"");
xmlSerializer.endTag(null, "id");

xmlSerializer.startTag(null, "address");
xmlSerializer.text(info.getAddress());
xmlSerializer.endTag(null, "address");

xmlSerializer.startTag(null, "date");
xmlSerializer.text(info.getDate());
xmlSerializer.endTag(null, "date");

xmlSerializer.startTag(null, "type");
xmlSerializer.text(info.getType()+"");
xmlSerializer.endTag(null, "type");

xmlSerializer.startTag(null, "body");
xmlSerializer.text(info.getBody());
xmlSerializer.endTag(null, "body");

xmlSerializer.endTag(null, "sms");
}
xmlSerializer.endTag(null, "smss");
xmlSerializer.endDocument();
//把文件缓冲区的数据写出去
fos.flush();
fos.close();
Looper.prepare();
Toast.makeText(getApplicationContext(), "备份成功", 0).show();
Looper.loop();
} catch (Exception e) {
e.printStackTrace();
Looper.prepare();
Toast.makeText(getApplicationContext(), "备份失败", 0).show();
Looper.loop();
}

};
}.start();

}

}

学会了在github下载源码,这个程序管理主要看了设置里面的代码
如何获得所有应用程序信息。

开发一个业务方法,获取所有应用程序。

用帧布局在中间放置一个进度条。

ProgressBar它是一个饼。有不同的样式 。
ProgressDialog它一定是个对话框,
在ProgressDialog里面一定有一个ProgressBar.

企业开发中会有所有消息对应的常量。

写一个适配器,要把所有的列表给过来 ,所以
要有下private List<SmsInfo> list;
并且写成构造方法 。

这次有一个像WEB中的模态要做,用了帧布局和消息机制。

因为单独的Adapter没有上下文,所以也要传过来。
这就是我们在封装的时间,缺什么变量加上成员变量
上,然后通过构造方法传过来,多么好的思路呀。

极其重要的问题?
优化问题:
1、怎么去优化listview?怎么优化gridview?
2、怎么去优化java虚拟机。
所有的优化时间问题都可以转化为下面两种思路
1、时间换时间
360安全卫士,QQ电脑管家。
(禁用一些开机启动项,或者延时加载一些开机启动)
优化安卓系统的开机启动时间。
有的只是把系统的一些功能启动了,开机以后电话,短信 ,
相机都不能,而是在开机完之后再自己加载。给用户造成一
种错觉,优化有效果了。

听音乐,sd卡 .mp3 .jpg这些听歌软件为什么能够直接列
出来所有,因为后台早就自己做好放到数据库里面了。

2、空间换时间
我们看图片正常应该是从SD卡到内存中才能看到。
图片资源  sd-->内存-->显示出来。
不过,现在我们定义一个缓存,直接显示更快了。
缓存-->显示。

因为每次显示一个条目,都会调用一次getView方法。
所以把View里面的控件变成静态的。

View.inflate()非常消耗内存。
View convertView 转化View对象,历史view对象的缓存。

也就是说我们要使用历史缓存对象,因为getView每次
每个条目都会调用,所以如果发现这个条目的缓存对象
存在就不再inflate它,而是直接用历史缓存对象。

static TextView tv;如果不用静态的,它每次都要在栈
内存空间创建一个对象。

inflate()会通过反射消耗大量内存所以不能总是创建,
每个条目只能创建(吹起来)一次,再次用的话,直接就用缓存的。

要明白,每个条目显示都要调用getView()方法,而getView
里面有大量消耗内存的操作,通过静态一些对象和利用缓存
去减少它的使用内存。

ListView重要总结:
android ListView几个比较特别的属性
由于这两天在做listView的东西,所以整理出来一些我个人认为比较特别的属性,通过设置这样的属性可以做出更加美观的列表
首先是stackFromBottom属性,这只该属性之后你做好的列表就会显示你列表的最下面,值为true和false
android:stackFromBottom="true"
第二是 transciptMode属性,需要用ListView或者其它显示大量Items的控件实时跟踪或者查看信息,
并且希望最新的条目可以自动滚动到可视范围内。通过设置的控件transcriptMode属性可以将Android平台的控件(支持ScrollBar)自动滑动到最底部。
android:transcriptMode="alwaysScroll"
第三cacheColorHint属性,很多人希望能够改变一下它的背景,
使他能够符合整体的UI设计,
改变背景背很简单只需要准备一张图片然后指定属性
android:background="@drawable/bg",不过不要高兴地太早,当你这么做以后,发现背景是变了,
但是当你拖动,或者点击list空白位置的时候发现ListItem都变成黑色的了,破坏了整体效果。
第四divider属性,该属性作用是每一项之间需要设置一个图片做为间隔,或是去掉item之间的分割线
android:divider="@drawable/list_driver"  其中  @drawable/list_driver 是一个图片资源,
如果不想显示分割线则只要设置为android:divider="@drawable/@null" 就可以了
第五fadingEdge属性,上边和下边有黑色的阴影
android:fadingEdge="none" 设置后没有阴影了~
第五scrollbars属性,作用是隐藏listView的滚动条,
android:scrollbars="none"与setVerticalScrollBarEnabled(true);的效果是一样的,不活动的时候隐藏,活动的时候也隐藏。
第六fadeScrollbars属性,android:fadeScrollbars="true"  配置ListView布局的时候,设置这个属性为true就可以实现滚动条的自动隐藏和显示。

原来做安卓下的模态需要帧布局,因为它跟WEB下的DIV是一样的,都是从左上角到右下角一层一层来的。所以,哈哈。这是一个非常重要的布局。

//总结 思路:在写业务类的一个重要思路,你写着写着,发现需要一个类,把它放在成员变量里面,然后在构造函数里面初始化,
//这样别人在调用你这个业务类的时间就可以把它给传过来
private Context context;
private PackageManager packageManager;
public AppInfoProvider(Context context){
this.context = context;
packageManager = context.getPackageManager();
}

//一些非常重要的布局和属性,必须要掌握。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/backgroundcolor"
android:orientation="vertical" >
<!-- 为什么要加个LinearLayout这是很值得思考的,因为你外包一层,位置控制就非常的容易 。 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dip"
android:background="#dd555F5F"
android:gravity="center_vertical|center_horizontal"
android:orientation="vertical"
>
<TextView
android:id="@+id/tv_app_manager_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="所有程序"
android:textColor="@android:color/white"
android:textSize="25sp"
/>
</LinearLayout>

<!--
帧布局 类似div层,所有的帧布局都是从左上角开始的.每个控件相当于一个
DIV,并且都是从左上角开始的.
ListView有一些个重要属性,必须要掌握。
android:listSelector:它对就的是 一个状态列表,即点击,获取焦点等不同状态下有不同的效果。
android:cacheColorHint: 这个是当你设置了背景图片以后,再滚动的时间缓存会出现,你把它的
背景色改成透明的就行了。
-->
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ListView
android:id="@+id/lv_app_manager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="1dip"
android:cacheColorHint="@android:color/transparent"
android:listSelector="@drawable/item_background_selector"
>
</ListView>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical|center_horizontal"
android:orientation="vertical"
android:visibility="visible"
android:id="@+id/ll_app_manager_loading"
>
<ProgressBar
android:layout_width="60dip"
android:layout_height="60dip"
/>
<TextView
android:text="正在加载应用程序"
android:textSize="18sp"
android:textColor="@android:color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>

</FrameLayout>

</LinearLayout>

//还有如何实现安卓下的模态。
结合上面布局的代码,我们来实现安卓的模态问题。

//一加载就开始显示那个帧布局的东西,处于可见状态。
ll_app_manager_loading.setVisibility(View.VISIBLE);
//因为搜索所有程序列表很耗时,所以另外起一个线程
new Thread(){
@Override
public void run() {
provider = new AppInfoProvider(AppManagerActivity.this);
appInfos = provider.getAppInfos();
//得到所有信息之后 ,通知主线程,开始更新界面吧。
Message message = new Message();
message.what = GET_ALL_APP_FINISH;
handler.sendMessage(message);
};
}.start();

然后在消息机制里面这样做:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case GET_ALL_APP_FINISH:

//结束之后 要处于不可见状态,这是多么好的模态呀。
ll_app_manager_loading.setVisibility(View.INVISIBLE);
adapter = new AppManagerAdapter(AppManagerActivity.this, appInfos);
lv_app_manager.setAdapter(adapter);
break;

}
};
};

ImageView一般用这个android:scaleType="fitXY"因为它正好能够填充满这个。
//很重要的一个方法,从listView里面直接得到这个对象 。这是SDK提供的方法。
AppInfo appInfo = (AppInfo) lv_app_manager.getItemAtPosition(position);

//popwindow它是比acitvity开销小的小窗体。
得到listview每个条目的左上角位置。
界面中只让存在一个popupwindow的操作。
在使用popupwindow的时间一定要设置一个背景颜色,否则会产生很多问题。

为某个东西添加控件的思路,先把动画通过XML或者代码定义出来,然后附加
在某个控件上,它们两个独立的。
LinearLayout ll = (LinearLayout) popupView.findViewById(R.id.ll_popup);
ScaleAnimation animation = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f);
animation.setDuration(200);
ll.startAnimation(animation);

操,太牛逼了,可以为某个控件添加一个值,就是附属在它上面的,它可以为
任何一个前台的组件赋值jquery里面的data,这个值可以附带一个值 ,当时感觉
jquery的强大之处,没有想到安卓也是这样设计的,有一个setTag()可以为某个
控件带一个值,在用的时间再取出来,原来所有前台的设计思想是一样的。
这个在安卓中,一个界面有很多view,不好通过ID区别,就可以通过它。
就是setTag方法太强大 了。用的地方特别多。
// 把当前条目在listview中的位置设置给view对象
ll_start.setTag(position);//把一个数据记录在这个控件上,我用jquery中的data谈过这个方法。
ll_share.setTag(position);
ll_uninstall.setTag(position);

调用 :
//上面设置Tag就是为了给其它地方传播数据。这是前台的各个控件数据传输的一种重要方法。
int position = (Integer) v.getTag();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: