手机卫士项目
来源:互联网 发布:sql入门到精通 编辑:程序博客网 时间:2024/05/18 15:30
手机卫士项目
下载地址:http://download.csdn.net/detail/miaozhenzhong/9622004
1. 项目功能描述:
1. 手机防盗功能:远程销毁数据,远程锁屏,远程警报, SIM卡变更报警, GPS追踪
2. 通讯卫士: 来电和短信黑名单的管理
3. 软件管理: 应用的列表展示,应用的卸载,运行与分享
4. 流量管理: 显示所有应用的上传和下载流量
5. 进程管理: 显示所有运行进程列表和内存占用大小,对选中的进程进行一键清理
6. 缓存清理:清理缓存
7. 高级工具: 手机归属地查询,短信备份与还原,程序锁和常用电话查询
8. 设置中心: 手机归属地服务,归属地显示风格,归属地显示位置,程序锁服务
9. 手机杀毒: 对手机中安装的所有应用进行病毒查杀,发现危险应用进行卸载
2. 项目功能与技术分解
功能
技术点
功能一:欢迎界面及版本更新检查
1. 使Activity界面没有标题或通知栏
theme, style
2. 显示界面布局
LinearLayout
3. 自定义旋转进度条
<rotate>, indeterminateDrawable
4. 得到应用的版本号
PackageManger
5. 显示透明,缩放,旋转动画
AlphaAnimation,ScaleAnimation,RotateAnimation
6. 拷贝assert下的db文件到files
AssertManager, 文件读写
7. 版本更新流程
画图分析
8. 联网请求远程服务器数据
URL, HttpUrlConnection/HttpClient/Volley
9. 解析xml/json数据并封装到对象中
XmlPull和JSONObject或GSON
10. 分线程网络请求后UI界面更新
Thread + Handler
11. 将apk保存在sd卡
Environment, Context
12. 读取手机联网状态
ConnectivityManager
功能二:主界面的显示和相关功能
显示主界面及操作
GridView, BaseAdapter
显示带输入框的修改名称Dialog
AlertDialog
自定义名称的存储与读取
SharedPreference
显示密码设置与登陆界面
自定义内容布局的AlertDialog
密码的存储与读取
SharedPreference
密码加密处理
MD5加密
连续2次back键才退出应用
KeyEvent处理, Handler
功能四:手机防盗的设置
设置流程
画图分析
各个设置界面
<RelativeLayout>, <LinearLayout>
<style>, <shape><selector>
设置界面之间的跳转动画
<translate>, overridePendingTransition()
绑定当前手机中的SIM卡
TelephonyManager,SharedPreference, CheckBox
显示手机联系人列表
ContentResolver,Phone,Cursor,ListView,BaseAdapter,ProgressDialog, Handler, Thread
设置页面得到选择的某个联系人号
startActivityForResult(), onActivityResult(), setResult()
使当前应用能获取设置管理权限
DevicePolicyManager, DeviceAdminReceiver,
<receiver>配置
开机检查SIM卡有没有更换
BroadcastReceiver及配置, SmsManager
短信命令实现gps追踪,远程数据销毁,远程锁屏,远程报警功能
短信BroadcastReceivere及配置,
LocationManager,DevicePolicyManager, MediaPlayer
功能五:手机归属地查询与来电显示
加载手机归属地数据库文件
流程分析, AsyncTask, ProgressDialog,
SD卡文件读写
查询手机归属地
APIDemo动画效果,SQLiteDatabase,
手机号归属地规则
设置显示风格
AlertDialog, SharedPreference
设置显示的位置
TouchEvent,LayoutParams, SharedPreference
来电显示归属地
Service,TelephonyManager, WindowManager,LayoutParams,
SharedPreference
功能六:通讯卫士
黑名单管理
SqliteOpenHelper, SqliteDataBase
ListActivity, ArrayAdapter
ContextMenu, AlertDialog
黑名单来电与短信拦截
Service, AIDL挂断电话,
短信BroadcastReceiver
功能七:软件管理
应用的列表显示,卸载,运行与分享
PackageManager,Map<Boolean, List<AppInfo>>,AsyncTask,
BaseAdapter, PopupWindow,
分享/卸载/运行Intent
功能八:程序锁
应用的列表显示, 加锁与解锁
SqliteDatabase, TranslateAnimation,
ContentProvider, ContentResolver,
ContentObsver, Service,
Thread,ActivityManager
功能九: 任务管理
显示系统和第三方应用进程列表
PackageManager, ActivityManager,
AsyncTask, ListView,BaseAdapter,
Map<Boolean,List<TaskInfo>>
内存的清理
ActivityManager
清理缓存的widget
AppWidgetProvider,<appwidget-provider>
RemoteView, Service,
PendingIntent, Timer/TimerTask
功能十:系统优化
清理缓存
PackageManager, AIDL,
反射调用@hide的方法,锁定List,
清理缓存的intent
功能十一:流量管理
应用流量列表显示
SlidingDrawer, PackageManager,
ResolveInfo, TrafficStats
功能十二:高级工具
常用手机号查询
ExpandableListView, BaseExpandableListAdapter, AssetManager, SQLiteDatabase,
打电话Intent
备份手机中的所有短信数据
ContentResolver, 短信Uri, Cursor,
Gson, SD文件操作
还原备份的短信数据
同上, ProgressDialog
功能十三:手机杀毒
理解病毒与杀毒软件
计算机病毒与手机病毒
杀毒软件的基本原理
手机杀毒的界面
旋转动画RotateAnimation
自定义ProgressBar
动态添加视图viewgroup.addView(view, index)
扫描应用, 查杀病毒
拷贝包含病毒数据的db文件
得到所有应用的名称和签名 PackageManager
对应用签名进行MD5加密
异步查找, 同步显示进度: AsyncTask毒应用
3. 应用功能详解
3.1. 功能一: 欢迎界面及版本更新检查(难)
1) 应用结构分析
在创建一个新的应用时,就需要对整个应用的包结构进行一个设计, 对应用包的组织大致有两种方法
l 方式一:按模块来组织代码的包结构,较大型的应用比较合适
办公软件
开会模块 com.atguigu.meeting
发工资模块 com.atguigu.money
出差模块 com.atguigu.travel
l 方式二: 按类型来组织代码的包结构, 中小型应用比较合适
手机卫士:
应用界面相关 com.atguigu.ms. activity|ui
显示列表的适配器 com.atguigu.ms.adapter
数据对象封装 com.atguigu.ms.bean|domain|entity
自定义视图 com.atguigu.ms.view|widget
复杂业务 com.atguigu.ms.engine
数据持久化 com.atguigu.ms.dao
广播接收者 com.atguigu.ms.receiver
后台服务 com.atguigu.ms.service
内容提供者 com.atguigu.ms.provider
相关工具类 com.atguigu.ms.util
访问网络的类 com.atguigu.ms.net|api APIClient
2) 界面布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_welcome_root"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="bottom|center_horizontal"
android:background="@drawable/logo">
<TextView
android:id="@+id/tv_welcome_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="版本号: 1.0"
android:textSize="20sp"
android:textColor="#000000"
android:layout_marginBottom="20dp"/>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"/>
</LinearLayout>
3) 自定义旋转进度条
l 定义进度条的旋转动画,指定旋转的图片
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/progess"/> //注意此属性不能提示
l 引入定义的动画
android:indeterminateDrawable="@anim/anim_loding_progress" //进度动画
android:indeterminateDuration="700" //旋转一周持续的时间
4) 动画效果
AlphaAnimation animation =new AlphaAnimation(0.0f, 1.0f);
animation.setDuration(2000);
mLinearLayout.startAnimation(animation);
5) 获取应用的当前版本号
PackageManager packageManager =context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0);
String version = packageInfo.versionName;
6) 将界面设置为无标题或全屏显示
l 动态编码的方式:
// 去掉窗口标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
// 隐藏顶部的状态栏
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView();
l 配置方式:
//方式二(不建议使用)
android:theme="@android:style/Theme.Black.NoTitleBar"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
//方式三
<style name="AppTheme" parent="AppBaseTheme">
<item name="android:windowNoTitle">true</item><!--没有标题-->
</style>
7) 拷贝assert下的db文件到files
/**
* 把assets目录下的assert下的address.db拷贝到
* /data/data/com.atguigu.ms/files/
* @param dbname
*/
private void copyDB(String dbname) {
File file = new File(getFilesDir(),dbname);
if(file.exists()&&file.length()>0){
Log.i (TAG, dbname+"数据库已经存在,不需要拷贝了...");
}else{
try {
InputStream is = getAssets().opendbname);
FileOutputStream fos = new FileOutputStream(file);
int len = 0;
byte buffer[] =new byte[1024];
while((len = is.read(buffer))!=-1){
fos.write(buffer, 0, len);
}
is.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8) 应用版本更新检查流程
画图分析
9) 读取手机联网状态:
/**
* 检查当前网络是否可用
*/
public static boolean isNetConnected (Context context) {
boolean connected = false;
// 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
ConnectivityManager manager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
// 获取active的NetworkInfo对象
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if(networkInfo!=null) {
// 得到是否连接状态
connected = networkInfo.isConnected();
}
return connected;
}
10) 设计服务器端版本更新信息数据格式
l XML数据格式
<?xml version="1.0" encoding="UTF-8"?>
<updateinfo>
<version>1.0</version>
<apkUrl>http://192.168.173.1:8080/SecurityServer/Security.apk</apkUrl>
<desc>解决了上一个版本中的不少bug,优化了网络请求逻辑</desc>
</updateinfo>
l JSON数据格式
{
version:"1.1",
apkUrl:"http://192.168.173.1:8080/SecurityServer/Security.apk",
desc:"解决了上一个版本中的不少bug,优化了网络请求逻辑"
}
11) 联网请求远程服务器数据
//创建url地址对象
URL url =new URL(urlPath);
//打开连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//设置可以从服务器端读取返回的数据
connection.setDoInput(true);
//设置连接超时的时间
connection.setConnectTimeout(2000);//5000
//得到服务器端返回的数据流
InputStream is = connection.getInputStream();
12) 使用XmlPull解析xml文件
private UpdateInfo parseXml(InputStream is)throws Exception {
UpdateInfo info = new UpdateInfo();
XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser();
pullParser.setInput(is, "utf-8");
int eventType = pullParser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
String tagName = pullParser.getName();
if ("version".equals(tagName)) {
info.setVersion(pullParser.nextText());
} else if ("apkUrl".equals(tagName)) {
info.setApkUrl(pullParser.nextText());
} else if ("desc".equals(tagName)) {
info.setDesc(pullParser.nextText());
}
}
eventType = pullParser.next();
}
return info;
}
13) 使用JsonObject解析json数据
private UpdateInfo parseJson(InputStream is)throws Exception {
//将数据流封装为json对象
String jsonString = Utils.readString(is);
JSONObject object = new JSONObject(jsonString);
//得到json对象中的数据封装为updateInfo对象
UpdateInfo info = new UpdateInfo();
info.setVersion(object.getString("version"));
info.setApkUrl(object.getString("apkUrl"));
info.setDesc(object.getString("desc"));
return info;
}
//使用Gson框架
UpdateInfo = new Gson().fromJson(jsonString, new TypeToken<UpdateInfo>(){}.getType())
14) 分线程网络请求后UI界面更新
//handler在主线程中处理分线程发过来的结果消息
private Handler handler =new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case REQUEST_UPDATE_ERROR:
break;
case REQUEST_UPDATE_SUCCESS:
break;
case REQUEST_APK_ERROR:
break;
case REQUEST_APK_SUCCESS:
break;
default:
break;
}
}
};
//启动分线程去请求服务器得到最新版本信息
new Thread(new Runnable() {
@Override
public void run() {
try {
info = APIClient.getUpdateInfo();
handler.sendEmptyMessage(REQUET_UPDATE_SUCCESS);
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(REQUEST_UPDATE_ERROR);
}
}
}).start();
15) 提示去下载和显示下载进度
/**
* 显示diaog,提示用户去下载
*/
private void showDownloadDialog() {
new AlertDialog.Builder(this)ddd.setTitle("版本更新")
.setMessage("有更新的版本,是否立即下载更新?")
.setPositiveButton("下载",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
startDownloadApk();
}
}).setNegativeButton("取消",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
toMain();
}
}).show();
}
/**
* 开启分线程下载apk,并进行安装更新
*/
private void startDownloadApk() {
//下载的url
final String apkUrl =info.getApkUrl();
//保存apk的本地file对象
createApkFile();
//显示下载进度的progressDialog
showDownloadProgress();
new Thread(new Runnable() {
@Override
public void run() {
try {
APIClient.downloadAPK(apkUrl,apkFile,pd);
handler.sendEmptyMessage(DOWNLOAD_APK_SUCCESS);
} catch (Exception e) {
e.printStackTrace();
handler.sendEmptyMessage(REQUEST_APK_ERROR);
}
}
}).start();
}
16) 将apk保存在sd卡中:
File sdFile = Environment.getExternalStorageDirectory();
apkFile = new File(sdFile,"update.apk");
/**或者**/
File filesDir = getExternalFilesDir(null);
apkFile = new File(filesDir,"update.apk");
17) 安装APK
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(apkFile),"application/vnd.android.package-archive");
context.startActivity(intent);
18) 相关权限
<!-- 读取手机联网状态的权限 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 读取wifi状态的权限-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!-- 请求网络的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- sd卡操作的权限-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3.2. 功能二: 主界面的显示和功能项名称修改
1) 主界面的显示
private static final String[]NAMES =new String[] {
"手机防盗","通讯卫士","软件管理","流量管理","进程管理",
"手机杀毒","缓存清理","高级工具","设置中心" };
private static final int[]ICONS =new int[] { R.drawable.widget01,
R.drawable.widget02,R.drawable.widget03, R.drawable.widget04,
R.drawable.widget05, R.drawable.widget06,R.drawable.widget07,
R.drawable.widget08, R.drawable.widget09 };
<GridView
android:id="@+id/gv_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:numColumns="3"
android:verticalSpacing="8dip">
</GridView>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="80dp"
android:layout_height="80dp"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:id="@+id/iv_item_main_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/ic_launcher"
android:scaleType="fitXY"/>
<TextView
android:id="@+id/tv_item_main_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="手机防盗"
android:textSize="18sp"
android:layout_marginTop="2dp"
android:textColor="#000000"/>
</LinearLayout>
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView ==null) {
convertView = inflater.inflate(R.layout.main_item,null);
}
ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_item_main_icon);
TextView textView = (TextView) convertView.findViewById(R.id.tv_item_main_name);
imageView.setImageResource(ICONS[position]);
textView.setText(NAMES[position]);
return convertView;
}
2) 显示带输入框的Dialog
final EditText editText =new EditText(this);
editText.setHint("输入新的名称");
new AlertDialog.Builder(this)
.setView(editText)
.setTitle("名称修改")
.setPositiveButton("确定",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
}
}).setNegativeButton("取消",null).show();
3) 自定义名称的存储与读取
SharedPreferences preferences= getSharedPreferences("config", Context.MODE_PRIVATE);
//保存数据
Editor edit = preferences.edit().putString("lost_name", name).commit();
//读取数据
String lostName = preferences.getString("lost_name",null);
4) 显示进入手机防盗界面的Dialog
l 是否设置过密码的判断
通过SharedPreferences存储密码来实现
l 设置密码的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dip"
android:background="#ffffff"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="50dip"
android:background="#E0EEEE"
android:gravity="center"
android:text="设置密码"
android:textSize="18sp" />
<EditText
android:id="@+id/et_pwd_set_pwd"
android:layout_width="280dip"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword" />
<EditText
android:id="@+id/et_pwd_set_confirm"
android:layout_width="280dip"
android:layout_height="wrap_content"
android:hint="请再次输入密码"
android:inputType="textPassword" />
<LinearLayout
android:layout_width="280dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/btn_pwd_set_confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="确定" />
<Button
android:id="@+id/btn_pwd_set_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="取消" />
</LinearLayout>
</LinearLayout>
l 登陆的布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="300dip"
android:background="#ffffff"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="50dip"
android:background="#E0EEEE"
android:gravity="center"
android:text="登 陆"
android:textSize="18sp" />
<EditText
android:id="@+id/et_pwd_login_pwd"
android:layout_width="280dip"
android:layout_height="wrap_content"
android:hint="请输入密码"
android:inputType="textPassword" />
<LinearLayout
android:layout_width="280dip"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/btn_pwd_login_confirm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="确定" />
<Button
android:id="@+id/btn_pwd_login_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="取消" />
</LinearLayout>
</LinearLayout>
5) 自定义Button
l 9patch图片的制作
1) 指定patch区域(可复制区域)
2) 指定能显示内容的区域
l Selector多状态图片的配置
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" //此item必须在前面
android:drawable="@drawable/button_pressed"/>
<item android:drawable="@drawable/button_normal"/>
</selector>
6) 连续点击回退键,才退出应用
l 监听back键的up事件
l 使用Handler进行延迟消息处理
private boolean exit =false;//只有它为true才退出
private Handlerhandler =new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(msg.what==1) {
Log.e("TAG","2s内没有再点back");
exit = false;//2s内没有再点back,下次点back不能退出
}
return false;
}
});
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if(keyCode==KeyEvent.KEYCODE_BACK) {
if(!exit) {
exit = true;//标识再点一次back就退出
handler.sendEmptyMessageDelayed(1, 2000);
return true;
} else {
handler.removeMessages(1);
}
}
return super.onKeyUp(keyCode, event);
}
7) 对密码进行MD5加密
l 对于应用中保存的密码,需要做加密处理再保存起来,这样就不至于将用户密码泄露
l 项目中用得最多的一种加密方式: MD5加密 明文123456 密文
l MD5加密是将指定字符串加密为一个32位的字符串,是一个不可逆的操作
l 在线MD5解密是通过保存很多明文与密文的方式来实现解密的
1. 先将指定的字符串转换为一个16位的byte[]
2. 遍历取出数组中的每个byte元素
3. 将取出的byte值与255(0xff)做与运算(&)后得到一个255以内的数值
4. 将得到的数值转换为16进制的字符串,如果它只有一位,在它的前面补0
5. 将生成的16个二位16进制形式的字符串连接起来,它就是md5加密后的32位字符串
public static String md5(String pwd) {
StringBuffer sb = new StringBuffer();
try {
//创建用于加密的加密对象
MessageDigest digest = MessageDigest.getInstance("md5");
//将字符串转换为一个16位的byte[]
byte[] bytes = digest.digest(pwd.getBytes("utf-8"));
for(byte b : bytes) {//遍历
//与255(0xff)做与运算(&)后得到一个255以内的数值
int number = b & 255;//也可以& 0xff
//转化为16进制形式的字符串,不足2位前面补0
String numberString = Integer.toHexString(number);
if(numberString.length()==1) {
numberString = 0+numberString;
}
//连接成密文
sb.append(numberString);
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
3.3. 功能三: 手机防盗与设置(难)
1) 设置流程
画图分析
2) 各个设置界面布局
l 关键技术点:< style>, <shape>, < RelativeLayout>, < LinearLayout>
l 布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/title_background"
android:orientation="vertical" >
<!--标题-->
<TextView
android:id="@+id/tv_title"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:text="1.欢迎使用手机防盗"
android:gravity="center"
android:textSize="25sp"
android:background="@color/title_text_color"
android:layout_marginBottom="10dp"/>
<TextView
android:id="@+id/tv_guide_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="您的手机防盗卫士: "
android:textSize="20sp" />
<!-- 带星号图标的文本 -->
<TextView
style="@style/SetupGuideContent"
android:text="@string/guide1_content"/>
<TextView
style="@style/SetupGuideItem"
android:text="@string/guide1_item1"/>
<TextView
style="@style/SetupGuideItem"
android:text="@string/guide1_item2"/>
<TextView
style="@style/SetupGuideItem"
android:text="@string/guide1_item3"/>
<TextView
style="@style/SetupGuideItem"
android:text="@string/guide1_item4"/>
<!-- 显示设置进度的图标 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dip"
android:gravity="center_horizontal"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/presence_online"
android:contentDescription="@string/hello_world"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/presence_invisible"
android:contentDescription="@string/hello_world"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/presence_invisible"
android:contentDescription="@string/hello_world"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/presence_invisible"
android:contentDescription="@string/hello_world"/>
</LinearLayout>
<!—设置界面跳转的按钮 -->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="5dp">
<Button
android:id="@+id/btn_guide_next"
android:text="@string/next"
style="@style/SetupGuideNext"/>
</RelativeLayout>
</LinearLayout>
l <style>的使用
<!-- 防盗功能列表项-->
<style name="SetupGuideItem">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textSize">18sp</item>
<item name="android:drawableLeft">@android:drawable/btn_star_big_on</item>
<item name="android:layout_marginTop">8dip</item>
<item name="android:gravity">center_vertical</item>
</style>
<!-- 防盗设置界面"下一步"按钮-->
<style name="SetupGuideNext">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:drawableRight">@drawable/next</item>
<item name="android:layout_alignParentBottom">true</item>
<item name="android:layout_alignParentRight">true</item>
</style>
<!-- 防盗设置界面"上一步"按钮-->
<style name="SetupGuidePre">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:drawableRight">@drawable/previous</item>
<item name="android:layout_alignParentBottom">true</item>
<item name="android:layout_alignParentLeft">true</item>
</style>
l <shap>的使用
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 描边 -->
<stroke
android:width="0.51dip"
android:color="#ffbc04e5" />
<!-- 渐变 -->
<gradient
android:startColor="#ffbc04e5"
android:centerColor="#ffc823ed"
android:endColor="#ffce30f2"/>
</shape>
3) 设置界面之间的跳转动画
l 关键技术: <translate>, overridePendingTransition()
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="400" >
</translate>
//启用Activity切换动画
overridePendingTransition(R.anim.translate_right_in, R.anim.translate_left_out);
4) 绑定当前手机中的SIM卡
l 关键技术: TelephonyManager, SharedPreference, CheckBox
public static String getSimNumber(Context context) {
TelephonyManager telephonyManager =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return telephonyManager.getSimSerialNumber();
}
5) 显示手机联系人列表:
l 关键技术: ContentResolver, Phone, Cursor, ListView, BaseAdapter, ProgressDialog, Handler, Thread
/**
* 得到所有联系人信息的集合
*/
public List<ContactInfo> getContactInfos() {
List<ContactInfo> list = new ArrayList<ContactInfo>();
ContentResolver resolver =context.getContentResolver();
String[] projection = { Phone.DISPLAY_NAME, Phone.NUMBER };
Cursor cursor = resolver.query(Phone.CONTENT_URI, projection, null,null,null);
while (cursor.moveToNext()) {
String name = cursor.getString(0);
String number = cursor.getString(1);
list.add(new ContactInfo(name, number));
}
cursor.close();
return list;
}
6) 在设置页面得到选择的某个联系人号
l 关键技术: startActivityForResult(), onActivityResult(), setResult()
protected void onActivityResult(int requestCode,int resultCode, Intent data) {
if(requestCode==REQ_CONTACT_CODE && resultCode==2) {
//显示选择的手机号
String number = data.getStringExtra("number");
et_guide_phoneNumber.setText(number);
}
}
7) 开机检查SIM卡有没有更换
l 关键技术: 开机BroadcastReceiver及配置, SmsManager
public class BootCompleteReceiverextends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 条件1:启动了防盗监听
boolean isProtected = SpUtils.getInstance(context).getBoolean(SpUtils.IS_PROTECTED,false);
if (!isProtected)
return;
// 条件2:当前手机SIM号与保存的SIM号不一致
String currSimNumber = Utils.getSimNumber(context);
String simNumber = SpUtils.getInstance(context).getString(SpUtils.SIM,null);
if (simNumber !=null && simNumber.equals(currSimNumber))
return;
// 发送一个提示短信给保存的安全号
String number = SpUtils.getInstance(context).getString(SpUtils.NUMBER,null);
Log.e("TAG","number="+number);
if (!Utils.isEmpty(number)) {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(number, null,"SIM卡有变更,手机可能被盗了!",null,null);
}
}
}
<receiver android:name=".receiver.BootCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
8) 短信命令实现gps追踪,远程数据销毁,远程锁屏,远程报警功能
l 关键技术: 短信BroadcastReceivere及配置, LocationManager, DevicePolicyManager, MediaPlayer
l 读取接收到短信的BR类
public class SmsReceiverextends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
byte[] pdu = (byte[]) pdus[0];
SmsMessage smsMessage = SmsMessage.createFromPdu(pdu);
String phoneNumber = smsMessage.getOriginatingAddress();
String message = smsMessage.getMessageBody();
if ("#location#".equals(message)) {//gps追踪
abortBroadcast();
Log.e("TAG","回送位置信息!");
GpsUtils.sendLocation(context, safeNumber);
} else if ("#reset#".equals(message)) {// 远程销毁数据(恢复出厂模式)
abortBroadcast();
Log.e("TAG","远程销毁数据!");
Utils.resetPhone(context);
} else if ("#lock#".equals(message)) {// 远程锁屏
abortBroadcast();
Log.e("TAG","远程锁屏!");
Utils.lockScreen(context);
} else if ("#alarm#".equals(message)) {// 报警音乐
abortBroadcast();
Log.e("TAG","报警音乐!");
Utils.runAlarm(context, R.raw.jxmzf);
}
}
}
l 注册BR
<receiver android:name=".receiver.SmsReceiver">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
l 远程销毁数据
public static void resetPhone(Context context) {
DevicePolicyManager manager = (DevicePolicyManager)
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
manager.wipeData(0);
}
l 远程锁屏
public static void lockScreen(Context context) {
DevicePolicyManager manager = (DevicePolicyManager)
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
manager.resetPassword("23456", 0);//重新设置密码
manager.lockNow();//锁屏
}
l 警报音乐
public static void runAlarm(Context context, int resourceId) {
MediaPlayer mediaPlayer = MediaPlayer.create(context, resourceId);
mediaPlayer.setVolume(1.0f, 1.0f);//设置最大音量
mediaPlayer.start();
}
l GPS定位
n 三种定位方式:基于卫星(gps),基于网络(network),基于基站
n 基于卫星: 定位慢,准确度高,受环境影响大
北斗卫星导航系统(BDS)和美国GPS、俄罗斯GLONASS、欧盟GALILEO
n 基于网络:定位快,准确度低,受环境影响小, 但手机必须联网
n 基于基站:误差与基站数量与远近有关
public void sendLocation(Context context, String number) {
this.safeNumber = number;
lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// 判断GPS是否正常启动
if(!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
MSUtils.showMsg(context,"GPS没有开启");
return;
}
// 获取位置信息
Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if(location!=null) {
double longitude = location.getLongitude();
double latitude = location.getLatitude();
String result = "经度: "+longitude+",纬度: "+latitude;
Log.e("TAG","发送经纬度: "+result);
SmsManager.getDefault().sendTextMessage(number,null, result,null,null);
}
// 绑定监听
// 参数1,设备:有GPS_PROVIDER和NETWORK_PROVIDER两种
// 参数2,位置信息更新周期,单位毫秒
// 参数3,位置变化最小距离:当位置距离变化超过此值时,将更新位置信息
// 参数4,监听
// 备注:参数2和3,如果参数3不为0,则以参数3为准;参数3为0,则通过时间来定时更新;两者为0,则随时刷新
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 1,listener);
}
9) 激活应用的管理权限
l 关键技术: DevicePolicyManager, DeviceAdminReceiver, <receiver>
l 对于第三方应用, 如果需要做锁屏,恢复出厂设置等操作,需要提前先激活当前应用的对应管理权限
l Android 2.2 SDK提供了一个可管理和操作设备的API叫DevicePolicyManager,使用它可以接管手机的应用权限,对手机做出很多大胆的操作,比如锁屏、恢复出厂设置、设置密码、强制清除密码,修改密码、设置屏幕灯光渐暗时间间隔等操作。
l 在Manifest文件中定义配置
<receiver
android:name=".receiver.MyAdminReceiver"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/my_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
l 定义设备管理权限配置(/xml/my_admin)
<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>
l 定义设备管理的receiver
public class MyAdminReceiverextends DeviceAdminReceiver {
}
l 启动激活
public static void activeDevince(Context context) {
DevicePolicyManager manager = (DevicePolicyManager)
context.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName componentName = new ComponentName(context, MyAdminReceiver.class);
if (!manager.isAdminActive(componentName)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
context.startActivityForResult(intent, 1);
}
}
判断是否激活
private boolean isActive() {
DevicePolicyManager manager = (DevicePolicyManager)
getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName name = new ComponentName(this, DeviceAdminSampleReceiver.class);
return manager.isAdminActive(name);
}
10) 相关权限
<!-- 请求电话状态信息 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 读取手机联系人 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- 接收开机完成的广播 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 发送短信 -->
<uses-permission android:name="android.permission.SEND_SMS" />
<!-- 接收短信的权限 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<!-- 进行精确定位的权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3.4. 功能四:手机归属地查询与来电显示
1) 查询手机归属地
l 关键技术: APIDemo动画效果, SQLiteDatabase,手机号正则表达式,手机号归属地规则
l 查询规则:
n 手机号:共11位数字, 第一个为: 1,第二位为: 3—8,前7位决定其归属地
n 匪警号码 : 3位
n 模拟器 : 4位
n 客服电话 : 5位
n 本地号码 : 6,7,8,9位
n 外地座机号:大于或等于10位, 以0开头,其第2,3位或2,3,4位决定其归属地
public static String getAddess(String number) {
String address = number;
SQLiteDatabase database = SQLiteDatabase.openDatabase(path,null,
SQLiteDatabase.OPEN_READONLY);
/*
* 手机号:
* 1. 共11位数字
* 2. 第一个为: 1
* 3. 第二位为: 3--8
*/
String reg = "^1[345678]\\d{9}$";
if(number.matches(reg)) {
//手机号的归属地由其前7位来查询
String sql = "select location from data2 where id=(select outkey from data1 where id=?)";
Cursor cursor = database.rawQuery(sql,
new String[]{number.substring(0, 7)});
if(cursor.moveToNext()) {
number = cursor.getString(0);
} else {
address = "未知地区手机号";
}
cursor.close();
} else {
switch (number.length()) {
case 3:// 110,119,120
address = "匪警号码";
break;
case 4:// 5554,5556
address = "模拟器";
break;
case 5:// 10086,10010
address = "客服电话";
break;
case 6:
case 7:
case 8:
case 9:
address = "本地号码";
break;
default://外地座机号
if(number.startsWith("0") && number.length()>9) {
String sql = "select location from data2 where area =?";
Cursor cursor = database.rawQuery(sql,
new String[]{number.substring(1, 3)});
if(cursor.moveToNext()) {
String location = cursor.getString(0);
address = location.substring(0, location.length()-2);
break;
}
cursor = database.rawQuery("select location from data2 where area =?",
new String[]{number.substring(1, 4)});
if(cursor.moveToNext()) {
String location = cursor.getString(0);
address = location.substring(0, location.length()-2);
break;
}
cursor.close();
}
break;
}
}
return address;
}
2) 设置显示风格
关键技术: AlertDialog, SharedPreference
private void setStyle() {
String[] items = {"半透明","活力橙","苹果绿","孔雀蓝","金属灰"};
new AlertDialog.Builder(this)
.setTitle("选择颜色")
.setSingleChoiceItems(items, 0,new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
SpUtils.getInstance(AToolActivity.this)
.putInt(SpUtils.BG_COLOR_INDEX, which);
}
})
.setPositiveButton(android.R.string.ok,null)
.show();
}
3) 设置显示的位置
关键技术: TouchEvent,LayoutParams, SharedPreference
iv_drag_location.setOnTouchListener(this);
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
firstX = (int) event.getRawX();
firstY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getRawX();
int moveY = (int) event.getRawY();
int distanceX = moveX -firstX;
int distanceY = moveY -firstY;
int newLeft =iv_drag_location.getLeft() + distanceX;
int newTop =iv_drag_location.getTop() + distanceY;
int newRight =iv_drag_location.getRight() + distanceX;
int newBottom =iv_drag_location.getBottom() + distanceY;
iv_drag_location.layout(newLeft, newTop, newRight, newBottom);
firstX = moveX;
firstY = moveY;
break;
case MotionEvent.ACTION_UP:
int x =iv_drag_location.getLeft();
int y =iv_drag_location.getTop();
SpUtils.getInstance(this).putInt(SpUtils.LOCATION_X, x);
SpUtils.getInstance(this).putInt(SpUtils.LOCATION_Y, y);
break;
default:
break;
}
return true;
}
4) 来电显示归属地
关键技术: Service,TelephonyManager, WindowManager,LayoutParams
windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// Listen for changes to the device call state. 监听电话状态
telephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
private PhoneStateListenerlistener =new PhoneStateListener() {
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: //来电前或挂断电话后
removeAdressView();
break;
case TelephonyManager.CALL_STATE_RINGING: //响铃中
showAddress(incomingNumber);
break;
case TelephonyManager.CALL_STATE_OFFHOOK: //通话中
removeAdressView();
break;
default:
break;
}
}
};
WindowManager.LayoutParams params =new WindowManager.LayoutParams();
params.width = WindowManager.LayoutParams.WRAP_CONTENT;//宽度自适应
params.height = WindowManager.LayoutParams.WRAP_CONTENT;//高度自适应
params.format = PixelFormat.TRANSLUCENT;//设置成透明的
params.type = WindowManager.LayoutParams.TYPE_PHONE;//使addressView能移动
params.flags = WindowManager.LayoutParams. FLAG_NOT_FOCUSABLE; //使addressView不用获得焦点
// 根据设置的位置坐标定位
int x = SpUtils.getInstance(this).getInt(SpUtils.LOCATION_X, -1);
int y = SpUtils.getInstance(this).getInt(SpUtils.LOCATION_Y, -1);
if (x != -1) {
params.gravity = Gravity.LEFT | Gravity.TOP;
params.x = x;
params.y = y;
}
windowManager.addView(addressView, params);
<!-- 在window添加一个视图(不是toast) -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
判断服务是否已经开启
public static boolean isServiceRunning(Context context, String className) {
ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningServiceInfo> runningServices =
am.getRunningServices(Integer.MAX_VALUE); //取出所有运行的
for(RunningServiceInfo info : runningServices) {
String serviceClassName = info.service.getClassName();
if(serviceClassName.equals(className)) {
return true;
}
}
return false;
}
3.5. 功能五: 通讯卫士
1) 黑名单管理
l 关键技术: SqliteOpenHelper, SqliteDataBase, Android单元测试, ListActivity, ArrayAdapter,
ContextMenu, AlertDialog
l Andorid单元测试的使用
<!-- 配置测试插件 -->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.atguigu.my_ms" />
<!-- 添加支撑测试的类库包 -->
<uses-library android:name="android.test.runner" />
//单元测试类
public class BlackNumberDaoTestextends AndroidTestCase
l ContextMenu的使用
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.add(1, 1, 1, R.string.update_number);
menu.add(1, 2, 1, R.string.delete_number);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
int position = menuInfo.position;
switch (item.getItemId()) {
case 1:
showDeleteDialog(position);
break;
case 2:
dao.deleteNumber(numbers.get(position));
numbers.remove(position);
adapter.notifyDataSetChanged();
break;
default:
break;
}
return super.onContextItemSelected(item);
}
2) 黑名单来电与短信拦截
l 关键技术: Service, AIDL挂断电话,短信BroadcastReceiver, ContentResolver, ContentObersver
l 添加AIDL文件:
l 挂断电话
private void endCall() {//挂断电话
try {
// 通过反射拿到android.os.ServiceManager里面的getService这个方法的对象
Class clazz = Class.forName("android.os.ServiceManager");
Method method = clazz.getMethod("getService", String.class);
// 通过反射调用这个getService方法,然后拿到IBinder对象,然后就可以进行aidl啦
IBinder iBinder = (IBinder) method.invoke(null,
new Object[] { Context.TELEPHONY_SERVICE });
ITelephony telephony = ITelephony.Stub.asInterface(iBinder);
telephony.endCall();
} catch (Exception e) {
e.printStackTrace();
}
}
l 删除通话记录
l 直接删除通话记录不可行,因为生成通话记录的过程是一个异步的过程, 在挂断电话的瞬间, 通话记录表数据还没有执行添加, 只能通过Obersver来删除
private void deleteCallLog(final String number) {
final Uri uri = CallLog.Calls.CONTENT_URI;
getContentResolver().registerContentObserver(uri, true,
new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
getContentResolver().delete(uri,
"number=?", new String[] { number });
getContentResolver().unregisterContentObserver(this);
}
}
);
}
<!-- 打电话的权限:用于挂断电话-->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- 读写通话记录:用在删除通话记录-->
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />
3) 判断骚扰电话并显示通知
l 响铃时间不超过1s
l 不是联系人号
l 不是黑名单
private void showNotification(String incomingNumber) {
NotificationManager manager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Intent intent = new Intent(this, NumberSecurityActivity.class);
intent.putExtra("blackNumber", incomingNumber);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification =new Notification.Builder(this)
.setContentTitle("发现响一声电话!")
.setContentText("点击添加"+incomingNumber+"为黑名单")
.setSmallIcon(R.drawable.logo)
.setContentIntent(pi)
.build();
manager.notify(0, notification );
}
<!-- 读取手机联系人 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
3.6. 功能六:软件管理
1) 应用的列表显示
l 关键技术: PackageManager, Map<Boolean, List<AppInfo>>, AsyncTask, BaseAdapter
public Map<Boolean, List<AppInfo>> getAllAppInfos()throws Exception {
Map<Boolean, List<AppInfo>> map =new HashMap<Boolean, List<AppInfo>>();
List<AppInfo> systemInfos = new ArrayList<AppInfo>();
map.put(true, systemInfos); //key是否是系统应用
List<AppInfo> customerInfos = new ArrayList<AppInfo>();//只存储非系统的第三方应用
map.put(false, customerInfos);
PackageManager packageManager =context.getPackageManager();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0);
for (ResolveInfo ri : resolveInfos) {
String packageName = ri.activityInfo.packageName;
Drawable icon = ri.loadIcon(packageManager);
String appName = ri.loadLabel(packageManager).toString();
boolean isSystemApp =isSystemApp(packageManager, packageName);
AppInfo appInfo = new AppInfo(icon, appName, packageName, isSystemApp);
if (appInfo.isSystemApp()) {
systemInfos.add(appInfo);
} else {
userInfos.add(appInfo);
}
}
return map;
}
private boolean isSystemApp(PackageManager pm, String packageName) throws Exception {
PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
return (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
}
2) 应用的卸载,运行与分享
l 关键技术: PopupWindow, 分享/卸载/运行Intent
l 显示PopupWindow
popupView = View.inflate(this, R.layout.popup_view,null);
ll_app_uninstall = (LinearLayout)popupView.findViewById(R.id.ll_app_uninstall);
ll_app_uninstall.setOnClickListener(this);
ll_app_start = (LinearLayout)popupView.findViewById(R.id.ll_app_start);
ll_app_start.setOnClickListener(this);
ll_app_share = (LinearLayout)popupView.findViewById(R.id.ll_app_share);
ll_app_share.setOnClickListener(this);
popupWindow = new PopupWindow(popupView, 240, view.getHeight());
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popupWindow.showAsDropDown(view, 50, 0 - view.getHeight());
Animation animation = new ScaleAnimation(0f, 1f, 0f, 1f);
animation.setDuration(500);
popupView.startAnimation(animation);
l 分享应用
private void shareApp(String appName) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");//纯文本
//intent.putExtra(Intent.EXTRA_SUBJECT,"应用分享");
intent.putExtra(Intent.EXTRA_TEXT,"分享一个不错的应用: " + appName);//内容
startActivity(intent);
}
l 根据包名启动应用
private void startApp(String packageName) {
PackageManager packageManager = getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(packageName);
if (intent ==null) {
Utils.showToast(this,"此应用无法启动");
} else {
startActivity(intent);
}
}
l 根据包名卸载应用
private void uninStallApp(AppInfo appInfo) {
if (appInfo.isSystemApp()) {
Toast.makeText(this,"系统应用不能卸载!", 0).show();
} else if(getPackageName().equals(appInfo.getPackageName())) {
Toast.makeText(this,"当前应用不能卸载!", 0).show();
}else {
Intent intent = new Intent(Intent.ACTION_DELETE);
intent.setData(Uri.parse("package:" + appInfo.getPackageName()));
startActivity(intent);
}
}
l 监视应用的卸载
receiver = new UninstallReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");// package:com.atguigu.ms
this.registerReceiver(receiver, filter);
private class UninstallReceiverextends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String dataString =intent.getDataString(); //package:com.atguigu.ms
if(dataString!=null) {
AppInfo appInfo =new AppInfo();
appInfo.packageName = dataString.substring(dataString.indexOf(":")+1);
userInfos.remove(appInfo);
adapter.notifyDataSetChanged();
}
}
}
3.7. 功能七:程序锁(难)
1) 应用的列表显示, 加锁与解锁
l 关键技术: SqliteDatabase, ListView, BaseAdapter, TranslateAnimation
Service, ActivityManager
ContentProvider, ContentResolver,ContentObserver,
@Override
public void onItemClick(AdapterView<?> parent, View view,int position,long id) {
// 添加动画效果,动画结束后,就把锁的图片改变
TranslateAnimation animation =new TranslateAnimation(Animation.RELATIVE_TO_SELF,
0f,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f);
animation.setDuration(500);
view.startAnimation(animation);//向右水平快速移动后复原
AppInfo appInfo = appInfoList.get(position);
ViewHolder viewHolder = (ViewHolder) view.getTag();
boolean lock = (Boolean) viewHolder.lockIV.getTag();
if (lock) {
viewHolder.lockIV.setImageResource(R.drawable.unlock);
viewHolder.lockIV.setTag(false);
lockedApps.remove(appInfo.getPackageName());
Uri uri = Uri.parse("content://com.atguigu.security.provider.applockprovider/delete");
getContentResolver().delete(uri,null,new String[] { appInfo.getPackageName() });
} else {
viewHolder.lockIV.setImageResource(R.drawable.lock);
viewHolder.lockIV.setTag(true);
lockedApps.add(appInfo.getPackageName());
ContentValues values = new ContentValues();
values.put("packagename", appInfo.getPackageName());
Uri uri = Uri.parse("content://com.atguigu.security.provider.applockprovider/insert");
getContentResolver().insert(uri, values);
}
}
2) 监听锁定应用的启动
l 关键技术: 流程分析ContentProvider, ContentResolver, ContentObsver, Service, Thread,ActivityManager
l AppLockMonitorService-----启动监听线程(一直运行)
n 获取最新启动的应用信息
n 判断它是否需要锁定
n 如果是, 启动解锁界面
n 适当的睡眠
/**
* 开启一直运行的子线程监听
*/
private void startMonitor() {
new Thread(new Runnable() {
@Override
public void run() {
while (flag) {
// 1. 得到当前最近启动的应用packageName
String packageName = getCurrentPackageName();
Log.e("TAG","start pn=" + packageName);
try {
// 2. 判断是否需要锁定此应用
// 判断是否在临时不锁定的列表中,如果是不用显示解锁界面
// 如果在,启动一个锁屏界面(LockScreenActivity)
if (!tempUnlockNames.contains(packageName) &&
appLockNames.contains(packageName)) {
startLockScreenActivity(packageName);
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
private StringgetTopPackageName() {
return am.getRunningTasks(1).get(0).topActivity.getPackageName();
}
private void startLockScreenActivity(String packageName) {
Intent intent = new Intent(this, LockScreenActivity.class);
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//必须是此flag
intent.putExtra("packagename", packageName);
startActivity(intent);
}
l AppLockProvider-----为应用锁表数据编写ContentProvider
@Override
public Uriinsert(Uri uri, ContentValues values) {
int match =matcher.match(uri);
if (match ==1) {
String packageName = values.getAsString("packagename");
appLockDao.add(packageName);
//通知所有监听在此URI上的obsever
getContext().getContentResolver().notifyChange(URI,null);
} else {
throw new RuntimeException("格式不正确");
}
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int match =matcher.match(uri);
if (match ==1) {
appLockDao.delete(selectionArgs[0]);
//通知所有监听在此URI上的obsever
getContext().getContentResolver().notifyChange(URI, null);
} else {
throw new RuntimeException("格式不正确");
}
return 0;
}
l AppLockActiviy-----通过ContentResolver向应用锁表中添加或删除记录
//删除数据
Uri uri = Uri.parse("content://com.atguigu.my_ms.provider.applockprovider/applock");
getContentResolver().delete(uri, null,new String[] { appInfo.getPackageName() });
//添加记录
ContentValues values = new ContentValues();
values.put("packagename", appInfo.getPackageName());
Uri uri = Uri.parse("content://com.atguigu.my_ms.provider.applockprovider/applock");
getContentResolver().insert(uri, values);
l AppLockMonitorService-----监听应用锁表数据的变化
Uri uri = Uri.parse("content://com.atguigu.security.provider.applockprovider");
getContentResolver().registerContentObserver(uri, true, observer);
protected ContentObserverobserver =new ContentObserver(new Handler()) {
public void onChange(boolean selfChange) {
appLockNames = appLockDao.getAll();
Log.e("TAG","ContentObserver onChange()..");
}
};
l LockScreenActivity ----密码验证与解锁通知
Intent service = new Intent(this, AppLockMonitorService.class);
service.putExtra("packageName",packageName);
startService(service );
3.8. 功能八: 进程管理
1) 显示系统和第三方应用进程列表
l 关键技术: PackageManager, ActivityManager,AsyncTask, ListView,
BaseAdapter, List<ProcessInfo>
l 得到所有正在运行的进程的信息
public static void getAllProcessInfos(Context context,
List<ProcessInfo> systemProcessInfos, List<ProcessInfo> userProcessInfos) {
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
PackageManager pm = context.getPackageManager();
for (RunningAppProcessInfo processInfo : processInfos) {
ProcessInfo info = new ProcessInfo();
// 包名
String packageName = processInfo.processName;
info.setPackageName(packageName);
// 应用占用的内存 bit/byte
MemoryInfo memoryInfo = am
.getProcessMemoryInfo(new int[] { processInfo.pid })[0];
long memInfoSize = memoryInfo.getTotalPrivateDirty() * 1024;
info.setMemInfoSize(memInfoSize);
try {
// 图标
Drawable icon = pm.getPackageInfo(packageName, 0).applicationInfo.loadIcon(pm);
info.setIcon(icon);
// 应用名称
String name = pm.getPackageInfo(packageName, 0).applicationInfo
.loadLabel(pm).toString();
info.setAppName(name);
// 是否是系统应用进程
int flag = pm.getPackageInfo(packageName, 0).applicationInfo.flags;
if ((flag & ApplicationInfo.FLAG_SYSTEM) == 0) {//用户进程
info.setSystem(false);
} else {//系统进程
info.setSystem(true);
}
} catch (NameNotFoundException e) {//根据包名得到不到PackageInfo
e.printStackTrace();
info.setIcon(context.getResources().getDrawable(R.drawable.icon));
info.setAppName(packageName);
info.setSystem(true);
}
//不同类型的Info保存到不同的集合中
if(info.isSystem()) {
systemProcessInfos.add(info);
} else {
userProcessInfos.add(info);
}
}
}
l 得到手机中可用内存大小
public static long getAvailMem(Context context) {
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(outInfo);
return outInfo.availMem;
}
l 得到手机中总内存大小
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static long getTotalMem(Context context) {
long totalMem = 0;
int sysVersion = VERSION.SDK_INT;//得到当前系统的版本号
// 下面的方式只能在JELLY_BEAN(16)及以上版本才有用
if (sysVersion >= Build.VERSION_CODES.JELLY_BEAN) {
ActivityManager am = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);
totalMem = memoryInfo.totalMem;
} else {
try {//在版本小于16时,读取/proc/meminfo文件的第一行来获取总大小
File file = new File("/proc/meminfo");
FileInputStream fis = new FileInputStream(file);
BufferedReader reader = new BufferedReader(
new InputStreamReader(fis));
String result = reader.readLine();// MemTotal: 510484 kB
result = result.substring(result.indexOf(":") + 1,
result.indexOf("k")).trim();// 510484
reader.close();
totalMem = Integer.parseInt(result) * 1024;
} catch (Exception e) {
e.printStackTrace();
}
}
return totalMem;
}
l 文件大小格式化
//返回格式化后的大小:1.5GB,12MB,16.66KB, 0.00B 101010101byte
String Formatter.formatFileSize(context,byteSize);
2) 进程的清理
l 关键技术: ActivityManager
//根据包名杀死对应的进程
activityManager.killBackgroundProcesses(packageName)
<!-- 杀死后台进程 -->
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
3) 清理进程的widget
l 关键技术: AppWidgetProvider,<appwidget-provider>, RemoteView, Service,
PendingIntent, Handler
l AppWidgetProvider : 接收widget工具添加到桌面或从桌面移除等广播的receiver
public class ProcessWidgetextends AppWidgetProvider {
private Intentintent;
@Override
public void onEnabled(Context context) {//长按产生一个widget
intent = new Intent(context,UpdateWidgetService.class);
context.startService(intent);
}
@Override
public void onDeleted(Context context,int[] appWidgetIds) {//移除桌面的widget
if (intent !=null) {
context.stopService(intent);
intent = null;
}
}
}
l 注册receiver
<receiver android:name=".receiver.ProcessWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/process_widget" />
</receiver>
l 应用小工具的配置文件
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/process_widget"
android:minHeight="72dp"
android:minWidth="294dp">
</appwidget-provider>
l UpdatewidgetService 用于更新widget的服务
@Override
public void onCreate() {
super.onCreate();/*
//创建用于更新widget的管理器
manager = AppWidgetManager.getInstance(this);
//包含widget视图布局的远程视图对象
remoteViews = new RemoteViews(getPackageName(),
R.layout.process_widget_view);
//为widget中的button添加点击监听
Intent intent = new Intent(this,UpdatewidgetService.class);
intent.putExtra("clear",true);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent,
0);
remoteViews.setOnClickPendingIntent(R.id.btn_widget_clear,
pendingIntent);
//准备用于更新widget的组件对象
componentName = new ComponentName(this, ProcessWidget.class);
//更新widget
updateWidget();
//发送延迟消息,使用widget能每隔2秒更新一次
handler.sendEmptyMessageDelayed(1, 2000);
}
private void updateWidget() {
MSUtils.log("updateWidget()...");
processCount = MSUtils.getProcessCount(this);//进程大小
remoteViews.setTextViewText(R.id.tv_widget_process_count,"进程数为 "
+ processCount);
availMem = MSUtils.getAvailMem(this);//可用内存大小
remoteViews.setTextViewText(R.id.tv_widget_process_memory,"可用内存为 "
+ Formatter.formatFileSize(this,availMem));
manager.updateAppWidget(componentName,remoteViews);
}
l 使用BroadcastReceiver使应用更省电
private ScreeenReceiverreceiver;
private void registScreenReceiver() {
receiver = new ScreeenReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON); //开屏
filter.addAction(Intent.ACTION_SCREEN_OFF);//锁屏
registerReceiver(receiver, filter );
}
private class ScreeenReceiverextends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(Intent.ACTION_SCREEN_ON.equals(action)) {
Log.e("TAG","开屏....");
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessage(1);
} else {
Log.e("TAG","锁屏....");
handler.removeCallbacksAndMessages(null);
}
}
}
l 为应用生成桌面快捷方式
private void createShortcut() {
//得到是否生成快捷图标的标识
boolean shortcut = SpUtils.getInstance(this)
.getBoolean(SpUtils.SHORT_CUT,false);
if(!shortcut) {
Intent intent = new Intent("com.android.launcher.action.INSTALL_SHORTCUT");
//图标
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON,
BitmapFactory.decodeResource(getResources(), R.drawable.logo));
//名称
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME,"手机卫士");
//点击快捷键,启动哪个界面
Intent callIntent = new Intent("com.atguigu.my_ms.action.Main");
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, callIntent);
//发送广播,桌面(laucher)应用有Receiver接收并生成桌面图标
sendBroadcast(intent);
//保存已生成快捷图标的标识
SpUtils.getInstance(this).put(SpUtils.SHORT_CUT,true);
}
}
<!-- 生成应用快捷方式 -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
3.9. 功能九: 缓存清理
1) 得到应用的安装包,数据和缓存大小
关键技术: PackageManager, AIDL, 反射调用@hide的方法
l 得到应用的cache大小
//得到安装的所有应用
List<ApplicationInfo> applications = pm.getInstalledApplications(0);
//得到某个应用的缓存数据
Method method = PackageManager.class.getMethod("getPackageSizeInfo",
String.class, IPackageStatsObserver.class);
method.invoke(pm, packageName,new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats,boolean succeeded) {
long cacheSize = pStats.cacheSize);
}
});
l 权限: android.permission.GET_PACKAGE_SIZE
2) 清理缓存
l 通过反射+AIDL调用PackageManager的hide方法实现
l 只能是系统应用使用,第三方应用不能使用
Method method = PackageManager.class.getMethod("deleteApplicationCacheFiles",
String.class, IPackageDataObserver.class);
method.invoke(pm, info.packageName,new IPackageDataObserver.Stub(){
@Override
public void onRemoveCompleted(String packageName,
boolean succeeded)throws RemoteException {
}
}});
l 调用系统设置应用清理缓存
Intent intent = new Intent();
intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
intent.setData(Uri.parse("package:" +cacheInfos.get(position).getPackageName()));
startActivity(intent);
3.10. 功能十: 流量管理
1) 应用流量列表显示:
l 关键技术: SlidingDrawer, PackageManager, ApplicationInfo, TrafficStats
/**
* 得到应用的所有流量信息
*/
public static List<TrafficInfo> getAllTrafficInfos(Context context) {
List<TrafficInfo> list = new ArrayList<TrafficInfo>();
PackageManager pm = context.getPackageManager();
//安装的所有应用(包含没有主界面的)
List<ApplicationInfo> infos = pm.getInstalledApplications(0);
for(ApplicationInfo info : infos) {
TrafficInfo trafficInfo = new TrafficInfo();
//appName
String appName = info.loadLabel(pm).toString();
trafficInfo.setName(appName);
//icon
Drawable icon = info.loadIcon(pm);
trafficInfo.setIcon(icon);
int uid = info.uid; //userID
//inSize 下载流量
long inSize =TrafficStats.getUidRxBytes(uid); //receive
trafficInfo.setInSize(inSize);
//outSize 上传流量
long outSize =TrafficStats.getUidTxBytes(uid);
trafficInfo.setOutSize(outSize);
list.add(trafficInfo);
}
return list; 7
}
//根据应用的id得到其下载流量和上传流量
long uidRxBytes =TrafficStats.getUidRxBytes(uid);
long uidTxBytes = TrafficStats.getUidTxBytes(uid);
//得到手机总的下载流量和上传流量(2g/3g/wifi)
long totalRxBytes = TrafficStats.getTotalRxBytes(); //receive
long totalTxBytes = TrafficStats.getTotalTxBytes();
//得到手机的下载流量和上传流量(2g/3g)
long mobileRxBytes = TrafficStats.getMobileRxBytes();
long mobileTxBytes = TrafficStats.getMobileTxBytes();
3.11. 功能十一: 高级工具
1) 常用手机号查询
l 关键技术: ExpandableListView, BaseExpandableListAdapter, SQLiteDatabase,打电话Intent
<ExpandableListView
android:id="@+id/elv_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:groupIndicator="@drawable/expendable_group_selector"/>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 展开时显示 -->
<item android:drawable="@drawable/group_list"
android:state_expanded="true"/>
<!-- 不展开显示 -->
<item android:drawable="@drawable/group_list_pressed"/>
</selector>
2) 备份手机中的所有短信数据
l 技术: ContentResolver, 短信Uri, Cursor, XmlSerializer, XmlPullParser Gson
l 保存为xml文件格式
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<smses>
<sms>
<address>445</address>
<date>1423316911894</date>
<type>2</type>
<body>R56yy</body>
</sms>
<sms>
<address>234</address>
<date>1423316902099</date>
<type>2</type>
<body>Wrtt</body>
</sms>
</smses>
l 读取短信数据(查看mmssms.db数据库)
ContentResolver resolver = context.getContentResolver();
Uri uri = Uri.parse("content://sms");
String[] projection = {"address","date","type","body"};
Cursor cursor = resolver.query(uri , projection , null,null,null);
l XmlSerializer的使用样例
// 创建用于生成xml数据的对象
XmlSerializer serializer =Xml.newSerializer();
try {
//设置文件输出流
OutputStream os = new FileOutputStream(smsFile);
serializer.setOutput(os,"utf-8");
//开始写文档
serializer.startDocument("utf-8",true);
//写<smses>
serializer.startTag(null,"smses");
while (cursor.moveToNext()) {
//写<sms>
serializer.startTag(null,"sms");
String address = cursor.getString(0);
String date = cursor.getString(1);
String type = cursor.getString(2);
String body = cursor.getString(3);
//写<address>abc</address>
createTag(serializer, "address", address);
createTag(serializer, "date", date);
createTag(serializer, "type", type);
createTag(serializer, "body", body);
//写</sms>
serializer.endTag(null,"sms");
}
//写</smses>
serializer.endTag(null,"smses");
//结束写文档
serializer.endDocument();
} catch (Exception e) {
e.printStackTrace();
}
private void createTag(XmlSerializer serializer, String tagName,
String value) throws Exception {
serializer.startTag(null, tagName);
//写文本标签体
serializer.text(value);
serializer.endTag(null, tagName);
}
3) 还原备份的短信数据
l 技术: ContentResolver, 短信Uri, XmlPullParser, ProgressDialog
l XmlPullParser使用样例
try {
//创建pull解析器
XmlPullParser pullParser = Xml.newPullParser();
//设置xml文件流
pullParser.setInput(fis ,"utf-8");
//读取第一个事件类型
int eventType = pullParser.getEventType();
//不断循环读取,直到文档最后
while(eventType!=XmlPullParser.END_DOCUMENT) {
String tagName = null;
switch (eventType) {
case XmlPullParser.START_TAG://开始标签
//得到标签名
tagName = pullParser.getName();
if("address".equals(tagName)) {
//保存<address>的文本标签体内容
values.put("address", pullParser.nextText());
} else if("date".equals(tagName)) {
values.put("date", pullParser.nextText());
} else if("type".equals(tagName)) {
values.put("type", pullParser.nextText());
} else if("body".equals(tagName)) {
values.put("body", pullParser.nextText());
}
break;
case XmlPullParser.END_TAG://结束标签
tagName = pullParser.getName();
if("sms".equals(tagName)) {
//如果读到</sms>,就将数据保存到短信表中
resolver.insert(uri, values);
}
break;
default:
break;
}
//解析下一个单元数据,并保存其事件类型
eventType = pullParser.next();
}
} catch (Exception e) {
e.printStackTrace();
}
3.12. 功能十二: 手机杀毒
1) 计算机病毒与手机病毒
计算机病毒
对电脑有破坏性的程序
蠕虫病毒 熊猫烧香
利用网络进行复制和传播,传染途径是通过网络和电子邮件
目地: 显摆技术,验证技术
木马病毒
指通过特定的程序(木马程序)来控制另一台计算机
目的:利益,账号:银行账号,游戏账号。
手机病毒
一种具有传染性、破坏性的手机程序. 其可利用发送短信、彩信,电子邮件,浏览网站,下载铃声,蓝牙等方式进行传播,会导致用户手机死机、关机、个人资料被删、向外发送垃圾邮件泄露个人信息、自动拨打电话、发短(彩)信等进行恶意扣费,甚至会损毁SIM卡、芯片等硬件,导致使用者无法正常使用手机
目地:利益
2) 杀毒软件
l 作用: 用来定位出带病毒的软件应用,并提示用户将它们给删除。
l 基本原理:
n 收集病毒保存起来形成强大的病毒库, 如果安装的应用在病毒库中有记录, 杀毒软件就会提示其是病毒(手机杀毒)
n 它会监视软件的行为来判断其是否可能是病毒
1.监听是否开机启动
2.监听软件是否有打开摄像头
3.是否发邮件
4.是否很特别占用资源
3) 得到安装应用的信息
//得到所有包(包括已删除但安装目录还存在的)
List<PackageInfo> packages = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
for(PackageInfo packInfo : packages){
//应用名称
info.name = packInfo.applicationInfo.loadLabel(pm).toString();
//应用包名
info.packacgeName = packInfo.packageName;
//应用签名
String signature = packInfo.signatures[0].toCharsString();
}
4) 判断是否是病毒
//对应用签名进行md5加密
signature = MSUtils.md5(signature);
//查询病毒库表
String desc = AntiVirusQueryDao.getDesc(getApplicationContext(), signature);
if(desc==null) {//不是
info.isVirus =false;
} else {//是
info.isVirus =true;
}
5) 扫描杀毒时的旋转动画
RotateAnimation ra =new RotateAnimation(0, 360,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
ra.setDuration(1000);
ra.setRepeatCount(RotateAnimation.INFINITE);//播放无限次
iv_antivirus_scanning.startAnimation(ra);
6) 自定义水平进度条
定义包含进度条图片的drawable文件
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@android:id/background"
android:drawable="@drawable/security_progress_bg">
</item>
<item android:id="@android:id/secondaryProgress"
android:drawable="@drawable/security_progress_bg">
</item>
<item android:id="@android:id/progress"
android:drawable="@drawable/security_progress">
</item>
</layer-list>
- android项目-手机卫士
- 手机卫士项目
- 手机卫士项目(第一天)
- 手机卫士项目(第二天)
- 手机卫士项目第三天
- Android项目实战--手机卫士
- Android项目实战--手机卫士--结束
- Android-手机卫士项目前凑
- 高仿精仿金山手机卫士源码项目完整版
- Android基础项目手机卫士总结
- 手机卫士
- Android项目实战--手机卫士08--获取手机联系人
- 手机卫士项目——手机防盗GPS追踪技术
- Android项目实战--手机卫士01--启动界面
- Android项目实战--手机卫士02--与服务器交互
- Android项目实战--手机卫士04--自定义图片
- Android项目实战--手机卫士07--设置向导
- Android项目实战--手机卫士19--短信的恢复
- 【ImageLoader】Universal-Image-Loader学习系列(二),ImageLoaderConfiguration的使用
- Java String 字符串
- 菜单栏的创建
- 复数类
- vmware ubuntu 新增硬盘方法
- 手机卫士项目
- 关于Chrome即谷歌调试工具的使用
- 互联网协议入门(一) 作者:阮一峰
- TextMate 编辑 HTML 使用的快捷键记录
- 一篇介绍PHP集成服务器Wamp安装,使用的文章
- 日期类
- Error:(1, 1) java: 非法字符: \65279 最有效解决方法
- InteliJ idea创建maven项目时一直处于loading状态
- unity-zip压缩与解压