MSG 08-02 添加黑名单布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.EditBlackNumberActivity">
<TextView
style="@style/TitleStyle"
android:text="添加黑名单" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电话号码" />
<EditText
android:id="@+id/et_search_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/edittext_input_selector"
android:hint="请输入要查询的内容"
android:inputType="number" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拦截类型" />
<RadioGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="电话" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="短信" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="全部" />
</RadioGroup>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="bottom"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="保存" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="取消" />
</LinearLayout>
</LinearLayout>
MSG 08-03 ButterKnife的使用
https://github.com/JakeWharton/butterknife
package cn.nubia.mobilesecurityguard.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.nubia.mobilesecurityguard.R;
public class EditBlackNumberActivity extends AppCompatActivity {
@BindView(R.id.et_search_content)
EditText etSearchContent;
@BindView(R.id.rb_phone)
RadioButton rbPhone;
@BindView(R.id.rb_sms)
RadioButton rbSms;
@BindView(R.id.rb_all)
RadioButton rbAll;
@BindView(R.id.bt_save)
Button btSave;
@BindView(R.id.bt_cancle)
Button btCancle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit_black_number);
ButterKnife.bind(this);
}
@OnClick({R.id.et_search_content, R.id.rb_phone, R.id.rb_sms, R.id.rb_all, R.id.bt_save, R.id.bt_cancle})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.et_search_content:
Log.e("sunyang","点击搜索");
break;
case R.id.rb_phone:
break;
case R.id.rb_sms:
break;
case R.id.rb_all:
break;
case R.id.bt_save:
break;
case R.id.bt_cancle:
break;
}
}
}
MSG 08-04 保存黑名单号码
private void savePhoneNumber() {
String number = etSearchContent.getText().toString().trim();
if (TextUtils.isEmpty(number)) {
Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show();
}
int radioButtonId = rgAll.getCheckedRadioButtonId();
int mode = 0;
switch (radioButtonId) {
case R.id.rb_phone:
mode = 0;
break;
case R.id.rb_sms:
mode = 1;
break;
case R.id.rb_all:
mode = 2;
break;
}
//保存到数据库
boolean isSuccess = dao.insert(number, mode);
if (isSuccess) {
Log.e("sunyang","已保存");
finish();
} else {
}
}
最新数据在数据库最下面
MSG 08-05 数据库逆序排列
select * from blacknumber order by _id desc limit 0,20
//逆序查询
public ArrayList<BlackNumberInfo> queryPartDescNumberAndMode(int index) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select number, mode from blacknumber order by _id desc limit ?,20", new String[]{index + ""});
ArrayList<BlackNumberInfo> list = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
int mode = cursor.getInt(1);
info.number = number;
info.mode = mode;
list.add(info);
}
cursor.close();
}
db.close();
return list;
}
MSG 08-06 保存黑名单并刷新页面(不重新加载数据库)
private void addBlackNumber() {
Intent intent = new Intent(BlackNumberActivity.this, EditBlackNumberActivity.class);
startActivityForResult(intent, 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 0:
if (resultCode ==RESULT_OK) {
String number = data.getStringExtra("number");
int mode = data.getIntExtra("mode", 0);
//封装一个新对象
BlackNumberInfo info = new BlackNumberInfo();
info.number = number;
info.mode = mode;
list.add(info);
blackNumberAdapter.notifyDataSetChanged();
}
break;
}
}
private void savePhoneNumber() {
String number = etSearchContent.getText().toString().trim();
if (TextUtils.isEmpty(number)) {
Toast.makeText(this, "不能为空", Toast.LENGTH_SHORT).show();
}
int radioButtonId = rgAll.getCheckedRadioButtonId();
int mode = 0;
switch (radioButtonId) {
case R.id.rb_phone:
mode = 0;
break;
case R.id.rb_sms:
mode = 1;
break;
case R.id.rb_all:
mode = 2;
break;
}
//保存到数据库
boolean isSuccess = dao.insert(number, mode);
if (isSuccess) {
Log.e("sunyang","已保存");
Intent intent = new Intent();
intent.putExtra("number", number);
intent.putExtra("mode", mode);
setResult(RESULT_OK,intent);
finish();
} else {
}
}
此时集合中数据在最后一条,需要将数据条件到第一条数据
list.add(0,info);
MSG 08-07 区分添加还是更新&更新黑名单数据回显
lvAddNumber.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(getApplicationContext(), EditBlackNumberActivity.class);
intent.putExtra("isUpdate", true);
BlackNumberInfo info = list.get(position);
intent.putExtra("number",info.number);
intent.putExtra("mode", info.mode);
startActivity(intent);
}
});
//区分添加还是更新
boolean isUpdate = getIntent().getBooleanExtra("isUpdate", false);
if (isUpdate) {
//修改标题栏
tvTitle.setText("更新黑名单");
//修改按钮文字
btSave.setText("更新");
//禁用文本框
etSearchContent.setEnabled(false);
//回显电话号码和拦截类型
String number = getIntent().getStringExtra("number");
etSearchContent.setText(number);
int mode = getIntent().getIntExtra("mode", 0);
switch (mode) {
case 0:
rgAll.check(R.id.rb_phone);
break;
case 1:
rgAll.check(R.id.rb_sms);
break;
case 2:
rgAll.check(R.id.rb_all);
break;
}
}
MSG 08-08 更新黑名单并刷新页面(不重新加载数据库)
if (resultCode == RESULT_OK) {
String number = data.getStringExtra("number");
int mode = data.getIntExtra("mode", 0);
//找到要更新的对象
updateInfo.mode = mode;
//刷新listview
blackNumberAdapter.notifyDataSetChanged();
}
MSG 08-09 添加和更新黑名单流程小结
MSG 08-10 删除黑名单
holder.ivDelete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//删除黑名单
//1、从数据库删除
dao.delete(info.number);
//2、从集合删除
list.remove(info);
//3、刷新listview
notifyDataSetChanged();
}
});
MSG 08-11 显示数据为空的布局
blackNumberAdapter = new BlackNumberAdapter(BlackNumberActivity.this, BlackNumberActivity.this.list);
lvAddNumber.setAdapter(blackNumberAdapter);
lvAddNumber.setEmptyView(ivEmpty);
MSG 08-12 添加骚扰拦截开关&创建服务
private void startSrljService() {
if (ServiceStatusUtils.isServiceRunning(getApplicationContext(),BlackNumberService.class)) {
stopService(new Intent(getApplicationContext(),BlackNumberService.class));
} else {
startService(new Intent(getApplicationContext(),BlackNumberService.class));
}
}
MSG 08-13 黑名单拦截短信
package cn.nubia.mobilesecurityguard.service;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.telephony.SmsManager;
public class BlackNumberService extends Service {
private SmsReceiver smsReceiver;
public BlackNumberService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
//拦截短信
//注册广播
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
filter.setPriority(Integer.MAX_VALUE);
smsReceiver = new SmsReceiver();
registerReceiver(smsReceiver, filter);
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(smsReceiver);
smsReceiver = null;
}
class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Object[] objs = (Object[]) intent.getExtras().get("pdus");
for (Object obj : objs) {
}
}
}
}
MSG 08-14 挂断电话&aidl文件配置
<uses-permission android:name="android.permission.CALL_PHONE"/>
private void endCall() {
//反射获取系统服务的getService方法对象
try {
Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
//执行这个方法得到一个IBinder对象
try {
IBinder binder = (IBinder) method.invoke(null, TELEPHONY_SERVICE);
//转换为具体的服务类
ITelephony telephony = ITelephony.Stub.asInterface(binder);
telephony.endCall();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
MSG 08-15 移除通话记录&内容观察者
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
private void deleteCallLog(String number) {
getContentResolver().delete(Uri.parse("content://call_log/calls"),"number=?",new String[]{number});
//getContentResolver().delete(CallLog.Calls.CONTENT_URI,"number=?",new String[]{number});
}
MSG 08-16 软件管理布局&自定义水平进度条
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:showIn="@layout/activity_app_manager">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="内部存储:" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/pb_progress"
style="?android:attr/progressBarStyleHorizontal"
android:progressDrawable="@drawable/custom_progress"
android:layout_width="match_parent"
android:progress="50"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0M已用" />
<TextView
android:id="@+id/tv_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="0M可用" />
</RelativeLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/background"
android:drawable="@drawable/progress_bg">
</item>
<item android:id="@+id/secondaryProgress">
<scale
android:drawable="@drawable/progress_secondary"
android:scaleWidth="100%" />
</item>
<item android:id="@+id/progress">
<scale
android:drawable="@drawable/progress_progress"
android:scaleWidth="100%" />
</item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#E69191"></solid>
</shape>
MSG 08-17 自定义进度条组合控件
package cn.nubia.mobilesecurityguard.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import cn.nubia.mobilesecurityguard.R;
/**
* 自定义进度条的组合控件
*/
public class ProgressView extends LinearLayout {
@BindView(R.id.tv_title)
TextView tvTitle;
@BindView(R.id.pb_progress)
ProgressBar pbProgress;
@BindView(R.id.tv_left)
TextView tvLeft;
@BindView(R.id.tv_right)
TextView tvRight;
public ProgressView(Context context) {
this(context, null);
}
public ProgressView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
View view = View.inflate(context, R.layout.app_manager_progress, this);
ButterKnife.bind(view);
}
public void setTitle(String title) {
tvTitle.setText(title);
}
public void setleftText(String title) {
tvLeft.setText(title);
}
public void setRightText(String title) {
tvRight.setText(title);
}
public void setProgress(int progress) {
pbProgress.setProgress(progress);
}
}
<cn.nubia.mobilesecurityguard.view.ProgressView
android:id="@+id/left_pb"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</cn.nubia.mobilesecurityguard.view.ProgressView>
<cn.nubia.mobilesecurityguard.view.ProgressView
android:id="@+id/right_pb"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</cn.nubia.mobilesecurityguard.view.ProgressView>
MSG 08-18 软件管理初始化控件信息
private void initSpaceInfo() {
//内部存储
long totalSpace = Environment.getDataDirectory().getTotalSpace();
long freeSpace = Environment.getDataDirectory().getFreeSpace();
long usedSpace = totalSpace - freeSpace;
//Formatter将字节转换成带单位的字符串
String use = Formatter.formatFileSize(this, usedSpace);
inPb.setleftText(use);
String free = Formatter.formatFileSize(this, freeSpace);
inPb.setRightText(free);
int progress = (int) (freeSpace * 100 / totalSpace);
inPb.setProgress(progress);
//外部存储
long sdCardTotalSpace = Environment.getExternalStorageDirectory().getTotalSpace();
long sdCardFreeSpace = Environment.getExternalStorageDirectory().getFreeSpace();
long sdCardUsedSpace = sdCardTotalSpace - sdCardFreeSpace;
//Formatter将字节转换成带单位的字符串
String useSdCard = Formatter.formatFileSize(this, sdCardUsedSpace);
outPb.setleftText(useSdCard);
String freeSdCard = Formatter.formatFileSize(this, sdCardFreeSpace);
outPb.setRightText(freeSdCard);
int progressSdCard = (int) (sdCardFreeSpace * 100 / sdCardTotalSpace);
outPb.setProgress(progressSdCard);
}
MSG 08-19 获取所有已安装的app
package cn.nubia.mobilesecurityguard.bean;
import android.graphics.drawable.Drawable;
public class AppManagerInfo {
public String packageName;
public String name;
public Drawable icon;
public long size;
}
package cn.nubia.mobilesecurityguard.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
/**
* 提供应用信息的工具类
*/
public class AppInfoProvider {
//获取已安装的app信息
public static ArrayList<AppManagerInfo> getInstalledApps(Context context) {
PackageManager pm = context.getPackageManager();
List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
ArrayList<AppManagerInfo> list = new ArrayList<>();
for (PackageInfo packageInfo : installedPackages) {
AppManagerInfo info = new AppManagerInfo();
String packageName = packageInfo.packageName;
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
String name = applicationInfo.loadLabel(pm).toString();
Drawable icon = applicationInfo.loadIcon(pm);
//安装路径
String sourceDir = applicationInfo.sourceDir;
File file = new File(sourceDir);
//文件大小
long size = file.length();
info.packageName = packageName;
info.icon = icon;
info.name = name;
info.size = size;
list.add(info);
}
return list;
}
}
package cn.nubia.mobilesecurityguard.adapter;
import android.content.Context;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
public class AppManagerAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<AppManagerInfo> mList;
public AppManagerAdapter(Context context, ArrayList list) {
mContext = context;
mList = list;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public AppManagerInfo getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.item_app_manager, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.tvLocation = convertView.findViewById(R.id.tv_location);
holder.tvSize = convertView.findViewById(R.id.tv_size);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
AppManagerInfo info = getItem(position);
holder.tvName.setText(info.name);
holder.ivIcon.setImageDrawable(info.icon);
String size = Formatter.formatFileSize(mContext, info.size);
holder.tvSize.setText(size);
return convertView;
}
static class ViewHolder {
public ImageView ivIcon;
public TextView tvName;
public TextView tvLocation;
public TextView tvSize;
}
}
MSG 08-20 展示应用列表
MSG 08-21 软件安装位置&软件安装流程
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.nubia.mobilesecurityguard"
android:installLocation="preferExternal">
三方软件:data/app
系统软件:system/app
MSG 08-22 判断软件安装位置
package cn.nubia.mobilesecurityguard.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
/**
* 提供应用信息的工具类
*/
public class AppInfoProvider {
//获取已安装的app信息
public static ArrayList<AppManagerInfo> getInstalledApps(Context context) {
PackageManager pm = context.getPackageManager();
List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
ArrayList<AppManagerInfo> list = new ArrayList<>();
for (PackageInfo packageInfo : installedPackages) {
AppManagerInfo info = new AppManagerInfo();
String packageName = packageInfo.packageName;
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
String name = applicationInfo.loadLabel(pm).toString();
Drawable icon = applicationInfo.loadIcon(pm);
//安装路径
String sourceDir = applicationInfo.sourceDir;
File file = new File(sourceDir);
//文件大小
long size = file.length();
info.packageName = packageName;
info.icon = icon;
info.name = name;
info.size = size;
//判断软件安装位置
int flags = applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) > 0) {
//当前app具备sdcard特性
info.isSDCard = true;
} else {
//当前app具备内存特性
info.isSDCard = false;
}
list.add(info);
}
return list;
}
}
package cn.nubia.mobilesecurityguard.adapter;
import android.content.Context;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
public class AppManagerAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<AppManagerInfo> mList;
public AppManagerAdapter(Context context, ArrayList list) {
mContext = context;
mList = list;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public AppManagerInfo getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.item_app_manager, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.tvLocation = convertView.findViewById(R.id.tv_location);
holder.tvSize = convertView.findViewById(R.id.tv_size);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
AppManagerInfo info = getItem(position);
holder.tvName.setText(info.name);
holder.ivIcon.setImageDrawable(info.icon);
String size = Formatter.formatFileSize(mContext, info.size);
holder.tvSize.setText(size);
boolean isSDCard = info.isSDCard;
if (isSDCard) {
holder.tvLocation.setText("sd卡");
} else {
holder.tvLocation.setText("手机内存");
}
return convertView;
}
static class ViewHolder {
public ImageView ivIcon;
public TextView tvName;
public TextView tvLocation;
public TextView tvSize;
}
}
MSG 09-02 区分展示用户应用和系统应用
package cn.nubia.mobilesecurityguard.utils;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
/**
* 提供应用信息的工具类
*/
public class AppInfoProvider {
//获取已安装的app信息
public static ArrayList<AppManagerInfo> getInstalledApps(Context context) {
PackageManager pm = context.getPackageManager();
List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
ArrayList<AppManagerInfo> list = new ArrayList<>();
for (PackageInfo packageInfo : installedPackages) {
AppManagerInfo info = new AppManagerInfo();
String packageName = packageInfo.packageName;
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
String name = applicationInfo.loadLabel(pm).toString();
Drawable icon = applicationInfo.loadIcon(pm);
//安装路径
String sourceDir = applicationInfo.sourceDir;
File file = new File(sourceDir);
//文件大小
long size = file.length();
info.packageName = packageName;
info.icon = icon;
info.name = name;
info.size = size;
//判断软件安装位置
int flags = applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) > 0) {
//当前app具备sdcard特性
info.isSDCard = true;
} else {
//当前app具备内存特性
info.isSDCard = false;
}
if ((flags & ApplicationInfo.FLAG_SYSTEM) > 0) {
//当前app具备系统app
info.isUserApp = false;
} else {
//当前app具备用户app
info.isUserApp = true;
}
list.add(info);
}
return list;
}
}
获取系统app和用户app
sysList = new ArrayList();
appList = new ArrayList();
for (int i = 0; i < installedApps.size(); i++) {
if (installedApps.get(i).isUserApp) {
appList.add(installedApps.get(i));
} else {
sysList.add(installedApps.get(i));
}
}
//清空集合
installedApps.clear();
installedApps.addAll(appList);
installedApps.addAll(sysList);
MSG 09-03 给集合添加标题栏对象
package cn.nubia.mobilesecurityguard.bean;
import android.graphics.drawable.Drawable;
public class AppManagerInfo {
public String packageName;
public String name;
public Drawable icon;
public long size;
public boolean isSDCard;
public boolean isUserApp;
public boolean isTitle; //标记当前是否是标题栏
public String title;
}
package cn.nubia.mobilesecurityguard.adapter;
import android.content.Context;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
public class AppManagerAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<AppManagerInfo> mList;
public AppManagerAdapter(Context context, ArrayList list) {
mContext = context;
mList = list;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public AppManagerInfo getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.item_app_manager, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.tvLocation = convertView.findViewById(R.id.tv_location);
holder.tvSize = convertView.findViewById(R.id.tv_size);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
AppManagerInfo info = getItem(position);
if(!info.isTitle) {
holder.tvName.setText(info.name);
holder.ivIcon.setImageDrawable(info.icon);
String size = Formatter.formatFileSize(mContext, info.size);
holder.tvSize.setText(size);
boolean isSDCard = info.isSDCard;
if (isSDCard) {
holder.tvLocation.setText("sd卡");
} else {
holder.tvLocation.setText("手机内存");
}
} else {
holder.tvName.setText(info.title);
}
return convertView;
}
static class ViewHolder {
public ImageView ivIcon;
public TextView tvName;
public TextView tvLocation;
public TextView tvSize;
}
}
package cn.nubia.mobilesecurityguard.activity;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.text.format.Formatter;
import android.widget.ListView;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.adapter.AppManagerAdapter;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
import cn.nubia.mobilesecurityguard.utils.AppInfoProvider;
import cn.nubia.mobilesecurityguard.view.ProgressView;
public class AppManagerActivity extends AppCompatActivity {
@BindView(R.id.left_pb)
ProgressView inPb;
@BindView(R.id.right_pb)
ProgressView outPb;
@BindView(R.id.lv_app_manager)
ListView lvAppManager;
private ArrayList sysList;
private ArrayList<AppManagerInfo> installedApps;
private ArrayList appList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_manager);
ButterKnife.bind(this);
inPb.setTitle("内部存储:");
outPb.setTitle("外部存储:");
initData();
}
private void initData() {
initSpaceInfo();
new Thread() {
@Override
public void run() {
//super.run();
installedApps = AppInfoProvider.getInstalledApps(AppManagerActivity.this);
sysList = new ArrayList();
appList = new ArrayList();
for (int i = 0; i < installedApps.size(); i++) {
if (installedApps.get(i).isUserApp) {
appList.add(installedApps.get(i));
} else {
sysList.add(installedApps.get(i));
}
}
//清空集合
installedApps.clear();
//添加用户应用的标题栏
AppManagerInfo infoApp = new AppManagerInfo();
infoApp.isTitle = true;
infoApp.title = "用户应用userApp";
installedApps.add(infoApp);
installedApps.addAll(appList);
//添加系统应用的标题栏
AppManagerInfo infoSys = new AppManagerInfo();
infoSys.isTitle = true;
infoSys.title = "系统应用sysApp";
installedApps.add(infoSys);
installedApps.addAll(sysList);
runOnUiThread(new Runnable() {
@Override
public void run() {
AppManagerAdapter adapter = new AppManagerAdapter(AppManagerActivity.this, installedApps);
lvAppManager.setAdapter(adapter);
}
});
}
}.start();
}
private void initSpaceInfo() {
//内部存储
long totalSpace = Environment.getDataDirectory().getTotalSpace();
long freeSpace = Environment.getDataDirectory().getFreeSpace();
long usedSpace = totalSpace - freeSpace;
//Formatter将字节转换成带单位的字符串
String use = Formatter.formatFileSize(this, usedSpace);
inPb.setleftText(use);
String free = Formatter.formatFileSize(this, freeSpace);
inPb.setRightText(free);
int progress = (int) (freeSpace * 100 / totalSpace);
inPb.setProgress(progress);
//外部存储
long sdCardTotalSpace = Environment.getExternalStorageDirectory().getTotalSpace();
long sdCardFreeSpace = Environment.getExternalStorageDirectory().getFreeSpace();
long sdCardUsedSpace = sdCardTotalSpace - sdCardFreeSpace;
//Formatter将字节转换成带单位的字符串
String useSdCard = Formatter.formatFileSize(this, sdCardUsedSpace);
outPb.setleftText(useSdCard);
String freeSdCard = Formatter.formatFileSize(this, sdCardFreeSpace);
outPb.setRightText(freeSdCard);
int progressSdCard = (int) (sdCardFreeSpace * 100 / sdCardTotalSpace);
outPb.setProgress(progressSdCard);
}
}
MSG 09-04 ListView展示多种布局类型
<?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:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:text="标题栏" />
</LinearLayout>
多种布局:
package cn.nubia.mobilesecurityguard.adapter;
import android.content.Context;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
public class AppManagerAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<AppManagerInfo> mList;
public AppManagerAdapter(Context context, ArrayList list) {
mContext = context;
mList = list;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public AppManagerInfo getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getViewTypeCount() {
return 2;
}
//布局的类型
@Override
public int getItemViewType(int position) {
AppManagerInfo info = getItem(position);
if (info.isTitle) {
return 0;
} else {
return 1;
}
//return super.getItemViewType(position);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int itemViewType = getItemViewType(position);
switch (itemViewType) {
case 0:
TitleHolder titleHolder = null;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.item_app_title, null);
titleHolder = new TitleHolder();
titleHolder.tvTitle = convertView.findViewById(R.id.tv_title);
//将holder保存和当前布局绑定
convertView.setTag(titleHolder);
} else {
titleHolder = (TitleHolder) convertView.getTag();
}
AppManagerInfo titleInfo = getItem(position);
if (position == 0) {
titleHolder.tvTitle.setText(titleInfo.title+"("+"10)");
} else {
titleHolder.tvTitle.setText(titleInfo.title+"("+"20)");
}
break;
case 1:
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(mContext, R.layout.item_app_manager, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.tvLocation = convertView.findViewById(R.id.tv_location);
holder.tvSize = convertView.findViewById(R.id.tv_size);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
AppManagerInfo info = getItem(position);
holder.tvName.setText(info.name);
holder.ivIcon.setImageDrawable(info.icon);
String size = Formatter.formatFileSize(mContext, info.size);
holder.tvSize.setText(size);
boolean isSDCard = info.isSDCard;
if (isSDCard) {
holder.tvLocation.setText("sd卡");
} else {
holder.tvLocation.setText("手机内存");
}
break;
}
return convertView;
}
static class ViewHolder {
public ImageView ivIcon;
public TextView tvName;
public TextView tvLocation;
public TextView tvSize;
}
static class TitleHolder {
public TextView tvTitle;
}
}
MSG 09-05 常驻浮窗开发
<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">
</ListView>
<TextView
android:id="@+id/tv_titles"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:background="#f00"
android:text="标题栏" />
</FrameLayout>
listview滑动监听:
lvAppManager.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (sysList != null && appList != null) {
if (firstVisibleItem >= appList.size() + 1) {
tvTitle.setText("系统应用sysApp(" + sysList.size() + ")");
} else {
tvTitle.setText("用户应用userApp(" + appList.size() + ")");
}
}
}
});
MSG 09-06 PopupWindow的使用
popupwindow布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:drawableTop="@drawable/ic_uninstall"
android:drawablePadding="3dp"
android:text="卸载" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:drawableTop="@drawable/ic_open"
android:drawablePadding="3dp"
android:text="打开" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:drawableTop="@drawable/ic_share"
android:drawablePadding="3dp"
android:text="分享" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:drawableTop="@drawable/ic_info"
android:drawablePadding="3dp"
android:text="信息" />
</LinearLayout>
加载布局:
lvAppManager.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
showPopupWindow(view);
}
});
private void showPopupWindow(View itemView) {
View view = View.inflate(this, R.layout.popup_appinfo, null);
//第4个参数:是否有焦点
PopupWindow mPopup = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
//设置一个背景,透明色,只有设置背景,点击返回键和窗口外侧,窗口才会消失
mPopup.setBackgroundDrawable(new ColorDrawable());
//mPopup.showAtLocation(itemView,0,0);
mPopup.showAsDropDown(itemView, 80, -itemView.getHeight());
}
MSG 09-07 PopupWindow添加动画
popup动画风格:
<!--归属地动画-->
<style name="PopupWindowAnimStyle">
<item name="android:windowEnterAnimation">@anim/popup_enter</item>
<item name="android:windowExitAnimation">@anim/popup__exit</item>
</style>
popup进入动画:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha
android:duration="300"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
<translate
android:duration="300"
android:fromXDelta="0"
android:interpolator="@android:anim/overshoot_interpolator"
android:toXDelta="200%" />
</set>
popup退出动画:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<alpha
android:duration="150"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<translate
android:duration="300"
android:fromXDelta="200%"
android:interpolator="@android:anim/anticipate_interpolator"
android:toXDelta="0" />
</set>
设置动画:
private void showPopupWindow(View itemView) {
View view = View.inflate(this, R.layout.popup_appinfo, null);
//第4个参数:是否有焦点
PopupWindow mPopup = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
//设置一个背景,透明色,只有设置背景,点击返回键和窗口外侧,窗口才会消失
mPopup.setBackgroundDrawable(new ColorDrawable());
//设置动画样式
mPopup.setAnimationStyle(R.style.PopupWindowAnimStyle);
//mPopup.showAtLocation(itemView,0,0);
mPopup.showAsDropDown(itemView, 80, -itemView.getHeight());
}
MSG 09-08 PopupWindow点击事件设置
private void showPopupWindow(View itemView) {
View view = View.inflate(this, R.layout.popup_appinfo, null);
TextView tvUninstall = view.findViewById(R.id.tv_uninstall);
TextView tvOpen = view.findViewById(R.id.tv_open);
TextView tvShare = view.findViewById(R.id.tv_share);
TextView tvInfo = view.findViewById(R.id.tv_info);
tvUninstall.setOnClickListener(this);
tvOpen.setOnClickListener(this);
tvShare.setOnClickListener(this);
tvInfo.setOnClickListener(this);
//第4个参数:是否有焦点
mPopup = new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
//设置一个背景,透明色,只有设置背景,点击返回键和窗口外侧,窗口才会消失
mPopup.setBackgroundDrawable(new ColorDrawable());
//设置动画样式
mPopup.setAnimationStyle(R.style.PopupWindowAnimStyle);
//mPopup.showAtLocation(itemView,0,0);
mPopup.showAsDropDown(itemView, 200, -itemView.getHeight());
}
@Override
public void onClick(View v) {
mPopup.dismiss();
switch (v.getId()) {
case R.id.tv_uninstall:
Log.e("sunyang","卸载");
break;
case R.id.tv_open:
break;
case R.id.tv_share:
break;
case R.id.tv_info:
break;
}
}
MSG 09-09 卸载应用
String packageName = currInfo.packageName;
Uri parse = Uri.parse("package:" + packageName);
Intent intent = new Intent(Intent.ACTION_DELETE, parse);
startActivityForResult(intent,0);
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
initData();
}
MSG 09-10 打开应用
PackageManager packageManager = getPackageManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Intent launchIntentForPackage = packageManager.getLeanbackLaunchIntentForPackage(currInfo.packageName);
if (launchIntentForPackage != null) {
startActivity(launchIntentForPackage);
}
}
MSG 09-11 分享功能
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("text/plain");
share.putExtra(Intent.EXTRA_TEXT,"你好,我叫算。。");
startActivity(share);
MSG 09-12 跳到应用信息页
Intent info = new Intent();
info.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", currInfo.packageName, null);
info.setData(uri);
startActivity(info);
MSG 10-02 ExpandableListView的使用
<ExpandableListView
android:id="@+id/elv_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:groupIndicator="@null">
</ExpandableListView>
package cn.nubia.mobilesecurityguard.activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import cn.nubia.mobilesecurityguard.R;
public class CommonNumberActivity extends AppCompatActivity {
@BindView(R.id.elv_list)
ExpandableListView elvList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common_number);
ButterKnife.bind(this);
CommonNumberAdapter adapter = new CommonNumberAdapter();
elvList.setAdapter(adapter);
}
class CommonNumberAdapter extends BaseExpandableListAdapter {
//返回组的个数
@Override
public int getGroupCount() {
return 5;
}
//返回某组孩子的个数
@Override
public int getChildrenCount(int groupPosition) {
return 3;
}
//getItem
@Override
public Object getGroup(int groupPosition) {
return null;
}
//返回孩子对象
@Override
public Object getChild(int groupPosition, int childPosition) {
return null;
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
//是否有固定id
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
TextView textView = new TextView(CommonNumberActivity.this);
textView.setText("第" + groupPosition + "组");
textView.setBackgroundColor(Color.GRAY);
textView.setPadding(10, 10, 10, 10);
textView.setTextSize(18);
return textView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
TextView textView = new TextView(CommonNumberActivity.this);
textView.setText("第" + groupPosition + "组" + "第" + childPosition + "项");
textView.setBackgroundColor(Color.GREEN);
textView.setPadding(10, 10, 10, 10);
textView.setTextSize(14);
return textView;
}
//孩子是否可以点击
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}
}
MSG 10-03 常用号码数据库封装
package cn.nubia.mobilesecurityguard.db.dao;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
public class CommonNumberDao {
public static ArrayList<GroupInfo> getCommonNumbers(Context context) {
//读取数据库文件
String dbFilePath = context.getFilesDir().getAbsolutePath();
//以只读方式打开数据库,参1:数据库文件的本地路径
SQLiteDatabase database = SQLiteDatabase.openDatabase(dbFilePath + "/commonnum.db", null, SQLiteDatabase.OPEN_READONLY);
//需要将资产目录下的数据库文件拷贝到本地文件路径下(一般在闪屏页面操作)copyDb()
//data/data/packagename/files/下
Cursor cursor = database.query("classlist", new String[]{"name", "idx"}, null, null, null, null, null);
ArrayList<GroupInfo> mList = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
GroupInfo groupInfo = new GroupInfo();
groupInfo.name = cursor.getString(0);
groupInfo.idx = cursor.getString(1);
groupInfo.children = getChildList(groupInfo.idx, database);
mList.add(groupInfo);
}
cursor.close();
}
database.close();
return mList;
}
private static ArrayList<ChildInfo> getChildList(String idx, SQLiteDatabase database) {
Cursor cursor = database.query("table" + idx, new String[]{"number", "name"}, null, null, null, null, null);
ArrayList<ChildInfo> mList = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
ChildInfo childInfo = new ChildInfo();
childInfo.number = cursor.getString(0);
childInfo.name = cursor.getString(1);
mList.add(childInfo);
}
cursor.close();
}
//database.close(); while循环还没结束
return mList;
}
public static class GroupInfo {
public String name;
public String idx;
public ArrayList<ChildInfo> children;
}
public static class ChildInfo {
public String number;
public String name;
}
}
MSG 10-04 常用号码数据展示及点击处理
package cn.nubia.mobilesecurityguard.activity;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.TextView;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.db.dao.CommonNumberDao;
public class CommonNumberActivity extends AppCompatActivity {
@BindView(R.id.elv_list)
ExpandableListView elvList;
private ArrayList<CommonNumberDao.GroupInfo> commonNumbers;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common_number);
ButterKnife.bind(this);
initData();
CommonNumberAdapter adapter = new CommonNumberAdapter();
elvList.setAdapter(adapter);
elvList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
//跳转到对应打电话界面
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + commonNumbers.get(groupPosition).children.get(childPosition).number));
startActivity(intent);
return false;
}
});
}
private void initData() {
commonNumbers = CommonNumberDao.getCommonNumbers(this);
}
class CommonNumberAdapter extends BaseExpandableListAdapter {
//返回组的个数
@Override
public int getGroupCount() {
return commonNumbers.size();
}
//返回某组孩子的个数
@Override
public int getChildrenCount(int groupPosition) {
return commonNumbers.get(groupPosition).children.size();
}
//getItem
@Override
public CommonNumberDao.GroupInfo getGroup(int groupPosition) {
return commonNumbers.get(groupPosition);
}
//返回孩子对象
@Override
public CommonNumberDao.ChildInfo getChild(int groupPosition, int childPosition) {
return commonNumbers.get(groupPosition).children.get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
//是否有固定id
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
TextView textView = new TextView(CommonNumberActivity.this);
CommonNumberDao.GroupInfo group = getGroup(groupPosition);
textView.setText(group.name);
textView.setBackgroundColor(Color.GRAY);
textView.setPadding(10, 10, 10, 10);
textView.setTextSize(18);
return textView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
TextView textView = new TextView(CommonNumberActivity.this);
CommonNumberDao.ChildInfo child = getChild(groupPosition, childPosition);
textView.setText(child.name + "\n" + child.number);
textView.setBackgroundColor(Color.GREEN);
textView.setPadding(10, 10, 10, 10);
textView.setTextSize(14);
return textView;
}
//孩子是否可以点击
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
}
MSG 10-05 读取短信数据
package cn.nubia.mobilesecurityguard.utils;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.Telephony;
import android.util.Log;
import java.util.ArrayList;
/**
* 短信备份和还原
*/
public class SmsUtils {
private static Cursor cursor;
private static ArrayList<SmsInfo> mList;
//短信备份
public static void smsBackup(Context context) {
//1、读取系统的短信数据库,data/data/android.provider.tetephony/databases/mmssms.db
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
cursor = context.getContentResolver().query(Telephony.Sms.CONTENT_URI, new String[]{"address", "date", "read", "type", "body"}, null, null, null);
} else {
cursor = context.getContentResolver().query(Uri.parse("content://sms"), new String[]{"address", "date", "read", "type", "body"}, null, null, null);
}
//3、权限
//<uses-permission android:name="android.permission.READ_SMS"/>
//<uses-permission android:name="android.permission.WRITE_SMS"/>
mList = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
SmsInfo smsInfo = new SmsInfo();
//smsInfo.address = cursor.getString(0);
smsInfo.address = cursor.getString(cursor.getColumnIndex("address"));
smsInfo.date = cursor.getString(cursor.getColumnIndex("date"));
smsInfo.read = cursor.getString(cursor.getColumnIndex("read"));
smsInfo.type = cursor.getString(cursor.getColumnIndex("type"));
smsInfo.body = cursor.getString(cursor.getColumnIndex("body"));
mList.add(smsInfo);
}
cursor.close();
}
Log.e("sunyang", "短信:" + mList);
//2、将短信内容保存在本地,将集合序列化 Gson
}
public static class SmsInfo {
public String address;
public String date;
public String read;
public String type;
public String body;
@Override
public String toString() {
return "SmsInfo{" +
"address='" + address + '\'' +
", date='" + date + '\'' +
", read='" + read + '\'' +
", type='" + type + '\'' +
", body='" + body + '\'' +
'}';
}
}
}
MSG 10-06 Gson使用
//2、将短信内容保存在本地,将集合序列化 Gson
Gson gson = new Gson();
String json = gson.toJson(mList);
Log.e("sunyang", "短信序列化:" + json);
MSG 10-07 将短信写入本地文件
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(json.getBytes());
fileOutputStream.flush();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
备份到:
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/sms.backup");
SmsUtils.smsBackup(this, file);
MSG 10-08 备份进度展示
1、创建进度条
private void smsBackup() {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return;
}
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle("正在备份");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();
new Thread() {
@Override
public void run() {
//super.run();
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/sms.backup");
SmsUtils.smsBackup(CommonToolsActivity.this, file, dialog);
dialog.dismiss();
}
}.start();
}
2、进度计算
package cn.nubia.mobilesecurityguard.utils;
import android.app.ProgressDialog;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.Telephony;
import android.util.Log;
import com.google.gson.Gson;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
/**
* 短信备份和还原
*/
public class SmsUtils {
private static Cursor cursor;
private static ArrayList<SmsInfo> mList;
//短信备份
public static void smsBackup(Context context, File file, ProgressDialog dialog) {
//1、读取系统的短信数据库,data/data/android.provider.tetephony/databases/mmssms.db
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
cursor = context.getContentResolver().query(Telephony.Sms.CONTENT_URI, new String[]{"address", "date", "read", "type", "body"}, null, null, null);
} else {
cursor = context.getContentResolver().query(Uri.parse("content://sms"), new String[]{"address", "date", "read", "type", "body"}, null, null, null);
}
int totalCount = cursor.getCount();
dialog.setMax(totalCount);
//3、权限
//<uses-permission android:name="android.permission.READ_SMS"/>
//<uses-permission android:name="android.permission.WRITE_SMS"/>
mList = new ArrayList<>();
if (cursor != null) {
int progress = 0;//当前备份条数
while (cursor.moveToNext()) {
SmsInfo smsInfo = new SmsInfo();
//smsInfo.address = cursor.getString(0);
smsInfo.address = cursor.getString(cursor.getColumnIndex("address"));
smsInfo.date = cursor.getString(cursor.getColumnIndex("date"));
smsInfo.read = cursor.getString(cursor.getColumnIndex("read"));
smsInfo.type = cursor.getString(cursor.getColumnIndex("type"));
smsInfo.body = cursor.getString(cursor.getColumnIndex("body"));
mList.add(smsInfo);
progress++;
//int percent = progress * 100 / totalCount;
//dialog.setProgress(percent);
dialog.setProgress(progress);
}
cursor.close();
}
Log.e("sunyang", "短信:" + mList);
//2、将短信内容保存在本地,将集合序列化 Gson
Gson gson = new Gson();
String json = gson.toJson(mList);
Log.e("sunyang", "短信序列化:" + json);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(json.getBytes());
fileOutputStream.flush();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static class SmsInfo {
public String address;
public String date;
public String read;
public String type;
public String body;
@Override
public String toString() {
return "SmsInfo{" +
"address='" + address + '\'' +
", date='" + date + '\'' +
", read='" + read + '\'' +
", type='" + type + '\'' +
", body='" + body + '\'' +
'}';
}
}
}
备注:6.0以上增加动态权限
MSG 10-09 项目耦合重引起的问题
回调实现解耦:
private void smsBackup() {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return;
}
dialog = new ProgressDialog(this);
dialog.setTitle("正在备份");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();
new Thread() {
@Override
public void run() {
//super.run();
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/sms.backup");
SmsUtils.smsBackup(CommonToolsActivity.this, file, new SmsCallback());
dialog.dismiss();
}
}.start();
}
//1、声明回调接口
public interface OnSmsCallback {
//获取短信个数
void onGetTotalCount(int totalCount);
//当前备份数量
void onGetCurrentProgress(int progress);
}
//2、实现回调接口
class SmsCallback implements OnSmsCallback {
@Override
public void onGetTotalCount(int totalCount) {
dialog.setMax(totalCount);
}
@Override
public void onGetCurrentProgress(int progress) {
dialog.setProgress(progress);
}
}
MSG 10-10 回调的使用
MSG 10-11 回调方法小结
MSG 10-12 将文件反序列化为对象
public static void smsRestore(Context context, File file, CommonToolsActivity.OnSmsCallback callback) {
//1、从本地文件读取短信
Gson gson = new Gson();
//2、输入流
try {
Type type = new TypeToken<ArrayList<SmsInfo>>() {
}.getType();
ArrayList<SmsInfo> list = gson.fromJson(new FileReader(file), type);
callback.onGetTotalCount(list.size());
//3、集合
ContentResolver resolver = context.getContentResolver();
int progress = 0;
for (SmsInfo info : list) {
ContentValues values = new ContentValues();
values.put("address", info.address);
values.put("date", info.date);
values.put("read", info.read);
values.put("type", info.type);
values.put("body", info.body);
//4、将数据插入数据库
resolver.insert(Uri.parse("content://sms"), values);
progress++;
callback.onGetCurrentProgress(progress);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
MSG 10-13 短信还原&插入短信数据库
备份和还原的完整代码:
package cn.nubia.mobilesecurityguard.utils;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.Telephony;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.activity.CommonToolsActivity;
/**
* 短信备份和还原
*/
public class SmsUtils {
private static Cursor cursor;
private static ArrayList<SmsInfo> mList;
//短信备份
public static void smsBackup(Context context, File file, CommonToolsActivity.OnSmsCallback callback) {
//1、读取系统的短信数据库,data/data/android.provider.tetephony/databases/mmssms.db
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
cursor = context.getContentResolver().query(Telephony.Sms.CONTENT_URI, new String[]{"address", "date", "read", "type", "body"}, null, null, null);
} else {
cursor = context.getContentResolver().query(Uri.parse("content://sms"), new String[]{"address", "date", "read", "type", "body"}, null, null, null);
}
int totalCount = cursor.getCount();
//dialog.setMax(totalCount);
callback.onGetTotalCount(totalCount);
//3、权限
//<uses-permission android:name="android.permission.READ_SMS"/>
//<uses-permission android:name="android.permission.WRITE_SMS"/>
mList = new ArrayList<>();
if (cursor != null) {
int progress = 0;//当前备份条数
while (cursor.moveToNext()) {
SmsInfo smsInfo = new SmsInfo();
//smsInfo.address = cursor.getString(0);
smsInfo.address = cursor.getString(cursor.getColumnIndex("address"));
smsInfo.date = cursor.getString(cursor.getColumnIndex("date"));
smsInfo.read = cursor.getString(cursor.getColumnIndex("read"));
smsInfo.type = cursor.getString(cursor.getColumnIndex("type"));
smsInfo.body = cursor.getString(cursor.getColumnIndex("body"));
mList.add(smsInfo);
progress++;
//int percent = progress * 100 / totalCount;
//dialog.setProgress(percent);
//dialog.setProgress(progress);
callback.onGetCurrentProgress(progress);
}
cursor.close();
}
Log.e("sunyang", "短信:" + mList);
//2、将短信内容保存在本地,将集合序列化 Gson
Gson gson = new Gson();
String json = gson.toJson(mList);
Log.e("sunyang", "短信序列化:" + json);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(json.getBytes());
fileOutputStream.flush();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void smsRestore(Context context, File file, CommonToolsActivity.OnSmsCallback callback) {
//1、从本地文件读取短信
Gson gson = new Gson();
//2、输入流
try {
Type type = new TypeToken<ArrayList<SmsInfo>>() {
}.getType();
ArrayList<SmsInfo> list = gson.fromJson(new FileReader(file), type);
callback.onGetTotalCount(list.size());
//3、集合
ContentResolver resolver = context.getContentResolver();
int progress = 0;
for (SmsInfo info : list) {
Cursor cursor = resolver.query(Uri.parse("content://sms"), null, "address=? and date=? and read=? and type=? and body=?", new String[]{info.address, info.date, info.read, info.type, info.body}, null);
if (cursor != null) {
if (cursor.moveToNext()) {
continue;
}
}
ContentValues values = new ContentValues();
values.put("address", info.address);
values.put("date", info.date);
values.put("read", info.read);
values.put("type", info.type);
values.put("body", info.body);
//4、将数据插入数据库
resolver.insert(Uri.parse("content://sms"), values);
progress++;
callback.onGetCurrentProgress(progress);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static class SmsInfo {
public String address;
public String date;
public String read;
public String type;
public String body;
@Override
public String toString() {
return "SmsInfo{" +
"address='" + address + '\'' +
", date='" + date + '\'' +
", read='" + read + '\'' +
", type='" + type + '\'' +
", body='" + body + '\'' +
'}';
}
}
}
备份和还原代码:
package cn.nubia.mobilesecurityguard.activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.utils.SmsUtils;
import cn.nubia.mobilesecurityguard.view.SettingItemView;
public class CommonToolsActivity extends AppCompatActivity implements View.OnClickListener {
private SettingItemView click1;
private SettingItemView click2;
private SettingItemView click3;
private SettingItemView click4;
private SettingItemView click5;
private SettingItemView click6;
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common_tools);
initView();
initData();
}
private void initData() {
}
private void initView() {
click1 = findViewById(R.id.click1);
click2 = findViewById(R.id.click2);
click3 = findViewById(R.id.click3);
click4 = findViewById(R.id.click4);
click5 = findViewById(R.id.click5);
click6 = findViewById(R.id.click6);
click1.setOnClickListener(this);
click2.setOnClickListener(this);
click3.setOnClickListener(this);
click4.setOnClickListener(this);
click5.setOnClickListener(this);
click6.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.click1:
Log.e("gordon", "号码归属地查询");
Intent intent1 = new Intent(this, AddressActivity.class);
startActivity(intent1);
break;
case R.id.click2:
Log.e("gordon", "常用号码查询");
Intent intent2 = new Intent(this, CommonNumberActivity.class);
startActivity(intent2);
break;
case R.id.click3:
Log.e("gordon", "短信备份");
smsBackup();
break;
case R.id.click4:
Log.e("gordon", "短信还原");
smsRestore();
break;
case R.id.click5:
Log.e("gordon", "程序锁管理");
Intent intent5 = new Intent(this, CommonToolsActivity.class);
startActivity(intent5);
break;
case R.id.click6:
Log.e("gordon", "开启程序锁服务");
break;
}
}
private void smsRestore() {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return;
}
dialog = new ProgressDialog(this);
dialog.setTitle("正在还原");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();
new Thread() {
@Override
public void run() {
//super.run();
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/sms.backup");
SmsUtils.smsRestore(CommonToolsActivity.this, file, new SmsCallback());
dialog.dismiss();
}
}.start();
}
private void smsBackup() {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return;
}
dialog = new ProgressDialog(this);
dialog.setTitle("正在备份");
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();
new Thread() {
@Override
public void run() {
//super.run();
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/sms.backup");
SmsUtils.smsBackup(CommonToolsActivity.this, file, new SmsCallback());
dialog.dismiss();
}
}.start();
}
//1、声明回调接口
public interface OnSmsCallback {
//获取短信个数
void onGetTotalCount(int totalCount);
//当前备份数量
void onGetCurrentProgress(int progress);
}
//2、实现回调接口
class SmsCallback implements OnSmsCallback {
@Override
public void onGetTotalCount(int totalCount) {
dialog.setMax(totalCount);
}
@Override
public void onGetCurrentProgress(int progress) {
dialog.setProgress(progress);
}
}
}
MSG 10-14 进程数据信息获取
package cn.nubia.mobilesecurityguard.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import java.util.HashSet;
import java.util.List;
/**
* <service
* android:name=".service.BlackNumberService"
* android:enabled="true"
* android:process="cn.nubia.balck.number.service"
* android:exported="true" />
*/
public class ProcessManagerProvider {
//正在运行的数量
public static int getRunningProcessNum(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int size = am.getRunningAppProcesses().size();
return size;
}
//获取可有进程数量,当全部应用的进程全部跑起来,总共有多少个进程
public static int getTotalProcessNum(Context context) {
PackageManager pm = context.getPackageManager();
List<PackageInfo> installedPackages = pm.getInstalledPackages(
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
| PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS);
int count = 0;
for (PackageInfo info : installedPackages) {
//不允许有重复的名字出现
HashSet<String> list = new HashSet<String>();
//记录app默认进程
list.add(info.applicationInfo.processName);
//统计activity进程
ActivityInfo[] activities = info.activities;
if (activities != null) {
for (ActivityInfo activityInfo : activities) {
list.add(activityInfo.processName);
}
}
//统计service进程
ServiceInfo[] services = info.services;
if (services != null) {
for (ServiceInfo servicesInfo : services) {
list.add(servicesInfo.processName);
}
}
//统计receiver进程
ActivityInfo[] receivers = info.receivers;
if (receivers != null) {
for (ActivityInfo receiverInfo : receivers) {
list.add(receiverInfo.processName);
}
}
//统计provider进程
ProviderInfo[] providers = info.providers;
if (providers != null) {
for (ProviderInfo providerInfo : providers) {
list.add(providerInfo.packageName);
}
}
count += list.size();
}
return count;
}
}
MSG 10-15 内存信息的获取和展示
//获取可用内存大小
public static long getAvailMemory(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);
return memoryInfo.availMem;
}
//总内存大小
public static long getTotalMemory(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
am.getMemoryInfo(memoryInfo);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
return memoryInfo.totalMem;
} else {
//为了兼容各个版本,可以读取proc/meminfo文件,获取总内存大小
try {
BufferedReader reader = new BufferedReader(new FileReader("proc/meminfo"));
String line = reader.readLine();
char[] chars = line.toCharArray();
StringBuffer sb = new StringBuffer();
for (char aChar : chars) {
if (aChar >= 0 && aChar <= 9) {
sb.append(aChar);
}
}
String strMem = sb.toString();
long memory = Long.parseLong(strMem) * 1024;
return memory;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return -1;
}
MSG 10-16 获取所有正在运行的进程
//获取所有正在运行的进程
public static ArrayList<ProcessInfo> getRunningProcesses(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = am.getRunningAppProcesses();
PackageManager pm = context.getPackageManager();
ArrayList<ProcessInfo> list = new ArrayList<>();
for (ActivityManager.RunningAppProcessInfo runningAppProcess : runningAppProcesses) {
ProcessInfo processInfo = new ProcessInfo();
String processName = runningAppProcess.processName;
Debug.MemoryInfo[] processMemoryInfo = am.getProcessMemoryInfo(new int[]{runningAppProcess.pid});
//内存大小
long totalPrivateDirty = processMemoryInfo[0].getTotalPrivateDirty() * 1024;
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(processName, 0);
String name = applicationInfo.loadLabel(pm).toString();
Drawable icon = applicationInfo.loadIcon(pm);
String packageName = applicationInfo.packageName;
processInfo.name = name;
processInfo.icon = icon;
processInfo.memory = totalPrivateDirty;
processInfo.packageName = packageName;
int flags = applicationInfo.flags;
if ((flags & ApplicationInfo.FLAG_SYSTEM)>0) {
processInfo.isUserProcess = false;
} else {
processInfo.isUserProcess = true;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
list.add(processInfo);
}
return list;
}
MSG 10-17 展示进程列表
MSG 11-02 studio关联第三方库
MSG 11-03 StickyListView的使用
package cn.nubia.mobilesecurityguard.activity;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import org.w3c.dom.Text;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.ProcessInfo;
import cn.nubia.mobilesecurityguard.utils.ProcessManagerProvider;
import cn.nubia.mobilesecurityguard.view.ProgressView;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
public class ProcessManagerActivity extends AppCompatActivity {
ProgressView leftPb;
ProgressView rightPb;
StickyListHeadersListView lvProcessManager;
private ProcessManagerAdapter processManagerAdapter;
private ArrayList<ProcessInfo> runningProcesses;
private ArrayList sysList;
private ArrayList appList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_process_manager);
lvProcessManager = findViewById(R.id.lv_process_manager);
leftPb = findViewById(R.id.left_pb);
rightPb = findViewById(R.id.right_pb);
//正在运行的进程数
int runningProcessNum = ProcessManagerProvider.getRunningProcessNum(this);
//可有的进程数
int totalProcessNum = ProcessManagerProvider.getTotalProcessNum(this);
leftPb.setTitle("进程数:");
rightPb.setTitle("内存:");
leftPb.setleftText("正在运行" + runningProcessNum + "个");
leftPb.setRightText("可有进程" + totalProcessNum + "个");
int precent = runningProcessNum * 100 / totalProcessNum;
leftPb.setProgress(precent);
long availMemory = ProcessManagerProvider.getAvailMemory(this);
long totalMemory = ProcessManagerProvider.getTotalMemory(this);
long useMemory = totalMemory - availMemory;
rightPb.setleftText("占用内存:" + Formatter.formatFileSize(this, useMemory));
rightPb.setRightText("可用内存:" + Formatter.formatFileSize(this, availMemory));
int memPrecent = (int) (useMemory * 100 / totalMemory);
rightPb.setProgress(memPrecent);
initData();
}
private void initData() {
new Thread() {
@Override
public void run() {
//super.run();
runningProcesses = ProcessManagerProvider.getRunningProcesses(ProcessManagerActivity.this);
sysList = new ArrayList();
appList = new ArrayList();
for (int i = 0; i < runningProcesses.size(); i++) {
if (runningProcesses.get(i).isUserProcess) {
appList.add(runningProcesses.get(i));
} else {
sysList.add(runningProcesses.get(i));
}
}
//清空集合
runningProcesses.clear();
runningProcesses.addAll(appList);
runningProcesses.addAll(sysList);
runOnUiThread(new Runnable() {
@Override
public void run() {
processManagerAdapter = new ProcessManagerAdapter();
lvProcessManager.setAdapter(processManagerAdapter);
}
});
}
}.start();
}
class ProcessManagerAdapter extends BaseAdapter implements StickyListHeadersAdapter {
@Override
public View getHeaderView(int position, View convertView, ViewGroup parent) {
TitleHolder titleHolder = null;
if (convertView == null) {
convertView = View.inflate(ProcessManagerActivity.this, R.layout.item_app_title, null);
titleHolder = new TitleHolder();
titleHolder.tvTitle = convertView.findViewById(R.id.tv_title);
//将holder保存和当前布局绑定
convertView.setTag(titleHolder);
} else {
titleHolder = (TitleHolder) convertView.getTag();
}
ProcessInfo info = getItem(position);
if (info.isUserProcess) {
titleHolder.tvTitle.setText("用户进程");
} else {
titleHolder.tvTitle.setText("系统进程");
}
return convertView;
}
@Override
public long getHeaderId(int position) {
return getItem(position).isUserProcess ? 0 : 1;
}
@Override
public int getCount() {
return runningProcesses.size();
}
@Override
public ProcessInfo getItem(int position) {
return runningProcesses.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(ProcessManagerActivity.this, R.layout.item_process_manager, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.tvMemory = convertView.findViewById(R.id.tv_memory);
holder.checkBox = convertView.findViewById(R.id.cb_checkbox);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ProcessInfo info = getItem(position);
holder.ivIcon.setImageDrawable(info.icon);
holder.tvName.setText(info.name);
holder.tvMemory.setText(Formatter.formatFileSize(getApplicationContext(), info.memory));
return convertView;
}
}
static class ViewHolder {
public ImageView ivIcon;
public TextView tvName;
public TextView tvMemory;
public CheckBox checkBox;
}
static class TitleHolder {
public TextView tvTitle;
}
}
MSG 11-04 单选效果实现
1、禁用checkbox的点击事件:
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
2、让listview可以点击
3、点击条目时切换checkbox的状态
lvProcessManager.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
CheckBox check = view.findViewById(R.id.cb_checkbox);
check.setChecked(!check.isChecked());
ProcessInfo info = runningProcesses.get(position);
info.isChecked = !info.isChecked;
}
});
public boolean isChecked;记录状态
MSG 11-05 全选和反选功能实现
全选:
for (ProcessInfo info : runningProcesses) {
info.isChecked = true;
}
processManagerAdapter.notifyDataSetChanged();
反选:
for (ProcessInfo info : runningProcesses) {
info.isChecked = !info.isChecked;
}
processManagerAdapter.notifyDataSetChanged();
MSG 11-06 跳过手机卫士选中
getView中:
if (info.packageName.equals(getPackageName())) {
holder.checkBox.setVisibility(View.GONE);
} else {
holder.checkBox.setVisibility(View.VISIBLE);
holder.checkBox.setChecked(info.isChecked);
}
在点击条目时跳过此对象:
CheckBox check = view.findViewById(R.id.cb_checkbox);
ProcessInfo info = runningProcesses.get(position);
if (info.packageName.equals(getPackageName())) {
return;
} else {
check.setChecked(!check.isChecked());
info.isChecked = !info.isChecked;
}
全选和反选:跳过
case R.id.bt_fan_xuan:
for (ProcessInfo info : runningProcesses) {
if (info.packageName.equals(getPackageName())) {
return;
}
info.isChecked = !info.isChecked;
}
processManagerAdapter.notifyDataSetChanged();
break;
case R.id.bt_all_xuan:
for (ProcessInfo info : runningProcesses) {
if (info.packageName.equals(getPackageName())) {
return;
}
info.isChecked = true;
}
processManagerAdapter.notifyDataSetChanged();
break;
MSG 11-07 一键清理&界面刷新
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (ProcessInfo info : runningProcesses) {
if (info.isChecked) {
am.killBackgroundProcesses(info.packageName);
}
info.isChecked = !info.isChecked;
}
需要权限:
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
//会出现并发修改异常
ArrayList<ProcessInfo> killedList = new ArrayList<>();
for (ProcessInfo info : runningProcesses) {
if (info.isChecked) {
am.killBackgroundProcesses(info.packageName);
//runningProcesses.remove(info);
killedList.add(info);
}
}
runningProcesses.removeAll(killedList);
processManagerAdapter.notifyDataSetChanged();
MSG 11-08 一键清理友好提示&进程内存信息更新
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
//会出现并发修改异常
ArrayList<ProcessInfo> killedList = new ArrayList<>();
for (ProcessInfo info : runningProcesses) {
if (info.isChecked) {
am.killBackgroundProcesses(info.packageName);
//runningProcesses.remove(info);
killedList.add(info);
}
}
long saveMemory= 0;
for (ProcessInfo info : killedList) {
saveMemory += info.memory;
}
runningProcesses.removeAll(killedList);
processManagerAdapter.notifyDataSetChanged();
String toast = String.format("一共杀死%d个进程,释放%sM内存空间", killedList.size(), Formatter.formatFileSize(this, saveMemory));
Toast.makeText(this, toast, Toast.LENGTH_SHORT).show();
MSG 11-09 SlidingDrawer抽屉效果实现
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:nubia="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.ProcessManagerActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
style="@style/TitleStyle"
android:text="进程管理" />
<ImageView
android:id="@+id/iv_clean"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="20dp"
android:src="@drawable/clean_normal" />
</RelativeLayout>
<cn.nubia.mobilesecurityguard.view.ProgressView
android:id="@+id/left_pb"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</cn.nubia.mobilesecurityguard.view.ProgressView>
<cn.nubia.mobilesecurityguard.view.ProgressView
android:id="@+id/right_pb"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</cn.nubia.mobilesecurityguard.view.ProgressView>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<se.emilsjolander.stickylistheaders.StickyListHeadersListView
android:id="@+id/lv_process_manager"
android:layout_width="match_parent"
android:layout_height="match_parent">
</se.emilsjolander.stickylistheaders.StickyListHeadersListView>
<SlidingDrawer
android:layout_width="match_parent"
android:layout_height="match_parent"
android:content="@+id/content"
android:handle="@+id/handle">
<RelativeLayout
android:id="@id/handle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/drawer_bg">
<ImageView
android:id="@+id/iv_arrow1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:src="@drawable/drawer_arrow_up" />
<ImageView
android:id="@+id/iv_arrow2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_arrow1"
android:layout_centerHorizontal="true"
android:src="@drawable/drawer_arrow_up" />
</RelativeLayout>
<LinearLayout
android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:text="进程管理设置" />
<cn.nubia.mobilesecurityguard.view.SettingItemView
android:id="@+id/show_sys_process"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
nubia:nubia_background="first"
nubia:nubia_show_toggle="true"
nubia:nubia_title="显示系统进程">
</cn.nubia.mobilesecurityguard.view.SettingItemView>
<cn.nubia.mobilesecurityguard.view.SettingItemView
android:id="@+id/screen_off_clean"
android:layout_width="match_parent"
android:layout_height="wrap_content"
nubia:nubia_background="last"
nubia:nubia_show_toggle="true"
nubia:nubia_title="锁屏自动清理">
</cn.nubia.mobilesecurityguard.view.SettingItemView>
</LinearLayout>
</SlidingDrawer>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/bt_all_xuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="全选" />
<Button
android:id="@+id/bt_fan_xuan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="反选" />
</LinearLayout>
</LinearLayout>
MSG 11-10 抽屉箭头动画
private void startAnimal() {
AlphaAnimation animation1 = new AlphaAnimation(0.2f, 1);
animation1.setDuration(500);
animation1.setRepeatCount(Animation.INFINITE);
animation1.setRepeatMode(Animation.REVERSE);
ivArrow1.startAnimation(animation1);
AlphaAnimation animation2 = new AlphaAnimation(1, 0.2f);
animation2.setDuration(500);
animation2.setRepeatCount(Animation.INFINITE);
animation2.setRepeatMode(Animation.REVERSE);
ivArrow2.startAnimation(animation2);
}
向下停止动画:
sdDrawer.setOnDrawerOpenListener(new SlidingDrawer.OnDrawerOpenListener() {
@Override
public void onDrawerOpened() {
ivArrow1.setImageResource(R.drawable.drawer_arrow_down);
ivArrow2.setImageResource(R.drawable.drawer_arrow_down);
ivArrow1.clearAnimation();
ivArrow2.clearAnimation();
}
});
sdDrawer.setOnDrawerCloseListener(new SlidingDrawer.OnDrawerCloseListener() {
@Override
public void onDrawerClosed() {
ivArrow1.setImageResource(R.drawable.drawer_arrow_up);
ivArrow2.setImageResource(R.drawable.drawer_arrow_down);
startAnimal();
}
});
MSG 11-11 是否显示系统进程
@Override
public int getCount() {
if (isShowSysProcess) {
return runningProcesses.size();
} else {
return appList.size();
}
}
打开关闭抽屉时会导致listview隐藏,会自动刷新listview
MSG 11-12 锁屏自动清理&定时器Timer
判断锁屏服务是否开启
case R.id.screen_off_clean:
if (ServiceStatusUtils.isServiceRunning(this,AutoCleanService.class)) {
stopService(new Intent(this,AutoCleanService.class));
} else {
startService(new Intent(this,AutoCleanService.class));
}
break;
创建服务:
package cn.nubia.mobilesecurityguard.service;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
import cn.nubia.mobilesecurityguard.utils.ProcessManagerProvider;
public class AutoCleanService extends Service {
private AutoKillReceiver receiver;
public AutoCleanService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
//动态注册广播,监听锁屏
receiver = new AutoKillReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(receiver,filter);
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
}
class AutoKillReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("sunyang","屏幕关闭了");
ProcessManagerProvider.killAllProcesses(context);
}
}
}
计时器关闭:
package cn.nubia.mobilesecurityguard.service;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
import cn.nubia.mobilesecurityguard.utils.ProcessManagerProvider;
public class AutoCleanService extends Service {
private AutoKillReceiver receiver;
private Timer timer;
public AutoCleanService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
//动态注册广播,监听锁屏
receiver = new AutoKillReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(receiver, filter);
//定时清理,参2:第一次执行任务的延时时间,参3:每次执行任务的时间间隔
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
}
}, 0, 5000);
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
timer.cancel();
}
class AutoKillReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.e("sunyang", "屏幕关闭了");
ProcessManagerProvider.killAllProcesses(context);
}
}
}
MSG 11-13 程序锁页面开发
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.AppLockActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">
<Button
android:id="@+id/bt_unlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tab_left_default"
android:text="未加锁" />
<Button
android:id="@+id/bt_lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/tab_right_pressed"
android:text="已加锁" />
</LinearLayout>
<TextView
android:id="@+id/tv_lock_number"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@color/gray"
android:text="未加锁应用(0)" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/lv_unlock"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
<ListView
android:id="@+id/lv_lock"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"></ListView>
</FrameLayout>
</LinearLayout>
MSG 12-02 程序锁布局开发&切换已加锁未加锁页面
MSG 12-03 程序锁数据库封装
package cn.nubia.mobilesecurityguard.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class AppLockOpenHelper extends SQLiteOpenHelper {
public AppLockOpenHelper(Context context) {
super(context, "applock.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建表
String sql = "create table applock(_id integer primary key autoincrement," +
" package varchar(50))";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
增删改查:
package cn.nubia.mobilesecurityguard.db.dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.bean.BlackNumberInfo;
import cn.nubia.mobilesecurityguard.db.AppLockOpenHelper;
import cn.nubia.mobilesecurityguard.db.BlackNumberOpenHelper;
/**
* 程序锁-单例设计模式
* <p>
* 两种方式进行初始化:
* 1、懒汉式:线程安全问题 1.给方法加同步锁 synchronized 2.创建对象的代码块加同步锁
* //2、公开方法,返回单例对象
* public static BlackNumberDao getInstance(Context context) {
* if (mInstance == null) {
* synchronized (BlackNumberDao.class) {
* if (mInstance == null) {
* mInstance = new BlackNumberDao(context);
* }
* }
* }
* return mInstance;
* }
* 2、饿汉式:
* private static BlackNumberDao mInstance = new BlackNumberDao();
*/
public class AppLockDao {
private final AppLockOpenHelper helper;
//3、声明一个静态对象
private static AppLockDao mInstance;
//1、构造方法私有
private AppLockDao(Context context) {
helper = new AppLockOpenHelper(context);
}
//2、公开方法,返回单例对象
public static AppLockDao getInstance(Context context) {
if (mInstance == null) {
synchronized (AppLockDao.class) {
if (mInstance == null) {
mInstance = new AppLockDao(context);
}
}
}
return mInstance;
}
public boolean insert(String packageName) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("packageName", packageName);
//返回插入记录的id,-1表示失败
long insert = db.insert("applock", null, values);
db.close();
return insert != -1;
}
public boolean delete(String packageName) {
SQLiteDatabase db = helper.getWritableDatabase();
//影响的行数,0表示未删除
int delete = db.delete("applock", "packageName=?", new String[]{packageName});
db.close();
return delete > 0;
}
//查询是否在数据库
public boolean query(String packageName) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("applock", new String[]{"packageName"}, "packageName=?", new String[]{packageName}, null, null, null);
boolean isExit = false;
if (cursor != null) {
if (cursor.moveToNext()) {
isExit = true;
}
cursor.close();
}
db.close();
return isExit;
}
//查询所有号码的集合
public ArrayList<String> queryAll() {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("applock", new String[]{"packageName"}, null, null, null, null, null);
ArrayList<String> list = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
String number = cursor.getString(0);
list.add(number);
}
cursor.close();
}
db.close();
return list;
}
public int getCount() {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select count(*) from applock", null);
int count = 0;
try {
if (cursor != null) {
if (cursor.moveToNext()) {
count = cursor.getInt(0);
}
cursor.close();
}
db.close();
return count;
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
}
MSG 12-04 加载数据&展示未加锁列表
MSG 12-05 展示已加锁列表&公用一个adapter
MSG 12-06 加锁和解锁逻辑处理
package cn.nubia.mobilesecurityguard.activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.AppManagerInfo;
import cn.nubia.mobilesecurityguard.db.dao.AppLockDao;
import cn.nubia.mobilesecurityguard.utils.AppInfoProvider;
public class AppLockActivity extends AppCompatActivity implements View.OnClickListener {
private ListView lvLock;
private ListView lvUnLock;
private Button btLock;
private Button btUnLock;
private AppLockDao dao;
private ArrayList<AppManagerInfo> mUnLockList;
private ArrayList<AppManagerInfo> mLockList;
private AppLockAdapter unlockAdapter;
private AppLockAdapter lockAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_lock);
btLock = findViewById(R.id.bt_lock);
btUnLock = findViewById(R.id.bt_unlock);
lvLock = findViewById(R.id.lv_lock);
lvUnLock = findViewById(R.id.lv_unlock);
btLock.setOnClickListener(this);
btUnLock.setOnClickListener(this);
dao = AppLockDao.getInstance(this);
initData();
}
private void initData() {
new Thread() {
@Override
public void run() {
//super.run();
//1、获取所以已安装app
ArrayList<AppManagerInfo> installedApps = AppInfoProvider.getInstalledApps(AppLockActivity.this);
//2、判读是否已加锁,如果已加锁放在已加锁集合
mUnLockList = new ArrayList<>();
mLockList = new ArrayList<>();
for (AppManagerInfo installedApp : installedApps) {
if (dao.query(installedApp.packageName)) {
mLockList.add(installedApp);
} else {
mUnLockList.add(installedApp);
}
}
sort();
runOnUiThread(new Runnable() {
@Override
public void run() {
unlockAdapter = new AppLockAdapter(false);
lvUnLock.setAdapter(unlockAdapter);
lockAdapter = new AppLockAdapter(true);
lvLock.setAdapter(lockAdapter);
}
});
}
}.start();
}
private void sort() {
//给两个集合按照字符进行排序
Collections.sort(mUnLockList, new Comparator<AppManagerInfo>() {
@Override
public int compare(AppManagerInfo o1, AppManagerInfo o2) {
return o1.name.compareTo(o2.name);
}
});
Collections.sort(mLockList, new Comparator<AppManagerInfo>() {
@Override
public int compare(AppManagerInfo o1, AppManagerInfo o2) {
return o1.name.compareTo(o2.name);
}
});
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_lock:
//显示加锁的listview
lvLock.setVisibility(View.VISIBLE);
lvUnLock.setVisibility(View.GONE);
btLock.setBackgroundResource(R.drawable.tab_right_pressed);
btUnLock.setBackgroundResource(R.drawable.tab_left_default);
break;
case R.id.bt_unlock:
lvLock.setVisibility(View.GONE);
lvUnLock.setVisibility(View.VISIBLE);
btLock.setBackgroundResource(R.drawable.tab_right_default);
btUnLock.setBackgroundResource(R.drawable.tab_left_pressed);
break;
}
}
class AppLockAdapter extends BaseAdapter {
//标记已加锁
private boolean isLock;
public AppLockAdapter(boolean isLock) {
this.isLock = isLock;
}
@Override
public int getCount() {
if (isLock) {
return mLockList.size();
} else {
return mUnLockList.size();
}
}
@Override
public AppManagerInfo getItem(int position) {
if (isLock) {
return mLockList.get(position);
} else {
return mUnLockList.get(position);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(AppLockActivity.this, R.layout.item_appunlock, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.ivUnlock = convertView.findViewById(R.id.iv_unlock);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final AppManagerInfo info = getItem(position);
holder.ivIcon.setImageDrawable(info.icon);
holder.tvName.setText(info.name);
if (isLock) {
holder.ivUnlock.setImageResource(R.drawable.list_button_lock_pressed);
holder.ivUnlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1、从数据库移除
dao.insert(info.packageName);
//2、从已加锁集合移除
mLockList.remove(info);
//3、给未加锁集合添加对象
mUnLockList.add(info);
sort();
//4、刷新listview
unlockAdapter.notifyDataSetChanged();
lockAdapter.notifyDataSetChanged();
}
});
} else {
//未加锁
holder.ivUnlock.setImageResource(R.drawable.list_button_unlock_pressed);
holder.ivUnlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1、添加到已加锁数据库
dao.insert(info.packageName);
//2、从未加锁集合移除
mUnLockList.remove(info);
//3、给已加锁集合添加对象
mLockList.add(info);
sort();
//4、刷新listview
unlockAdapter.notifyDataSetChanged();
lockAdapter.notifyDataSetChanged();
}
});
}
return convertView;
}
}
static class ViewHolder {
public ImageView ivIcon;
public TextView tvName;
public ImageView ivUnlock;
}
}
MSG 12-07 给集合排序
//给两个集合按照字符进行排序
Collections.sort(mUnLockList, new Comparator<AppManagerInfo>() {
@Override
public int compare(AppManagerInfo o1, AppManagerInfo o2) {
return o1.name.compareTo(o2.name);
}
});
Collections.sort(mLockList, new Comparator<AppManagerInfo>() {
@Override
public int compare(AppManagerInfo o1, AppManagerInfo o2) {
return o1.name.compareTo(o2.name);
}
});
问题:增加数据时,还是排在最后,需要刷新排序
自定义集合排序
MSG 12-08 加锁解锁动画
在构造方法中初始化动画
class AppLockAdapter extends BaseAdapter {
//标记已加锁
private boolean isLock;
private final TranslateAnimation translateAnimationRight;
private final TranslateAnimation translateAnimationLeft;
public AppLockAdapter(boolean isLock) {
this.isLock = isLock;
//初始化平移动画
translateAnimationRight = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
translateAnimationRight.setDuration(500);
translateAnimationLeft = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 1, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0);
translateAnimationRight.setDuration(500);
}
@Override
public int getCount() {
if (isLock) {
return mLockList.size();
} else {
return mUnLockList.size();
}
}
@Override
public AppManagerInfo getItem(int position) {
if (isLock) {
return mLockList.get(position);
} else {
return mUnLockList.get(position);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(AppLockActivity.this, R.layout.item_appunlock, null);
holder = new ViewHolder();
holder.ivIcon = convertView.findViewById(R.id.iv_icon);
holder.tvName = convertView.findViewById(R.id.tv_name);
holder.ivUnlock = convertView.findViewById(R.id.iv_unlock);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final AppManagerInfo info = getItem(position);
holder.ivIcon.setImageDrawable(info.icon);
holder.tvName.setText(info.name);
final View finalConvertView = convertView;
if (isLock) {
holder.ivUnlock.setImageResource(R.drawable.list_button_lock_pressed);
holder.ivUnlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//动画是异步执行,需要监听动画结束的事件,在动画结束时再刷新集合
translateAnimationLeft.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//1、从数据库移除
dao.insert(info.packageName);
//2、从已加锁集合移除
mLockList.remove(info);
//3、给未加锁集合添加对象
mUnLockList.add(info);
sort();
//4、刷新listview
unlockAdapter.notifyDataSetChanged();
lockAdapter.notifyDataSetChanged();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
finalConvertView.startAnimation(translateAnimationLeft);
}
});
} else {
//未加锁
holder.ivUnlock.setImageResource(R.drawable.list_button_unlock_pressed);
holder.ivUnlock.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
translateAnimationRight.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
//1、添加到已加锁数据库
dao.insert(info.packageName);
//2、从未加锁集合移除
mUnLockList.remove(info);
//3、给已加锁集合添加对象
mLockList.add(info);
sort();
//4、刷新listview
unlockAdapter.notifyDataSetChanged();
lockAdapter.notifyDataSetChanged();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
finalConvertView.startAnimation(translateAnimationRight);
}
});
}
return convertView;
}
}
MSG 12-09 程序锁数量更新
//只要listview刷新,就需要调用一次
private void updateLockNum(boolean isLock) {
if (isLock) {
tvLockNumber.setText("已加锁("+mLockList.size()+")");
} else {
tvLockNumber.setText("未加锁("+mUnLockList.size()+")");
}
}
在第一次设置数据和每次刷新数据时都会调用
@Override
public int getCount() {
updateLockNum(isLock);
if (isLock) {
return mLockList.size();
} else {
return mUnLockList.size();
}
}
MSG 12-10 辅助功能服务介绍
/**
* 程序锁原理:
* 1、监听当前屏幕展示的页面
* 2、判断该页面是否需要加锁
* 3、如果加锁,就跳到输入密码页面
* 4、密码验证成功后,才能正常使用app
*/
配置程序锁服务:
https://developer.android.google.cn/reference/android/accessibilityservice/AccessibilityService
<service
android:name=".service.AppLockAccessibilityService"
android:label="@string/appLock_accessibility_service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibilityservice" />
</service>
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeViewClicked|typeViewFocused|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:description="辅助功能" />
package cn.nubia.mobilesecurityguard.service;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
public class AppLockAccessibilityService extends AccessibilityService {
public AppLockAccessibilityService() {
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
MSG 12-11 程序锁服务开关控制
private void startAccessibilityActivity() {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
}
@Override
protected void onStart() {
super.onStart();
boolean serviceRunning = ServiceStatusUtils.isServiceRunning(this, AppLockAccessibilityService.class);
click6.setToggleOn(serviceRunning);
}
MSG 12-12 跳到输入密码页面
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
String packageName = event.getPackageName().toString();
boolean isExist = dao.query(packageName);
//跳到输入密码界面
if (isExist) {
Intent intent = new Intent(this, AppGraLockActivity.class);
intent.putExtra("packageName", packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
package cn.nubia.mobilesecurityguard.activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import cn.nubia.mobilesecurityguard.R;
public class AppGraLockActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_gra_lock);
ImageView ivIcon = findViewById(R.id.iv_icon);
TextView tvName = findViewById(R.id.tv_name);
findViewById(R.id.et_pwd);
String packageName = getIntent().getStringExtra("packageName");
PackageManager pm = getPackageManager();
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName, 0);
Drawable icon = applicationInfo.loadIcon(pm);
String name = applicationInfo.loadLabel(pm).toString();
ivIcon.setImageDrawable(icon);
tvName.setText(name);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
MSG 12-13 拦截物理返回键跳到桌面
@Override
public void onBackPressed() {
//super.onBackPressed();
//隐式意图跳到桌面
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
finish();
}
MSG 12-14 验证密码&通过自定义广播传递数据
btOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String pwd = etPwd.getText().toString().trim();
if (!TextUtils.isEmpty(pwd)) {
if (pwd.equals("123")) {
finish();
}
}
}
});
结束后,在服务中还会开启,所以需要从activity传递给服务
//发送自定义广播,传递数据
Intent intent = new Intent();
intent.setAction("action_skip_package");
intent.putExtra("packageName", packageName);
sendBroadcast(intent);
package cn.nubia.mobilesecurityguard.service;
import android.accessibilityservice.AccessibilityService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import cn.nubia.mobilesecurityguard.activity.AppGraLockActivity;
import cn.nubia.mobilesecurityguard.db.dao.AppLockDao;
public class AppLockAccessibilityService extends AccessibilityService {
private AppLockDao dao;
private AppLockReceiver receiver;
private String packageName;
public AppLockAccessibilityService() {
}
@Override
public void onCreate() {
super.onCreate();
dao = AppLockDao.getInstance(this);
receiver = new AppLockReceiver();
IntentFilter filter = new IntentFilter("action_skip_package");
registerReceiver(receiver, filter);
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(receiver);
receiver = null;
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
String packageName = event.getPackageName().toString();
boolean isExist = dao.query(packageName);
//跳到输入密码界面
if (isExist && !packageName.equals(packageName)) {
Intent intent = new Intent(this, AppGraLockActivity.class);
intent.putExtra("packageName", packageName);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
}
@Override
public void onInterrupt() {
}
class AppLockReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
packageName = intent.getStringExtra("packageName");
Log.e("sunyang", "跳过验证");
}
}
}
MSG 12-15 通过activity启动模式解决任务栈bug
MSG 12-16 不出现在最近任务列表中
MSG 12-17 AsyncTask 的基本使用
MSG 12-18 AsyncTask 的高级使用
MSG 12-19 流量统计原理
MSG 13-02 流量统计—初始化数据
MSG 13-03 RecyclerView的基本使用
MSG 13-04 流量统计添加进度条
MSG 13-05 杀毒原理&病毒数据库封装
MSG 13-06 扫描app判断是否是病毒
MSG 13-07 展示病毒扫描列表
MSG 13-08 扫描过程中刷新RecyclerView
MSG 13-09 假冒病毒并测试
MSG 13-10 CircleProgress的使用
MSG 13-11 手机杀毒进度更新
MSG 13-12 展示扫描病毒结果
MSG 13-13 开门动画布局
MSG 13-14 获取布局所展示的截图
MSG 13-15 截取图片一半进行展示
MSG 13-16 开门动画实现
MSG 13-17 重新扫描实现
MSG 13-18 卸载病毒
MSG 13-19 启用&禁用扫描按钮
MSG 13-20 停止异步任务
MSG 13-21 横竖屏切换