【Android项目实战-手机安全卫士】
目标:快速积累开发经验,具备中级Android工程师能力。
如遇到难以理解的逻辑或功能,可以先将程序打断点观察程序的执行逻辑。
第一章项目简介:欢迎界面、主界面
第二章手机防盗:手机sim卡绑定、GPS定位、远程锁屏、远程删除数据
第三章通讯卫士:黑名单添加、电话拦截、短信拦截
第四章软件管家:软件的快速启动、卸载、分享、设置
第五章手机杀毒:第三方数据库的使用、病毒扫描、病毒查杀
第六章缓存清理:获取手机中的缓存信息并对缓存清理
第七章进程管理:获取手机中正在运行的进程、结束进程、锁屏时是否清理进程
第八章流量统计:运营商信息设置、获取流量套餐、显示本月和本日流量
第九章高级工具:号码归属地查询、短信备份、短信还原、程序锁
第十章设置中心:手机卫士设置(是否开启黑名单拦截、是否开启程序锁)
动手完成每一个功能模块!
本项目按照功能分类放在不同的包中。以章节为编号作为包名将每个大的功能模块放在一个包中。例如操作界面的Activity都直接放在章节包中(chapter01)。
主界面HomeActivity因为是公用界面,这里单独放在mobilesafe包中。
按章节具体学习如下:
【第一章】
HomeAdapter主界面的布局填充器(填充界面中的9个功能图标及文字信息)
VersionEntity封装版本信息的实体类(从服务器获取到的信息存储到实体类中)
DownLoadUtils从服务器下载apk的工具类
MyUtils获取应用程序的版本信息、实现新版本apk的安装
VersionUpdateUtils进行版本更新的工具类
SplashActivity展示欢迎界面及版本信息
HomeActivity展示程序主界面及实现各个功能图标的点击事件
由于需要获取服务端应用的版本号以及下载最新apk,所以需要搭建一个服务器。
1.这里采用tomcat作为服务器,点击tomcat目录下bin/startup.bat开启服务器。
2.创建一个HTML页面(updateinfo.html),该页面返回的信息需要包括服务器中apk的版本号、版本说明、新版本的下载地址。
updateinfo.html页面的json信息如下(直接新建一个只含有下面内容的网页即可):
{
"code":"2.0",
"des":"手机卫士2.0版本,新增了手机杀毒功能",
"apkurl":"http://172.16.25.13:8080/mobilesafe.apk"
}
3.将updateinfo.html页面及卫士2.0版本的apk(经过签名打包用于发布的apk,而不是调试的apk)复制到tomcat的webapps/ROOT文件夹下。
本项目采用第三方开源框架xUtils下载apk,因此需要将xUtils的jar包导入。它包含很多工具类,本项目用到:
1.HttpUtils类用于发送Http请求、上传文件、下载文件等。
2.RequestCallBack
接口有三个抽象方法可以获取到文件的下载状态。
Java中是没有指针的,因此在实现回调时都是通过接口或抽象类。
主界面包含一个GridView(九宫格控件),它的每个Item类似,都由一张图片和一个标题组成,因此创建一个Item布局为GridView设置每个条目数据。
要将这个Item中的功能图标和文字填充到主界面中,还需要创建一个布局填充器HomeAdapter。
【第二章】
ContactAdapter用于填充联系人界面的数据适配器
InterPasswordDialog自定义的dialog对话框,用于输入防盗密码
SetUpPasswordDialog自定义对话框,用于设置防盗密码
ContactInfo用于存储联系人信息的实体类
BootCompleteReciever用于监听手机开机启动的广播接收者,检测SIM卡是否更换
MyDeviceAdminReciever用于获取超级管理员权限的广播接收者
SmsLostFindReciver监听短信到来的广播接收者,接收到指令后进行相应的报警操作
GPSLocationService用于定位手机的服务
ContactInfoParser用于从数据库中解析联系人信息
MD5Utils加密算法的工具类
BaseSetUpActivity设置向导的公共父类,封装了一些手势识别的方法
ContactSelectActivity获取手机中的联系人并展示到界面中
LostFindActivity用于显示手机防盗界面以及防盗指令
SetUp1Activity显示设置向导第一个界面
SetUp2Activity显示设置向导第二个界面并绑定SIM卡
SetUp3Activity显示设置向导第三个界面并选择安全联系人
SetUp4Activity显示设置向导第四个界面并开启防盗保护功能
在设置密码界面布局setup_password_dialog.xml中drawable/coner_bg_white这个文件中使用shape属性自定义对话框的样式,
其中corners指定对话框的圆角solid指定圆角的颜色。
在values/styles是自定义对话框样式文件。
在保存用户密码时,采用了MD5加密算法,这种算法是不可逆的。每次验证时,把新输入密码在加密后和最初设置密码加密后的结果比对。
小圆点界面(滑动到当前界面的动态指示小圆点),界面中这些小圆点实际上就是RadioGroup中放了一组RadioButton,通过background属性来指定按钮颜色。
在setup_radiogroup.xml中使用了selector(背景选择器)drawable/circle_purple_bg_selector,其中circle_purple又指定按钮的颜色和形状。
在设置向导界面一中activity_setup1.xml通过include标签引入小圆点布局。
titlebar.xml用于设置标题栏。
滑屏动画:res/anim目录下。(p代表屏幕,整个屏幕左上角代表坐标轴原点0,0)
下一个(从右往左)界面进入动画:next_in.xml,android:fromXDelta="100%p"与android:toXDelta="0"表示当前界面从X轴坐标100%p移动到0。
下一个(从右往左)界面切出:next_out.xml。
上一个(从左往右)界面进入:pre_in.xml。
上一个(从左往右)界面切出:pre_out.xml。
手势滑动:通过手势左右滑动实现切换设置向导的4个界面。每个界面都需要滑动,可以定义一个父类实现手势识别功能,其他类继承该类。
在检测SIM卡是否发生变化时,可以使用Application类。该类是Android框架的一个系统组件,当Android程序启动时系统会创建Application对象。
它用来存储系统的一些信息,直到应用的所有Activity全部被销毁之后才会被释放。可以进行数据传递、数据共享、数据缓存操作。
当Application启动时,系统会创建一个进程ID,所有的Activity都会在此进程上运行。
当Application创建时会初始化全局变量,所以同一个应用的所有Activity都可以取得这些全局变量的值。
通常它会由系统自动创建,如果需要使用,就自己创建一个类继承它,并在配置文件中application标签进行注册(只需增加name属性即可)。
因此,通过这种特性,可以将检测方法放在Application的onCreate()方法中,当程序启动时就会检测SIM卡是否变更。本项目是App类继承它的。
有时需要重启手机识别新的SIM卡,因此还需要创建一个开机启动的广播接收者BootCompleteReciever来
实现监听手机开机事件,并调用APP中的correctSIM()方法判断SIM卡是否变更。
在第三个向导界面中,联系人列表的界面(activity_contact_select.xml)需要在ListView控件中展示,
同时该控件需要填充列表的Item布局item_list_contact_select.xml。联系人的实体类ContactInfo用于封装联系人信息。
ContactInfoParser:联系人信息都存储在SQLite数据库中,需要先获取到联系人的id,
根据id在data表中查询联系人名字和电话,并封装到ContactInfo中,然后存入List集合。
从数据库中查询出的联系人信息,通过数据适配器(ContactAdapter)填充到ListView中,本ListView有两处提升性能的优化。
在ContactSelectActivity类中将数据填充到界面。
在4个向导界面之后是防盗指令界面LostFindActivity。
为了监听设置的安全号码发来的短信,识别其中的防盗指令,需要创建一个广播接收者SmsLostFindReciver根据收到的短信指令来执行不同操作。
在执行远程锁屏和删除数据时,需要获得超级管理员权限,可以手动开启,可以通过代码开启。大多情况是需要代码开启的。有三步:
1.定义超级管理员的广播接收者MyDeviceAdminReciever。
2.在配置清单文件中,注册上述广播,并且添加权限BIND_DEVICE_ADMIN用于指定绑定超级权限。
其中,meta-data标签表示超级管理员的元数据,里面的resource属性用于指定安全策略。
3.安全策略在文件xml/device_admin_sample.xml中。
防盗指令中的定位服务GPSLocationService,用于获取手机所在经纬度、移动速度、精确度。
【第三章】
BlackContactAdapter用于填充黑名单的数据适配器
ContactAdapter用于填充手机联系人信息的数据适配器
BlackNumberDao用于对黑名单中的数据进行增删查
BlackNumberOpenHelper创建黑名单数据库,继承自SQLiteOpenHelper
BlackContactInfo存储黑名单信息的实体类
ContactInfo联系人信息的实体类
InterceptCallReciever拦截电话的广播接收者
InterceptSmsReciever拦截短信的
TestBlackNumberDao操作黑名单数据的测试类
ContactInfoParser联系人信息的解析器(从系统通信录中读取联系人信息)
AddBlackNumberActivity添加黑名单界面
ContactSelectActivity显示系统联系人界面
SecurityPhoneActivity显示黑名单信息界面
黑名单数据库使用SQLite存储黑名单信息,BlackNumberOpenHelper创建了数据库blackNumber.db和表格blacknumber。
因为需要保存黑名单中联系人信息,所以定义一个黑名单联系人的实体类BlackContactInfo。
操作黑名单数据库的工具类BlackNumberDao,对黑名单中数据进行增删查。
模块方法测试:对每一个新模块或者方法可以进行测试来避免bug。
此处对数据库工具类中操作黑名单数据的方法测试(黑名单数据需要填充到主界面中,为了避免后期出现错误导致调试困难)。
Android系统自带了测试框架JUnit,使用该框架对数据库工具类中的方法进行测试,
首先要在清单文件中配置相应信息及在标签下添加,
然后写测试文件(TestBlackNumberDao继承自AndroidTestCase,并对数据库中增删查进行测试。需要注意:
在JUnit测试框架中,测试方法的异常必须抛出,不能使用try-catch,否则测试框架捕获不到异常),
运行时点击Android JUnit Test,如果测试通过则会在JUnit窗口中显示绿色条。
通讯卫士主界面布局activity_securityphone.xml中黑名单列表ListView的Item布局是item_list_blackcontact.xml。
主界面逻辑代码SecurityPhoneActivity包括显示黑名单信息,点击删除将当前黑名单从数据库中删除。
主界面通过ListView显示黑名单列表时,使用的数据适配器BlackContactAdapter。
添加黑名单逻辑,包括手动输入和从联系人列表中选择。
联系人列表的开发同上一章内容,这一部分包括:ContactInfo联系人信息的实体类、ContactInfoParser联系人信息的解析器(从系统通信录中读取联系人信息
)、ContactAdapter用于填充手机联系人信息的数据适配器、ContactSelectActivity显示系统联系人界面。
当电话和短信到来时都会产生广播,因此可以利用广播接收者将广播终止,实现拦截。
拦截短信InterceptSmsReciever,在广播中获取到电话号码以及短信内容,查询该号码是否在黑名单中(需在清单文件中注册并设置优先级最高)。
拦截电话InterceptCallReciever,需要自动挂断电话并且不在界面上显示该记录,由于Android系统的安全性而隐藏了挂断电话的服务方法,
因此该操作只能通过反射获取底层服务(使用了AIDL进程间通信)。具体参考代码和以下注释说明:
/**
* 挂断黑名单的呼入电话,需要复制两个aidl。
* 首先通过反射 获取到ServiceManager字节码,然后通过该字节码获取getService()方法,
* 该方法接收一个String类型的参数,然后通过invoke()执行getService()方法。
* 由于getService()方法是静态的,因此invoke()的第一个参数可以为null,第二个参数是TELEPHONY_SERVICE。
* 由于getService()方法的返回值是一个IBinder对象(远程服务的代理类),因此需要使用AIDL的规则将其转化为接口类型,
* 由于操作是挂断电话,因此需要使用与电话相关的ITelephony.aidl,然后调用接口中的endCall()方法将电话挂断即可。
* 还要注意的是:
* 与电话相关的操作一般都使用TelephonyManager类,但是由于挂断电话的方法在ITelephony接口中,而这个接口是隐藏的(@hide),
在开发时看不到,因此需要使用ITelephony.aidl。
* 在使用ITelephony.aidl时,需要创建一个与其包名一致的包com.android.internal.telephony,然后把系统的ITelephony.aidl文件复制进来。
* 同时,由于ITelephony.aidl接口关联了NeighboringCellInfo.aidl,也需要一起复制进来。
但是注意,NeighboringCellInfo.aidl所在的包名是android.telephony,
* 因此还需要新建一个android.telephony包,然后把NeighboringCellInfo.aidl放到该包中。
*/
同时,该广播也要注册,并添加相应权限(CALL_PHONE,READ_CALL_LOG,WRITE_CALL_LOG)。
【第四章】
AppManagerAdapter主界面的数据适配器
AppInfo应用程序的实体类
AppInfoParser获取手机中的所有应用程序
DensityUtil单位dip与px互转的工具类
EngineUtils启动、卸载、分享、设置应用的业务工具类
AppManagerActivity展示软件管家界面,包括应用列表、手机剩余内存、SD卡剩余内存
软件管家界面中,ListView中的Item布局item_appmanager_list.xml(在选中某个应用程序时,程序下方会浮出小条目,提供启动卸载分享设置四个选项)。
在获取应用程序列表之前首先需要创建一个实体类AppInfo,用于存储应用程序的相关信息。
在布局文件中设置单位时可以使用px或dip,通常情况下会使用dip,这样可以保证在不同分辨率机器上布局的一致。
但是代码中,有很多控件中都只提供设置px的方法,例如AppManagerAdapter文件中动态设置TextView的setPadding()方法并没有提供设置dip的方法,
因此需要转换后再调用。
在业务工具类EngineUtils中,当用户选中某个条目时,隐藏在下面的线性布局就会弹出(启动、卸载、分享、设置四个选项)。
软件管家界面中,AppManagerActivity将手机内存信息及应用程序列表等数据加载到界面中显示(本类是重点,逻辑比较多)。
只要使用了ListView控件,必须创建数据适配器AppManagerAdapter,用于填充数据到界面中。
【第五章】
手机中的病毒都存在于apk文件中,只要将病毒所在的apk文件删除即可将其清理掉。
手机杀毒模块的核心代码就是查询程序的MD5码是否在病毒库中,如果在数据库中那么该程序就是病毒,不在就不是病毒。
MD5Count.exe是一种获取MD5码的工具。每个程序的MD5码都是唯一的。如果两个文件内容是相同的,即使文件名字不同,其MD5码也是一样的。
ScanVirusAdapter杀毒界面ListView的适配器
AntiVirusDao病毒数据库的操作类
ScanAppInfo应用程序的实体类
MD5Utils获取文件MD5码的工具类
VirusScanActivity手机杀毒模块的主界面逻辑,主要包含上次查杀时间与跳转按钮
VirusScanSpeedActivity病毒查杀进度界面逻辑
这里使用较完整的第三方病毒数据库antivirus.db,该文件在assets目录下,使用SQLite Expert可以打开该数据库。
病毒查杀界面activity_virusscan.xml,逻辑代码VirusScanActivity实现在初始化时将病毒数据库从assets目录下复制到工程目录中,
并通过sharedpreferences得到上次扫描的时间显示在界面上。
在查杀进度界面中使用了ListView控件,需要定义一个Item布局item_list_applock.xml用于展示应用程序信息。
VirusScanSpeedActivity实现显示查杀进度的百分比、正在扫描的程序、显示已扫描程序的列表。
【第六章】
CacheCleanAdapter扫描缓存界面的ListView适配器
CacheInfo用于存储缓存信息的实体类
CacheClearListActivity扫描缓存程序界面的逻辑代码
CleanCacheActivity缓存清理界面的逻辑
缓存信息是通过AIDL接口调用系统底层方法获取的。
扫描缓存界面布局activity_cacheclearlist.xml中的@anim/broom_animation是一个帧动画,类似GIF图片,通过一系列图片依次显示来模拟动画。
该界面下的ListView的缓存清理Item布局item_cacheclean_list.xml。
扫描缓存的逻辑是获取每个应用程序的缓存大小、将所有缓存累加在一起展示在界面上、展示扫描后的应用列表。
在获取缓存时,IPackageStatsObserver是一个远程服务的AIDL接口,获取缓存时需要使用这个接口,因此需要创建一个android.content.pm包,
将IPackageStatsObserver.aidl复制到这个包下。由于该接口还依赖于PackageStats.aidl接口,也需要复制到该包下。
同时需要在清单文件中注册权限GET_PACKAGE_SIZE。
在清理缓存时,需要在界面上不停地更新数据,显示清理了多少缓存;清理完成时,动态切换布局,显示完成界面。
在方法cleanAll()中,清除全部 缓存,是利用Android系统的一个漏洞:freeStorageAndNotify
//通过反射的形式获取到freeStorageAndNotify(long freeStorageSize,IPackageDataObserver observer)方法,
//第一个参数表示要释放的缓存大小,第二个是远程服务接口。漏洞在于第一个参数,
//当freeStorageSize设置的值大于手机本身的内存空间时,可以清除掉所有缓存。
由于用到IPackageDataObserver.aidl接口,需要将其复制到android.content.pm包中。同时需要在清单文件中注册权限CLEAR_APP_CACHE。
【第七章】
ProcessManagerAdapter进程管理的数据适配器
TaskInfo进程信息的实体类
AutoKillProcessService锁屏清理进程的服务类
SystemInfoUtils获取手机内存信息的工具类
TaskInfoParser进程信息的解析器
ProcessManagerActivity进程管理界面逻辑
ProcessManagerSettingActivity进程设置界面逻辑(是否显示系统进程、锁屏时是否清理进程)
进程管理界面布局activity_processmanager.xml,界面中的ListView的Item布局item_processmanager_list.xml展示条目数据。
将所有正在运行的进程分为用户进程和系统进程展示,当选中某些进程时,点击清理即可。
用于获取系统信息SystemInfoUtils工具类。
用于获取正在运行的进程TaskInfoParser工具类。
在清理进程时,ProcessManagerSettingActivity可以设置为在手机睡眠时自动清理进程(利用服务和广播接收者在锁屏时自动清理进程)。
当设置界面中开启锁屏清理时,就会打开进程清理服务,在该服务中注册了监听屏幕锁屏的广播接收者,当监听到锁屏时会自动清理进程。
AutoKillProcessService服务在清单文件中注册中,persistent="true"可以保证该应用程序所在进程不会被LMK机制(最小内存阀值)杀死。
【第八章】
TrafficDao对流量数据库进行修改查询增加的工具类
TrafficOpenHelper保存流量信息的数据库
BootCompleteReciever监听开机启动的广播接收者,当手机开机时打开服务
TrafficMonitoringService获取流量具体数据的服务
SystemInfoUtils判断服务是否开启的工具类
OperatorSetActivity信息设置界面(第一个界面)逻辑
TrafficMonitoringActivity流量监控界面(第二个界面)逻辑
OperatorSetActivity运营商设置界面将选择的手机使用的运营商存储到SharedPreferences对象中,点击确定按钮后进入流量监控界面。
存储每日流量使用SQLite数据库,由于向运营商发送短信只能获取本月使用总流量和本月已用流量,无法得到每日使用流量,需要实时计算,
并根据日期将使用的流量存储到数据库中,不断更新数据库。TrafficOpenHelper创建数据库traffic.db。TrafficDao实现对数据库操作。
流量监控界面,实现显示当天、本月流量。点击校正流量时,会自动发送(代码以联通3G为例)一条短信进行获取当前流量使用情况并显示。
开启一个服务,实时统计已使用的流量数据,获取应用程序的实时流量信息,并通过SharedPreferences对象进行存储。
另外,为了实现在手机刚开机时开启流量监控服务,利用开机广播监听并开启该服务。
【第九章】
AppLockAdapter未加锁和已加锁界面ListView控件的适配器
AppLockDao程序锁数据库的操作逻辑
NumBelongtoDao查询号码归属地的数据库逻辑
AppLockOpenHelper创建程序锁数据库
AppInfo应用程序的实体类
SmsInfo短信的实体类
AppLockFragment已加锁应用的界面逻辑
AppUnLockFragment未加锁应用的界面逻辑
AppLockService程序锁服务(判断应用是否已加锁,如果应用加锁则打开应用时显示输入密码框)
AppInfoParser获取手机中所有的应用程序的工具类
Crypto加密和解密base64编码后的内容,用作程序锁密码
DensityUtil工具类dp与px互转换
SmsBackUpUtils短信备份的工具类
SmsReducitionUtils短信还原的工具类
UIUtils封装Toast的工具类
AdvancedToolsView自定义控件,封装高级工具模块的主页面
MyCircleProgress自定义控件(带进度的圆形按钮),用于显示短信备份与还原时的进度
AdvancedToolsActivity高级工具主界面逻辑
AppLockActivity程序锁界面逻辑
EnterPswActivity输入密码界面逻辑
NumBelongtoActivity归属地查询界面逻辑
SMSBackupActivity短信备份界面逻辑
SMSReducitionActivity短信还原界面逻辑
由于高级工具界面的四个条目布局类似,因此条目布局使用自定义组合控件来实现,自定义控件开发方法如下:
1.创建条目布局ui_advancedtools_view.xml,由三部分组成(左边图标,中间名称,右边箭头)
2.自定义属性,在res/values目录下建立attrs.xml文件,在该文件中对控件添加自定义属性(用于在自定义控件中引用)。
在自定义一个控件时,如果需要定义一些新的属性,就会用到attrs.xml文件。该文件中定义的是类的属性,
这些属性为了能在XML文件中被引用到,换句话说就是指定类中变量的值,这些属性会在类的构造方法中用到。
3.创建自定义控件类AdvancedToolsView,该类需要继承系统布局或者控件,并使用带AttributeSet参数的类的构造方法,
在构造方法中将自定义控件类中变量与attrs.xml中的属性连接起来。
4.布局中使用自定义控件,在布局中引用已经创建好的自定义控件activity_advancetools.xml。
在使用自定义控件时,每个控件的开始节点和结束节点都为自定义控件类的全路径,
其中custom_android:desc属性和android:src属性是自定义的,分别用于显示描述文字及功能图标。要注意,
在使用自定义控件时必须使用XML命名空xmlns:custom_android="http://schemas.android.com/apk/res/cn.itcast.mobliesafe"将
自定义控件引入到布局。
至此,自定义控件已经完成。要使用自定义控件,需要在具体类中创建该自定义控件类对象,然后对控件中属性进行操作。
主界面逻辑AdvancedToolsActivity,设置每一个条目的点击事件,跳转到相应界面,该类中需要创建上面的自定义控件对象。
查询号码归属地,用到一个第三方数据库address.db(在assets目录下),该库存储了大量的归属地信息,包含两个表data1和data2,
data1中RecNo自增主键,id存储号码前7位,outkey是外键与data2表中的id关联;data2中id为自增主键,location归属地,area座机区号。
查询时,根据号码截取前7位找到data1对应的outkey值,然后将该值作为data2中的id查询对应的location值;如果是座机号码直接查询data2。
查询时,需要创建数据库操作类NumBelongtoDao。归属地查询逻辑NumBelongtoActivity。
要把短信备份在本地有很多方法,这里使用XML的形式保存短信内容。
Android手机中的短信是在data/data/com.android.provider.telephony应用的database目录下mmssms.db数据库中。
用SQLiteExpert工具打开该数据库的sms表,可以看到短信的内容。该表中,只需关心几项数据即可:
address短信地址,type短信类型(1代表接收,2发送),date短信时间,body短信内容。只需将这几项数据以节点形式存入XML文件即可。
在类SmsBackUpUtils获取短信内容时,不直接操作数据库,而是使用ContentResolver得到短信数据库中的这几项数据。
使用base64对象将短信内容加密解密Crypto。
短信备份界面使用了自定义控件(界面中间有一个红色按钮,点击之后开始备份短信,并显示备份进度):
由于要在该控件中使用自定义的属性,显示备份进度和颜色,因此先要在attrs.xml中创建对应的变量,根节点是MyCircleProgress,
然后创建控件类MyCircleProgress,接着在布局activity_smsbackup.xml中引用该控件,
最后在短信备份逻辑界面SMSBackupActivity上创建该控件对象来使用。
相对应的,在还原手机中删除的短信时,使用XML解析器将已备份的短信还原。
短信还原界面与短信备份界面唯一不同的地方就是中间按钮的文字。
短信还原逻辑SMSReducitionActivity控制短信的还原、取消、还原进度。
程序锁功能是将加锁的程序信息存入数据库,当程序锁服务打开时,后台会运行一个服务检查当前打开的程序,打开加锁程序时会弹出输入密码界面。
首先创建用于存储加锁应用的数据库AppLockOpenHelper及数据库操作类AppLockDao。获取手机上的所有应用程序AppInfoParser。
为了方便对应用进行操作,定义应用程序的实体类AppInfo。
程序锁界面包含两个Fragment(分别展示已加锁和未加锁界面),并使用ViewPager控件使这两个界面可以滑动切换。
程序锁逻辑AppLockActivity是控制两个Fragment的切换(对应用的加解锁操作都在Fragment中进行)。
未加锁界面fragment_appunlock.xml和已加锁fragment_applock.xml界面;未加锁逻辑AppUnLockFragment和已加锁逻辑AppLockFragment。
未加锁和已加锁界面ListView使用的数据适配器都是AppLockAdapter,它接收一个List集合,不同界面调用Adapter时传输的数据集合不同。
程序锁服务AppLockService通过获取任务栈的信息来判断当前开启的是哪个应用,是否弹出密码锁界面。
密码锁界面activity_enterpsw.xml,密码锁逻辑EnterPswActivity。
【第十章】
SystemInfoUtils判断程序锁服务是否运行
SettingView自定义控件类(设置中心的条目)
SettingsActivity设置中心界面逻辑
自定义控件布局ui_settings_view.xml。
自定义控件属性(在attrs.xml中的SettingView节点中)。
实现自定义控件类SettingView。
设置中心界面在布局activity_settings.xml中引入两个自定义组合控件SettingView。
设置中心逻辑SettingsActivity,完成黑名单及程序锁的设置。