手机安全卫士
1 初始化界面的搭建
1.1 界面UI
界面的ui主要完成的是背景图片的显示,以及版本号的显示,其中版本号是需要动态获取显示的。
主要实现:由于布局的特点选择相对布局,在RelativeLayout中设置背景图片,用一个textview显示版本信息。
1.2 界面activity需要重写的方法
界面需要完成的功能是在初始化页面的同时完成与服务器上的版本比对,看是否有新的版本,如果存在应该提醒用户是否进行更新。在完成相应的逻辑后进入到程序的主界面。
根据这些功能,可以知道页面只需要重写oncreate()方法。
1.3 activity里重写的相应的方法
onCreate()方法:
需要完成于服务器上的版本比对,也就是需要获取服务器上的相关数据,由于Android不能在主线程里进行耗时的操作,所以开启子线程完成网络的请求。
第一步:在activity里定义一个CheckVersionTask实现runnable接口重写里面的run()方法来实现子线程的开启。
第二步:在oncreate()方法中new Thread(new CheckVersionTask()).start();开启子线程完成访问服务器更新版本的操作。
1.4 CheckVersionTask()方法的实现
在方法中访问服务器,比较是否有新的版本,如果有新的版本就使用handle机制给主线程发送消息让主线程完成更新ui提示用户更新,如果没有新的版本则让主线程完成跳转到下一界面的操作。
第一步:访问服务器
在res资源文件夹下的values子文件夹下创建一个string.xml文件来保存访问的服务器的路径,这样做的好处是当服务器的地址发生变化时我们不需要在复杂的代码中去修改路径,只需要在这个配置文件里修改即可。
通过getResources().getString(R.string.resource);方法得到配置的路径,通过URL统一资源定位器包装路径,使它成为一个对应相应服务器的一个连接的对象,然后使用openconnction方法建立连接通过强制类型装换为http协议的连接方式(openconection支持有很多的连接协议,通过类型装换使他对应http的连接),得到连接后设置连接的时长,设置连接的方式,
(这里使用get方法,关于post方式及两种方式的区别在后面进行补充),然后得到程序的响应码,进行判断是否与服务器连接成功,如果连接成功则得到服务器返回的流数据,如果失败则返回给用户一个对应的错误码(也是通过handle机制交给主线程在界面上显示)。
第二步:处理服务器返回的数据
在成功连接得到服务器返回的数据后(json数据),由于数据的类型需要先把流转换为一个字符串,通过自定义的工具类的StreamTools.readStream(is);方法完成流的转换,在得到字符串后,服务器返回的是一个json的数据,对其进行解析得到相应的数据。通过解析得到的服务器上的最新的版本信息与本地的版本进行比较,如果不一致提醒更新。
补充一:关于流的转换
这里使用内存流进行转换,相关代码如下:
public class StreamTools {
public static String readStream(InputStream is) throws IOException{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
int len=-1;
byte[] buffer=new byte[1024];
if((len=is.read(buffer))!=-1){
bos.write(buffer, 0, len);
}
is.close();
return bos.toString();
}
}
这里选择使用内存流是操作最方便效率最好的一种方式,关于流的读取是边读边取,定义的字节数组是每次搬运时的容器,字节数组的大小决定了每次搬运的多少,为了使不至于占用过多的内存空间,每次搬运时都不会直接把整个文件搬运过去,所以在拷贝一个文件时特别是一个大的文件时,一般都是读一个字节数组容量过去然后取出来,并不会全部放在内存中,由于整个原因字节数组也不会定义的特别大(会消耗内存空间),也就是说每次读都需要有一个容器去接收,容器可以是一个流对应的本地文件,也可以是一个stringbuffer或stringbuild甚至是一个集合(一般不会用,因为集合拼接的问题的存在)(这些容器是可以自动扩展的相应的区别与原理在后文进行具体分析)。关于优化的缓冲流具体的原理差不多,只是每次搬运的更多。
显然在这里转换的不会是一个大的文件,(请求的是一些版本的信息)而且最好是能一次性读取,所以这里不用前面说的方法读取(后面会介绍相应的各种流的读取),而是采用内存本身作为容器,先把流的数据通过字节数组一点点搬运到内存中,当搬运完时直接一次性取出(bos.tostring),这样效率会高很多,而且由于文件本身的大小不会占用过多的内存。
补充二:json的解析
这里得json数据是很简单的结构,只有一层的json对象,没有夹杂json数组,所以在解析时很简单清晰;
得到一个jsonobject对象绑定对应的要解析的字符串,然后通过getString(“key”)方法取得对应的信息。(关于json解析的详细不在这里说明)
解析的核心代码如下:
String result = StreamTools.readStream(is);
// 解析得到的json字符串
JSONObject json = new JSONObject(result);
String version = json.getString("version");
String description = json.getString("description");
downloadPath = json.getString("apkurl");
补充三:得到版本信息的方法
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
String version=packageInfo.versionName;
return version;
通过包管理器(Google为我们包装的框架)在介绍Android的结构时介绍。
第三步:通过handle机制给主线程发消息
通过message封装消息,通过handle发送。
Message msg = Message.obtain();取得message对象,也可以通过new 来创建,不过这里推荐使用前面的方法
msg.what = SUCCESS_UPDATE;标志是什么消息,方便handle进行处理,后面的是自定义的常量
msg.obj = description;发送的内容,object类型,可以接受任何内容
handler.sendMessage(msg);发送给主线程的handle处理
1.5 handle处理
关于handle机制的详细原理不在这里进行介绍,只介绍本程序的相应实现。
版本一致不更新,返回的各种错误码的,只需要在界面给一个toast提醒然后进入主界面,版本不一致提醒更新则需要先弹出一个对话框提醒用户,然后根据不同的点击,设置不同的逻辑判断,执行不同的方法。
第一步:页面的跳转,跳转到menu页面
(关于页面的跳转方式:分为显示意图和隐式意图)
这里使用显示意图(一般调用系统的一些activity时使用隐式意图,如后面使用的自动安装更新)
由于在很多地方都需要使用到这个页面的跳转,所以抽取成一个方法。创建意图,设置跳转的页面,开启意图的跳转,结束页面。
具体代码如下:
private void loadMainUi() {
Intent intent = new Intent(Activity_splash.this, Activity_menu.class);
startActivity(intent);
finish(); }
第二步:创建一个对话框提醒用户是否更新
通过AlertDialog.Builder builder = new Builder(context);方法来创建一个对话框,通过setXXX方法设置对话框的显示的信息。完成信息设置后设置对话框的两个点击事件:builder.setNegativeButton,builder.setPositiveButton。
这两个方法里有2个参数,
Parameters:
text The text to display in the negative button
listener The DialogInterface.OnClickListener to use.
第一个参数text代表对话框点击按钮的名称,第二个参数是一个点击事件的监听器,注意是Dialoginterface包下的,最好加上包名。
第三步:两个点击事件的处理
在builder.setNegativeButton(取消更新)中只需要调用方法进入menu界面即可,重点在builder.setPositiveButton(更新)的处理。
在确定更新时,需要完成两步,第一是用多线程断点下载下载服务器返回的指定路径里的apk安装包(在下载完成后完成自动更新),第二步,在界面上创建一个下载的进度条显示下载的状态。
1.6 多线程断点下载
本程序使用的是xutils开源框架完成多线程断点下载,关于多线程断点下载的源码实现与开源框架的包装设计的原理WHHHH
在使用开源框架前把相应的jar包导入到项目中,在使用时只需要创建对象,开启下载两步即可。
第一步:创建对象
HttpUtils httpUtils = new HttpUtils();
创建httpUtils对象,拿到这个对象去开启下载,在开启下载中有下载中,下载成功,下载完成三个回调事件,分别在3个事件里做对应的逻辑处理即可完成想要的交互与效果。
第二步:开启下载
在开启下载前(这里是用sd卡存储)先判断sd卡是否可用。
httpUtils.download(downloadPath,file.getAbsolutePath(),new RequestCallBack<File>() {});
3个参数分别是,下载的路径,下载完成后文件存放的路径,请求下载响应的事件,里面需要重写3个方法,分别是下载中,下载失败,下载完成;
在下载中显示下载的进度,在下载成功后通过隐式意图开启系统的自动更换apk的页面完成apk的替换。
1.7 apk的替换
Apk的替换实在多线程断点下载的onsuceess回调方法里完成的,方法的代码:
public void onSuccess(ResponseInfo<File> fileinfo) {
// 下载成功回调的方法
// 下载成功应该替换掉apk,这里使用的是隐式意图的方法来替换
pd.dismiss();//关闭下载进度条对话框
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(Uri.fromFile(fileinfo.result),"application/vnd.android.package-archive");
// 如果activity已经运行到了task,再次跳转不会再运行这个activity
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);}
上面的代码中有两点特殊的地方,第一点是setdataandtype方法里的data信息是由下载成功返回的responseinfo信息里提供的,第二点是设置了不同的activity在task栈里运行模式(和activity里4种开启模式有所不同,WHHHH)。
1.8 页面细节的处理
第一:初始化页面的显示时间(无论网络请求的快慢,都使页面显示一个固定的时长)
第二:无论网络连接是否成功以及下载的操作是否成功都应该在提示信息后进入主界面
2. 主页面menu页面的搭建
在完成初始化界面后,程序进入主界面菜单,在menu界面里显示各个功能模块的入口,显示一个比较好看绚丽的页面动画效果。
2.1 界面UI
界面的ui主要实现里标题框的布局以及各个模块入口的布局。其中标题框里面有一个旋转的图片做成动画效果,有一个标签可以在其中动态的显示文字,功能模块的入口布局使用网格布局,gridview。界面的ui主要是这3个部分完成。
2.2 图片旋转动画的实现(属性动画)
使用ObjectAnimator类完成。分为3步:创建实例,设置动画的显示信息,开启动画。
第一步:创建动画对象实例
ObjectAnimator oa = ObjectAnimator.ofFloat(iv_menu_heima, "rotationY",
45, 90, 135, 180, 225, 270, 315);
3个参数分别为,关联的控件(也就是操作的控件),旋转的中心轴,后面一串为一个可变参数的数组参数,记录的是每次旋转的角度。
第二步:设置动画的信息
主要是:设置动画执行一次的时长:oa.setDuration(3000);
动画重复的次数:oa.setRepeatCount(ObjectAnimator.INFINITE);
动画旋转的模式:oa.setRepeatMode(ObjectAnimator.RESTART);
第三步:开启动画
使用start方法开启。
2.3 动态文本的显示
动态文本的实现使用的是自定义的textview,它继承textview重写了里面的相应的一些方法,以欺骗Android系统的一些处理,从而达到想要的动态效果
第一步:页面的参数
除了宽高距离颜色这些普遍的信息以外,主要在页面中设置了两个特殊的属性,一个是focusableInTouchMode,设置这个属性为true,意思是页面获取焦点的事件是开启的,当页面获得焦点后就可以动态显示文本,然后除了设置文本一行显示以外,还要设置ellipsize属性,这样多余的文字不会以省略号的形式显示。
然而即使设置了这些属性还是不能动态显示,因为textview不像button那样通过点击事件可以获取焦点(当然这里可以设置属性使它是可以点击的),显然根据需求,是一直的动态显示,也就是textview是一直获取焦点的,所以需要重写方法欺骗系统这个textview是一直获取焦点的。
第二步:重写方法,欺骗系统页面一直在获取焦点的状态。
这个通过自定义的textview继承textview重写里面的isfocused方法实现。把该方法的返回值始终返回true,注意在重写时重写父类的所有构造方法.
2.4 gridview的布局
Gridview与listview类似,不同的地方是可以通过设置显示每行显示的模块数量,(通过numcolumns设置列的信息)通过适配器和inflator打气筒完成布局的显示。
第一步:适配器的设置
通过设置适配器显示每个条目,定义一个自己的适配器继承baseAdapter实现里面的4个方法,这里我们需要关注的是其中的两个方法:getview,getcount。
Getcount方法是返回页面总共需要显示的条目,一般与数组集合一起使用。
Getview方法返回每次打气好的view视图,返回视图,补充:无论是当重新new一个adapter适配器还是通过调用notifyDataSetChanged();方法刷新视图,都是重新执行一次getview方法,不同的是通过notifyDataSetChanged();方法刷新记录了上一次视图显示的信息,这样可以记录到用户正在操作的数据的位置。
对于类似的这些布局都是采用了使用适配器然后定义布局文件然后打气的一类操作,不同的是界面的布局不同,大气文件里的处理的逻辑不同。
3. 设置界面setting页面的搭建
在主界面中除了各功能模块的入口还有设置页面的实现,主要设置完成软件是否在启动时自动检测更新等功能,为方便起见应用程序都采用sharedperfrence来存储数据,关于Android中的几种存储操作在后面总结归纳(文件存储,首选项,sqilte数据库,内容提供者,sd卡存储等方式)
在页面布局中为了使界面交互性更好代码复用性更优使用了自定义的imageview以及状态选择器selector。
3.1 自定义的imageview
实现点击切换图片的功能,继承imageview在里面增加改变图片的方法,当每次点击事件发生时调用该方法,通过该方法实现图片的切换。点击事件的触发在整个相对布局上完成,把布局设置为可点击的,然后通过设置状态选择器实现点击击中时整个布局背景颜色改变实现好的交互性。
3.2 状态选择器
新建一个drawable文件夹(名称不可变),在文件夹下新建相应的xml文件,在文件中配置相应的状态信息。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/last_pressed" android:state_pressed="true">
</item>
<item android:drawable="@drawable/last_normal"></item>
</selector>
在布局文件的background属性里配置该文件就可以实现。
4. 第一个功能模块:安全模块
4.1 密码设置
密码设置分为两部分,第一部分是第一次点击时也就是用户没有设置过密码时,这种情况弹出第一个自定义的对话框去提醒用户设置密码,如果用户设置好了密码或设置过了密码(也就是密码有过保存数据)就进入验证密码的对话框去验证,验证通过分两种情况进入页面,如果用户是第一次登录设置密码则进入第一个页面开始设置,如果不是则直接进入后面的界面。
4.1.1 设置密码的自定义对话框
在用户点击进入第一个安全模块的时候触发,通过AlertDialog.Builder创建对话框,然后通过打气筒打好一个自定义的view(类似于适配器里的操作),然后通过setview方法设置对话框的显示为这个view的布局,实现布局里的两个控件的点击监听事件,就完成了对话框的基本逻辑,不过有一点要注意,完成的事件是自定义的事件,如何去实现对话框的关闭涉及到作用域的问题,因为关闭的操作在点击事件中完成,也就是在一个方法的内部类中,而对话框的开启则是在点击事件发生前就应该开启,且对话框对象的关联是通过开启事件的操作完成(当调用show方法的时候会返回一个dialog对象,通过返回的这个对象就得到定义的对话框的实例,这样才可以进行关闭操作)。这样需要定义一个变量使方法中的和方法的内部类中的都可以访问到,最好的方式是在外部类中定义一个成员变量,因为在方法中定义变量最好是在定义的时候就赋值(如果不赋值在内部类中使用则需要定义为final,而final的值是不可改变的,这样就是一个矛盾),显然不符合需求,在内部类中定义外部方法又访问不到,所以在这里使用外部类的成员变量的定义方式(不然会有bug)。
密码确认的自定义对话框逻辑类似。
4.1.2 自定义对话框的优化
4.2 第一个activity页面:stepFirst
主要完成显示一些信息主要是布局代码的完成,里面涉及到一个style的设置,可以去帮助优化布局,使代码复用性高,且维护性好,当有很多页面使用类似的布局设置时应该去提取一个style。
在res/values/styles.xml文件里去添加style节点抽取布局。具体实现代码:
<style name="text_title">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">48dp</item>
<item name="android:background">#99CCFF</item>
<item name="android:gravity">center_vertical</item>
<item name="android:textColor">#ffffff</item>
<item name="android:textSize">18sp</item>
</style>
4.2 第二个activity页面:step02
页面主要完成绑定sim卡的逻辑,当用户点击了绑定sim卡时应该把sim的信息进行保存,这样当手机被盗换卡时能够比较出sim信息的不同然后通知用户。如果用户没有设置绑定则提醒用户必须绑定才能进入下一步。
为了使页面的交互性更好,使用了屏幕的事件处理,判断用户手指在屏幕上的操作,然后通过Android提供的GestureDetector方法去处理这些事件,在该方法中重写一些方法的逻辑就可以完成用户对应事件的捕获与处理。而且在activity的切换效果中使用了自定义的动画效果,使画面的切换更美观。
4.2.1 屏幕事件的处理
因为在多个activity中都需要这样的效果所以在代码重构时把这逻辑抽取到一个activity里面,这个activity继承了activity,增加了一些公有的逻辑和方法,如果页面需要使用这些效果直接继承这个抽取过得activity即可,这样的操作大大提高了页面的维护性以及代码的复用性。关于代码的重构不在这里进行介绍。
关于屏幕事件的处理,采用重写ontouchevevt方法去处理得到的屏幕事件由于并没有采用过ontouch的事件的监听,所以不会因为优先级的关系影响到该方法的执行,并不会被截断。Android提供了很方便的api去方便开发者处理屏幕的事件:GestureDetector。
在实例化该对象时需要创建一个监听器,在该内部类里对应多种类型的事件处理,根据需要重写onfling方法,在里面完成手指怎样滑动会执行什么操作。
对于页面的切换,使用了自定义的动画效果,由于作为公共的父类,在其中的方法中并不提供具体的方法实现体。所以只给出方法的规范,定义出相应的抽象的方法,由子类去重写实现。
4.2.2 自定义的界面切换动画的实现
关于切换的动作是由子类去实现的,在finish或startxxx方法后(也就是跳转代码完成后)写overridePendingTransition(R.anim.anim_stepin, R.anim.anim_stepout);方法去实现自定义动画的效果,方法中的两个参数分别代表前一个页面的退出效果和后一个页面的进入的动画的效果,(通过xml文件去定义这些动画效果,有一定的格式,必须放在anim文件夹下(自定义的文件夹,名称不能改变));
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:fromXDelta="100%p"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="0" >
</translate>
4.2.3 保存sim卡的信息
通过首选项保存sim卡的信息,当用户锁定sim卡时就保存该信息,通过的到系统的telephony service来得到teleponymanager,通过teleponymanager中的getsimserialnumber方法得到数据通过首选项保存。(下次开启重启是会比较sim信息看是否有换卡的操作,如果有就给安全号码发短信)
4.3 第三个activity页面:step03
第3个页面完成安全号码的存储,由于是安全操作的关键部分,如果用户没有填写内容则不能进入下一个页面,(这里只需做一个判断即可)。用还可以选择通讯录里的号码来作为安全号码。安全号码的保存也是通过首选项来保存。
4.3.1 得到开启的页面返回的数据
通过点击页面上的安全号码按钮可以进入到联系人页面,通过startActivityForResult方式开启意图,到达contactactivity的页面(该页面完成联系人信息的展示),在该页面完成选择后,通过重写onactivityresult方法可以得到开启的那个页面里的选择的信息的内容。
(通过意图intent,以及bundle在底层完成数据的封装和传输)。再将得到的信息显示在编辑框里即可。
4.3.2 contactactivity里对于联系人信息的显示
通过内容提供者来得到联系人数据库里的数据,(Android4大组件之一,如果需要访问不同应用程序间的数据需要用到)
两句关键的代码:
ContentResolver resolver = context.getContentResolver();
// 查询raw_contact表
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
第一句代码得到解析工具的对象,第二句得到需要解析的资源的信息。(也就是要去解析哪一个资源)
然后利用面向对象的思想就可以直接拿着这个工具去采取查询操作,在对数据库进行持查询等操作前,先要清楚操作的表的结构。
利用sqlite的工具打开导出的数据库中联系人表,分析其中的结构(分析过程在小结中提供)可以知道要完成联系人数据的读取涉及到多张表,先得到id信息在去另外的表中遍历查询。
拿到的信息存储在cursor游标中,对游标进行遍历读取,得到每一个id,查询每个id对应的信息(这些信息依然保存在一个cursor中),遍历第二层的cursor得到具体的信息保存在集合中,再将保存有信息的集合数据通过适配器显示在界面上(listview)。
通过listview的条目点击事件返回选择的数据,这样就拿到了用户点击选择的联系人的信息。
4.4 第四个activity页面:step04
主要是页面的布局和利用首选项对配置信息的存储,方法类似。
4.5 第五个activity页面:最后配置信息完全显示
显示首选项中保存的相关的配置信息,提供一个可以点击文本可以返回到第一个step01界面进行重新的信息配置。然后显示相应的指令信息,当手机被盗后,利用安全号码给对应号码发送指定的指令让手机完成指定的操作(后面介绍)。
4.6 如何完成对被盗手机的远程操作
可以发送指定的短信完成:播放音乐,通过短信返回手机所在位置,锁屏,远程清理数据。
实现的原理:通过检测sim卡改变的信息来完成是否换卡,如果sim有改变,则给安全号码发送一条手机可能被盗的短信,这样安全手机也就获得了换卡后的号码,由于android手机的换卡手机需要重新启动,利用广播,当重新开机后就完成sim卡的对比。
写一个broadcast,在清单文件里配置意图过滤器监听开启启动的动作,当监听到事件的发生时,执行onreceiver里的方法(完成sim的对比),如果不相符合,给安全号码发送短信。
关键代码:
获取sim信息的api:
TelephonyManager tm=getSystemService(TELEPHONY_SERVICE);
tm.getSimSerialNumber;
发送短信的api:
SmsManager.getDefault().sendTextMessage();
4.6.1 解析短信
安全号码接受到了短信也就得到了相应的号码,给手机回复相应的短信,通过解析判断短信的内容选择对应的逻辑执行。
写一个brodcast,在清单文件里配置意图过滤器,把该意图过滤器的优先级设置为最高,当有短信发进来时,先在onreceive里进行判断,如果是指定的特定短信或者是垃圾信息就进行拦截,不让用户收到。(短信是有序广播,先由优先级高的处理,处理完了再交给低优先级的)
Object[] objects = (Object[]) intent.getExtras().get("pdus");
SmsMessage message = SmsMessage.createFromPdu((byte[]) obj);
String body = message.getMessageBody();
4.6.2 播放音乐
在res/目录下新建一个raw文件夹,存放需要播放的音乐,利用mediaplayer的api播放音乐。
MediaPlayer player=MediaPlayer.create(context, R.raw.zjwz);
player.start();
4.6.3 返回手机的位置
由于中国所用坐标系是不正确的(火星坐标系,国家对坐标进行了加偏),返回的坐标需要进行进一步的处理才能得到真实的坐标,先得到手机返回的坐标。
由于位置是变化着的,就需要得到能够更新的数据,所以通过服务在后台进行位置信息的获取,当用户的位置发生了较大移动时就返回新的位置信息。
当短信中解析到该指令信息时开启一个服务,通过服务监测位置信息的改变,在把得到的信息通过一定的方法转换为真实的位置坐标,把坐标点通过短信发送给真实的手机。
服务的写法:
先得到locationManager,通过个体systemservice(locationservice)得到。
关键的代码:
lm.requestLocationUpdates("gps", 0, 0, locationListener);
locationListener是一个接口,写一个实现类去实现这个接口里某实现的方法,这个类的作用是监听位置信息。
重写里面的onLocationChanged方法,当位置信息改变时,执行该方法里的逻辑,通过短信发送位置信息。(关于火星坐标的转换在小结里叙述)
4.6.4 清楚数据和锁屏
通过系统的服务去获得超级管理员的权限(获得后还需要用户在手机设置里去开启该权限)
DevicePolicyManager dpm= (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
配置超级管理员信息,要写一个特定的广播,系统通过该广播判断该程序是否配置了超级管理员的权限已经有哪些操作的权限。也就是当你要执行上面的语句时,会有相应的广播响应,如果没有配置该广播则会出错。
该广播继承DeviceAdminReceiver,在清单文件里完成一系列的文件信息的配置。(固定的写法)
<receiver
android:name="com.itheima.mobilesoft.receiver.MyAdmin"
android:description="@string/sample_device_admin_description"
android:label="@string/sample_device_admin"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin_sample" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
<string name="sample_device_admin">黑马手机安全卫士</string>
<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<watch-login />
<reset-password />
<force-lock />
<wipe-data />
<expire-password />
<encrypted-storage />
<disable-camera />
</uses-policies>
</device-admin>
在执行超级管理权限的相应代码时,会解析这些xml文件,看该程序的超级管理权限拥有哪些,只能执行权限里又得方法。该api里有修改密码,清除数据,锁屏的方法。
到此为止第一个功能模块手机防盗的功能结构大致介绍完了,下面介绍第二个功能模块。
5. 第二个功能模块:骚扰拦截
(由于时间关系,在后期慢慢补上完善)