package com.ittianyu.mobileguard.activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AlertDialog;
import android.text.format.Formatter;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.ittianyu.mobileguard.R;
import com.ittianyu.mobileguard.activity.base.BaseActivityUpEnableWithMenu;
import com.ittianyu.mobileguard.domain.ProcessInfoBean;
import com.ittianyu.mobileguard.engine.ProcessManagerEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* running app manager
*/
public class ProcessManagerActivity extends BaseActivityUpEnableWithMenu {
// view
private TextView tvTotalMemory;
private TextView tvFreeMemory;
private TextView tvTypeLabel;
private ListView lvApp;
private ProgressBar pbLoading;
// data
private List<ProcessInfoBean> systemApps = new CopyOnWriteArrayList<>();// CopyOnWriteArrayList use for concurrent operate
private List<ProcessInfoBean> userApps = new CopyOnWriteArrayList<>();
private List<Boolean> checkeds = new ArrayList<>();// represent which item was checked
private AppAdapter adapter = new AppAdapter();
// thread operation
private Thread initDataThread;
/**
* construct method set title
*/
public ProcessManagerActivity() {
super(R.string.process_manager, R.menu.menu_process_manager);
}
/**
* 1
*/
@Override
protected void initView() {
setContentView(R.layout.activity_process_manager);
// bind view
tvTotalMemory = (TextView) findViewById(R.id.tv_total_memory);
tvFreeMemory = (TextView) findViewById(R.id.tv_free_memory);
tvTypeLabel = (TextView) findViewById(R.id.tv_type_label);
lvApp = (ListView) findViewById(R.id.lv_app);
pbLoading = (ProgressBar) findViewById(R.id.pb_loading);
// set adapter
lvApp.setAdapter(adapter);
}
/**
* 2
*/
@Override
protected void initData() {
// get free space
long totalMemory = ProcessManagerEngine.getTotalMemory();
long freeMemory = ProcessManagerEngine.getFreeMemory(this);
// set memory info
tvTotalMemory.setText(getString(R.string.total_memory) + Formatter.formatFileSize(this, totalMemory));
tvFreeMemory.setText(getString(R.string.free_memory) + Formatter.formatFileSize(this, freeMemory));
// if the thread is running, no need to start a new thread
if (null != initDataThread && initDataThread.isAlive()) {
return;
}
// the following operator are time consuming, need run on child thread
initDataThread = new Thread() {
@Override
public void run() {
// get apps info
List<ProcessInfoBean> infos = ProcessManagerEngine.getRunningProcessesInfo(ProcessManagerActivity.this);
// change data and refresh ui
initAppsInfo(infos);
}
};
initDataThread.start();
}
/**
* init apps info
*
* @param infos
*/
private void initAppsInfo(final List<ProcessInfoBean> infos) {
// change data and notify Adapter need run on the same handler
runOnUiThread(new Runnable() {
@Override
public void run() {
// clear list before distribute
userApps.clear();
systemApps.clear();
checkeds.clear();
// distribute
for (ProcessInfoBean info : infos) {
if (info.isSystemApp()) {
systemApps.add(info);
} else {
userApps.add(info);
}
checkeds.add(false);
}
// because of two label, so need more
checkeds.add(false);
checkeds.add(false);
onDataChanged();
}
});
}
/**
* It will notify ListView and progress bar refresh
*/
private void onDataChanged() {
// refresh ListView
adapter.notifyDataSetChanged();
// hide progress bar
pbLoading.setVisibility(View.GONE);
// show type label
tvTypeLabel.setVisibility(View.VISIBLE);
}
/**
* 3
*/
@Override
protected void initEvent() {
// set scroll listener to implement type label change text
lvApp.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// prevent the data haven't loaded
if (0 == userApps.size() || 0 == systemApps.size())
return;
// the system app label is at first or above the screen
if (firstVisibleItem >= userApps.size() + 1) {
// change the type label to system app
tvTypeLabel.setText(getString(R.string.system_app) + "(" + systemApps.size() + ")");
} else {
// change the type label to user app
tvTypeLabel.setText(getString(R.string.user_app) + "(" + userApps.size() + ")");
}
}
});
// set on click listener
lvApp.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ProcessInfoBean bean = (ProcessInfoBean) lvApp.getItemAtPosition(position);
// System.out.println(bean.getName());
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS",
Uri.parse("package:" + bean.getPackageName()));
startActivity(intent);
}
});
}
/**
* cancel all ListView item
*/
private void cancelAllItem() {
for (int i = 1; i < checkeds.size(); i++) {
checkeds.set(i, false);
}
// refresh ui
adapter.notifyDataSetChanged();
}
/**
* select all ListView item
*/
private void selectAllItem() {
// set checked
for (int i = 1; i < checkeds.size(); i++) {
// prevent empty app or this app be selected
ProcessInfoBean bean = adapter.getItem(i);
if (null == bean || getPackageName().equals(bean.getPackageName()))
continue;
checkeds.set(i, true);
}
// refresh ui
adapter.notifyDataSetChanged();
}
/**
* @return the count of checked item
*/
private int getCheckedItemCount() {
int count = 0;
for (int i = 1; i < checkeds.size(); i++) {
if (i == userApps.size() + 1)
continue;
if (checkeds.get(i))
count++;
}
return count;
}
/**
* Will be call before shown
* Use it to enable or disable item
*
* @param menu
* @return
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// find items
MenuItem killProcess = menu.findItem(R.id.m_kill_process);
MenuItem selectAllItem = menu.findItem(R.id.m_select_all);
MenuItem cancelAllItem = menu.findItem(R.id.m_cancel_all);
// get checked item count
int checkedCount = getCheckedItemCount();
if (0 == checkedCount) {
// no checked item, disable it
killProcess.setEnabled(false);
cancelAllItem.setEnabled(false);
} else {
// enable it
killProcess.setEnabled(true);
cancelAllItem.setEnabled(true);
}
// if all item are checked, no need enable select all
if (checkedCount == checkeds.size()) {
selectAllItem.setEnabled(false);
} else {
selectAllItem.setEnabled(true);
}
return true;
}
/**
* menu click event
*
* @param item
* @return
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.m_select_all:
selectAllItem();
break;
case R.id.m_cancel_all:
cancelAllItem();
break;
case R.id.m_kill_process:
onKillSelectedProcesses();
break;
}
return super.onOptionsItemSelected(item);
}
/**
* query user whether kill selected process
*/
private void onKillSelectedProcesses() {
// create dialog
new AlertDialog.Builder(this)
.setTitle(R.string.tips)
.setMessage(R.string.message_kill_process)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
killSelectedProcesses();
}
})
.setNegativeButton(R.string.cancel, null)
.show();
}
/**
* kill all checked processes
*/
private void killSelectedProcesses() {
long totalReleaseMemory = 0;
int releaseCount = 0;
// get activity manager
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
// kill checked process
// delete from last position
for (int i = checkeds.size() - 1; i > 0; i--) {
if (!checkeds.get(i))
continue;
// get bean
ProcessInfoBean bean = adapter.getItem(i);
if (null == bean)
continue;
System.out.println("position:" + i + " packageName:" + bean.getPackageName());
// kill process
am.killBackgroundProcesses(bean.getPackageName());
// add count
releaseCount++;
totalReleaseMemory += bean.getMemory();
// remove app from list. Because the list is CopyOnWriteArrayList. So it can remove directly.
if (bean.isSystemApp()) {
systemApps.remove(bean);
} else {
userApps.remove(bean);
}
checkeds.remove(i);
}
// notify refresh ListView
adapter.notifyDataSetChanged();
// show tips
String tips = getString(R.string.tips_kill_process, releaseCount, Formatter.formatFileSize(this, totalReleaseMemory));
Toast.makeText(this, tips, Toast.LENGTH_SHORT).show();
}
/**
* app adapter
*/
private class AppAdapter extends BaseAdapter {
@Override
public int getCount() {
// +2: because of the two tips label
return systemApps.size() + userApps.size() + 2;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
Context context = ProcessManagerActivity.this;
// the first position is user label
if (0 == position) {
TextView tv = (TextView) View.inflate(context, R.layout.item_software_manager_type_label_lv, null);
tv.setText(getString(R.string.user_app) + "(" + userApps.size() + ")");
return tv;
}
// the userApps.size() + 1 position is system label
if (userApps.size() + 1 == position) {
TextView tv = (TextView) View.inflate(context, R.layout.item_software_manager_type_label_lv, null);
tv.setText(getString(R.string.system_app) + "(" + systemApps.size() + ")");
return tv;
}
// other position is app view
AppItem item = null;
View view = null;
// check cache
if (null == convertView || convertView instanceof TextView) {
// no cache, create a new view
view = View.inflate(context, R.layout.item_software_manager_app_lv, null);
// create item and cache it
item = new AppItem();
view.setTag(item);
// bind child view
item.tvTitle = (TextView) view.findViewById(R.id.tv_title);
item.tvSize = (TextView) view.findViewById(R.id.tv_size);
item.ivIcon = (ImageView) view.findViewById(R.id.iv_icon);
item.cb = (CheckBox) view.findViewById(R.id.cb);
} else {
// have cache and the view is not TextView
view = convertView;
item = (AppItem) view.getTag();
}
// get AppBean
ProcessInfoBean bean = this.getItem(position);
// set value
item.ivIcon.setImageDrawable(bean.getIcon());
item.tvTitle.setText(bean.getAppName());
item.tvSize.setText(Formatter.formatFileSize(context, bean.getMemory()));
item.cb.setChecked(checkeds.get(position));// update checked
// set checkbox listener, can't use checked change event, it would effect setChecked
item.cb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CheckBox cb = (CheckBox) v;
checkeds.set(position, cb.isChecked());
System.out.println("position:" + position + " isChecked:" + cb.isChecked());
}
});
return view;
}
/**
* the the item bean
*
* @param position
* @return return item bean if success, null otherwise
*/
@Override
public ProcessInfoBean getItem(int position) {
ProcessInfoBean bean = null;
// the type label, return null
if (0 == position || userApps.size() + 1 == position)
return bean;
if (position < userApps.size() + 1) {
// user apps
bean = userApps.get(position - 1);// for example, position is 4, then the bean position should be 3
} else {
// system apps
/*
0 user app
1 up1
2 up2
3 system app
4 sp1
5 sp2
*/
bean = systemApps.get(position - userApps.size() - 2);// for example, user apps count is 2, position is 5, then bean position is 1
}
return bean;
}
@Override
public long getItemId(int position) {
return 0;
}
}
}