package com.ittianyu.mobileguard.activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; 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 com.ittianyu.mobileguard.R; import com.ittianyu.mobileguard.activity.base.BaseActivityUpEnableWithMenu; import com.ittianyu.mobileguard.domain.AppInfoBean; import com.ittianyu.mobileguard.engine.AppManagerEngine; import com.stericson.RootTools.RootTools; import com.stericson.RootTools.RootToolsException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; /** * Managing all installed app. Include system app and user app. */ public class SoftwareManagerActivity extends BaseActivityUpEnableWithMenu { // view private TextView tvRomFreeSpace; private TextView tvSdCardFreeSpace; private TextView tvTypeLabel; private ListView lvApp; private ProgressBar pbLoading; // data private List<AppInfoBean> systemApps = new ArrayList<>(); private List<AppInfoBean> userApps = new ArrayList<>(); private List<Boolean> checkeds = new ArrayList<>();// represent which item was checked private AppAdapter adapter = new AppAdapter(); private AppRemovedReceiver uninstallReceiver = new AppRemovedReceiver(); // logic private boolean root; // thread operation private Thread initDateThread; /** * construct method */ public SoftwareManagerActivity() { super(R.string.software_manager, R.menu.menu_software_manager); } /** * 1 */ @Override protected void initView() { setContentView(R.layout.activity_software_manager); // bind view tvRomFreeSpace = (TextView) findViewById(R.id.tv_rom_free_sapce); tvSdCardFreeSpace = (TextView) findViewById(R.id.tv_sd_card_free_sapce); tvTypeLabel = (TextView) findViewById(R.id.tv_type_label); lvApp = (ListView) findViewById(R.id.lv_app); pbLoading = (ProgressBar) findViewById(R.id.pb_loading); // set adapter. This method must run before initData lvApp.setAdapter(adapter); } /** * 2 */ @Override protected void initData() { // check whether root root = RootTools.isRootAvailable(); // get free space long romFreeSpace = AppManagerEngine.getRomFreeSpace(); long sdCardFreeSpace = AppManagerEngine.getSdCardFreeSpace(); // set space info to TextView tvRomFreeSpace.setText(getString(R.string.rom_free_space) + Formatter.formatFileSize(this, romFreeSpace)); tvSdCardFreeSpace.setText(getString(R.string.sd_card_free_space) + Formatter.formatFileSize(this, sdCardFreeSpace)); // if the thread is running, no need to start a new thread if (null != initDateThread && initDateThread.isAlive()) { return; } // start a new thread // the following operator are time consuming, need run on child thread initDateThread = new Thread() { @Override public void run() { // get apps info AppManagerEngine.getInstalledAppInfo( SoftwareManagerActivity.this, new AppManagerEngine.AppInfoListener() { @Override public void onGetInfoCompleted(List<AppInfoBean> apps) { initAppsInfo(apps); } }); } }; initDateThread.start(); } /** * init apps info * * @param apps */ private void initAppsInfo(final List<AppInfoBean> apps) { // change data and notify need run on the same handler // prevent Exception "The content of the adapter has changed but ListView did not receive a notification." runOnUiThread(new Runnable() { @Override public void run() { // clear list before add systemApps.clear(); userApps.clear(); checkeds.clear(); // add to list for (AppInfoBean app : apps) { if (app.isSystemApp()) { systemApps.add(app); } else { userApps.add(app); } checkeds.add(false); } // because of two label, so need more checkeds.add(false); checkeds.add(false); // refresh ui 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 item click listener for starting app info activity lvApp.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { AppInfoBean bean = (AppInfoBean) lvApp.getItemAtPosition(position); // System.out.println(bean.getName()); Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS", Uri.parse("package:" + bean.getPackageName())); startActivity(intent); } }); // register app removed receiver IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); registerReceiver(uninstallReceiver, filter); } /** * unregister app removed receiver */ @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(uninstallReceiver); } /** * when receiver the app uninstalled, notify refresh ListView */ private class AppRemovedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { initData(); System.out.println("AppRemovedReceiver"); } } /** * 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 = SoftwareManagerActivity.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 AppInfoBean bean = this.getItem(position); // set value item.ivIcon.setImageDrawable(bean.getIcon()); item.tvTitle.setText(bean.getName()); item.tvSize.setText(Formatter.formatFileSize(context, bean.getSize())); // when app is user app or system has root, need set cb enable and set checked and listener if (!bean.isSystemApp() || root) { item.cb.setEnabled(true); 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()); } }); } else { item.cb.setEnabled(false); item.cb.setChecked(false);// system app can't uninstall } return view; } /** * the the item bean * * @param position * @return return item bean if success, null otherwise */ @Override public AppInfoBean getItem(int position) { AppInfoBean 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; } } /** * 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_uninstall: onUninstallSelectedApp(); break; } return super.onOptionsItemSelected(item); } /** * query user whether uninstall selected app */ private void onUninstallSelectedApp() { // create dialog new AlertDialog.Builder(this) .setTitle(R.string.tips) .setMessage(R.string.message_uninstall_app) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { uninstallSelectedApp(); } }) .setNegativeButton(R.string.cancel, null) .show(); } /** * uninstall selected apps */ private void uninstallSelectedApp() { for (int i = 1; i < checkeds.size(); i++) { // if not checked, run next if (!checkeds.get(i)) { continue; } // get app info AppInfoBean app = (AppInfoBean) lvApp.getItemAtPosition(i); if (null == app) continue; System.out.println(app.getName()); System.out.println(app.getApkPath()); // if system app and system have been root if (app.isSystemApp() && root) { // need to apply root access try { if (!RootTools.isAccessGiven()) continue; // change system dir access RootTools.sendShell("mount -o remount rw /system", 10000); // delete apk file RootTools.sendShell("rm -r " + app.getApkPath(), 10000); // restore system dir access RootTools.sendShell("mount -o remount r /system", 10000); } catch (TimeoutException e) { e.printStackTrace(); } catch (RootToolsException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } continue; } // uninstall user app Intent intent = new Intent(Intent.ACTION_DELETE, Uri.parse("package:" + app.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 if (root) { // it can select all when root for (int i = 1; i < checkeds.size(); i++) { checkeds.set(i, true); } } else { // can only select user app for (int i = 1; i <= userApps.size(); i++) { 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 uninstallItem = menu.findItem(R.id.m_uninstall); 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 uninstallItem.setEnabled(false); cancelAllItem.setEnabled(false); } else { // enable it uninstallItem.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; } } /** * just used for cache child view of AppListView Item */ class AppItem { ImageView ivIcon; TextView tvTitle; TextView tvSize; CheckBox cb; }