Java tutorial
/* * Copyright (C) 2013 - 2014 Alexander "Evisceration" Martinz * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package org.namelessrom.devicecontrol.appmanager; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import android.content.res.Resources; import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NavUtils; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.astuetz.PagerSlidingTabStrip; import com.github.mikephil.charting.charts.PieChart; import com.github.mikephil.charting.components.Legend; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.PieData; import com.github.mikephil.charting.data.PieDataSet; import com.github.mikephil.charting.utils.ColorTemplate; import com.stericson.roottools.RootTools; import com.stericson.roottools.execution.CommandCapture; import org.namelessrom.devicecontrol.Application; import org.namelessrom.devicecontrol.Logger; import org.namelessrom.devicecontrol.R; import org.namelessrom.devicecontrol.activities.BaseActivity; import org.namelessrom.devicecontrol.appmanager.permissions.AppSecurityPermissions; import org.namelessrom.devicecontrol.objects.AppItem; import org.namelessrom.devicecontrol.objects.PackageStatsObserver; import org.namelessrom.devicecontrol.utils.AppHelper; import org.namelessrom.devicecontrol.utils.Utils; import java.util.ArrayList; public class AppDetailsActivity extends BaseActivity implements PackageStatsObserver.OnPackageStatsListener, View.OnClickListener { public static final String ARG_FROM_ACTIVITY = "arg_from_activity"; public static final String ARG_PACKAGE_NAME = "arg_package_name"; private static final int DIALOG_TYPE_DISABLE = 0; private static final int DIALOG_TYPE_UNINSTALL = 1; private static final Handler mHandler = new Handler(); private final PackageManager mPm = Application.get().getPackageManager(); private AppItem mAppItem; private View mAppDetailsContainer; private View mAppDetailsError; private LinearLayout mAppContainer; private ImageView mAppIcon; private TextView mAppLabel; private TextView mAppPackage; private TextView mAppVersion; private Button mForceStop; private Button mUninstall; private PieChart mCacheGraph; private LinearLayout mCacheInfo; private Toast mToast; @Override protected void onNewIntent(final Intent intent) { super.onNewIntent(intent); setIntent(intent); } @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_app_details); // Setup toolbar final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); final Intent i = getIntent(); if (i != null && i.hasExtra(ARG_FROM_ACTIVITY) && getSupportActionBar() != null) { getSupportActionBar().setDisplayHomeAsUpEnabled(true); } final String[] titles = { getString(R.string.app_details) }; final AppPagerAdapter adapter = new AppPagerAdapter(titles); final ViewPager viewPager = (ViewPager) findViewById(R.id.pager); viewPager.setAdapter(adapter); final PagerSlidingTabStrip tabHost = (PagerSlidingTabStrip) findViewById(R.id.tabHost); // TODO: make it visible once used tabHost.setVisibility(View.GONE); //tabHost.setViewPager(viewPager); mAppDetailsContainer = findViewById(R.id.app_details_container); mAppDetailsError = findViewById(R.id.app_details_error); mAppContainer = (LinearLayout) findViewById(R.id.item_app); // allow to launch app via pressing on the icon mAppIcon = (ImageView) findViewById(R.id.app_icon); mAppIcon.setOnClickListener(this); mAppLabel = (TextView) findViewById(R.id.app_label); mAppPackage = (TextView) findViewById(R.id.app_package); mAppVersion = (TextView) findViewById(R.id.app_version); mCacheGraph = (PieChart) findViewById(R.id.app_cache_graph); mCacheInfo = (LinearLayout) findViewById(R.id.app_cache_info_container); mForceStop = (Button) findViewById(R.id.app_force_stop); ViewCompat.setElevation(mForceStop, 2.0f); mForceStop.setOnClickListener(this); mUninstall = (Button) findViewById(R.id.app_uninstall); ViewCompat.setElevation(mUninstall, 2.0f); mUninstall.setOnClickListener(this); final Button clearCache = (Button) findViewById(R.id.clear_cache); ViewCompat.setElevation(clearCache, 2.0f); clearCache.setOnClickListener(this); final Button clearData = (Button) findViewById(R.id.clear_data); ViewCompat.setElevation(clearData, 2.0f); clearData.setOnClickListener(this); } @Override protected void onResume() { super.onResume(); final String packageName = getTargetPackageName(getIntent()); if (!TextUtils.isEmpty(packageName)) { PackageInfo info = null; try { info = mPm.getPackageInfo(packageName, 0); } catch (Exception ignored) { } if (info != null && info.applicationInfo != null) { mAppItem = new AppItem(info, String.valueOf(info.applicationInfo.loadLabel(mPm)), info.applicationInfo.loadIcon(mPm)); } } setupCacheGraph(); refreshAppDetails(); } @Nullable private String getTargetPackageName(@Nullable Intent intent) { String packageName = null; Bundle args = null; if (intent != null) { args = intent.getExtras(); packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; } if (packageName == null) { intent = (args == null) ? getIntent() : (Intent) args.getParcelable("intent"); if (intent != null && intent.getData() != null) { packageName = intent.getData().getSchemeSpecificPart(); } } Logger.i(this, "packageName: %s", String.valueOf(packageName)); return packageName; } @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.menu_app_details, menu); if (mAppItem != null) { if (!AppHelper.isPlayStoreInstalled()) { menu.removeItem(R.id.menu_action_play_store); } // prevent disabling Device Control if (Application.get().getPackageName().equals(mAppItem.getPackageName())) { menu.removeItem(R.id.menu_app_disable); } final MenuItem disable = menu.findItem(R.id.menu_app_disable); if (disable != null) { disable.setTitle(mAppItem.isEnabled() ? R.string.disable : R.string.enable); } } else { menu.removeItem(R.id.menu_action_play_store); menu.removeItem(R.id.menu_app_disable); } return true; } @Override public boolean onOptionsItemSelected(final MenuItem item) { final int id = item.getItemId(); switch (id) { case android.R.id.home: { NavUtils.navigateUpFromSameTask(this); return true; } case R.id.menu_action_refresh: { refreshAppDetails(); return true; } case R.id.menu_action_play_store: { AppHelper.showInPlaystore("market://details?id=" + mAppItem.getPackageName()); return true; } case R.id.menu_app_disable: { disableApp(); return true; } } return super.onOptionsItemSelected(item); } @Override public void onClick(@NonNull View v) { final int id = v.getId(); switch (id) { case R.id.app_force_stop: { forceStopApp(); break; } case R.id.app_uninstall: { uninstallApp(); break; } case R.id.clear_cache: { clearAppCache(); break; } case R.id.clear_data: { clearAppData(); break; } case R.id.app_icon: { Intent i = getPackageManager().getLaunchIntentForPackage(mAppItem.getPackageName()); if (i != null) { try { startActivity(i); return; } catch (ActivityNotFoundException anfe) { Logger.e(this, "Could not launch activity", anfe); } } if (mToast != null) { mToast.cancel(); } mToast = Toast.makeText(this, R.string.could_not_launch_activity, Toast.LENGTH_SHORT); mToast.show(); break; } } } private void setupCacheGraph() { mCacheGraph.setDescription(""); mCacheGraph.setHoleRadius(60f); mCacheGraph.setDrawHoleEnabled(true); mCacheGraph.setTransparentCircleRadius(65f); mCacheGraph.setDrawSliceText(false); mCacheGraph.setDrawMarkerViews(false); mCacheGraph.setDrawCenterText(true); mCacheGraph.setTouchEnabled(false); final int color = Application.get().isDarkTheme() ? R.color.dark_background : R.color.light_background; mCacheGraph.setBackgroundResource(color); mCacheGraph.animateXY(1000, 1000); } private void refreshAppDetails() { if (mAppItem == null) { mAppDetailsContainer.setVisibility(View.GONE); mAppDetailsError.setVisibility(View.VISIBLE); invalidateOptionsMenu(); return; } mAppDetailsContainer.setVisibility(View.VISIBLE); mAppDetailsError.setVisibility(View.GONE); mAppIcon.setImageDrawable(mAppItem.getIcon()); mAppLabel.setText(mAppItem.getLabel()); mAppPackage.setText(mAppItem.getPackageName()); mAppContainer .setBackgroundResource(mAppItem.isEnabled() ? android.R.color.transparent : R.color.darker_gray); final int color = mAppItem.isSystemApp() ? getResources().getColor(R.color.red_middle) : Application.get().isDarkTheme() ? Color.WHITE : Color.BLACK; mAppLabel.setTextColor(color); final String version = String.format("%s (%s)", mAppItem.getPackageInfo().versionName, mAppItem.getPackageInfo().versionCode); mAppVersion.setText(version); setupPermissionsView(); // prevent uninstalling of Device Control mUninstall.setEnabled(!TextUtils.equals(Application.get().getPackageName(), mAppItem.getPackageName())); AppHelper.getSize(mPm, this, mAppItem.getPackageName()); refreshAppControls(); invalidateOptionsMenu(); } private void setupPermissionsView() { final LinearLayout permissionsView = (LinearLayout) findViewById(R.id.permissions_section); final boolean unsupported = Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN; if (unsupported) { permissionsView.setVisibility(View.GONE); return; } AppSecurityPermissions asp = new AppSecurityPermissions(this, mAppItem.getPackageName()); // Make the security sections header visible LinearLayout securityList = (LinearLayout) findViewById(R.id.security_settings_list); securityList.removeAllViews(); securityList.addView(asp.getPermissionsView()); // If this app is running under a shared user ID with other apps, // update the description to explain this. String[] packages = mPm.getPackagesForUid(mAppItem.getApplicationInfo().uid); if (packages != null && packages.length > 1) { final ArrayList<CharSequence> pnames = new ArrayList<>(); for (final String pkg : packages) { if (mAppItem.getPackageName().equals(pkg)) { continue; } try { ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0); pnames.add(ainfo.loadLabel(mPm)); } catch (PackageManager.NameNotFoundException ignored) { } } final int N = pnames.size(); if (N > 0) { final Resources res = getResources(); String appListStr; if (N == 1) { appListStr = pnames.get(0).toString(); } else if (N == 2) { appListStr = res.getString(R.string.join_two_items, pnames.get(0), pnames.get(1)); } else { appListStr = pnames.get(N - 2).toString(); for (int i = N - 3; i >= 0; i--) { appListStr = res.getString( i == 0 ? R.string.join_many_items_first : R.string.join_many_items_middle, pnames.get(i), appListStr); } appListStr = res.getString(R.string.join_many_items_last, appListStr, pnames.get(N - 1)); } final TextView descr = (TextView) findViewById(R.id.security_settings_desc); descr.setText(res.getString(R.string.security_settings_desc_multi, mAppItem.getApplicationInfo().loadLabel(mPm), appListStr)); } } } private void refreshAppControls() { mForceStop.setEnabled(AppHelper.isAppRunning(mAppItem.getPackageName())); } private void forceStopApp() { AppHelper.killProcess(mAppItem.getPackageName()); mHandler.postDelayed(new Runnable() { @Override public void run() { refreshAppControls(); } }, 500); } private void disableApp() { showConfirmationDialog(DIALOG_TYPE_DISABLE); } private void uninstallApp() { showConfirmationDialog(DIALOG_TYPE_UNINSTALL); } private void clearAppData() { AppHelper.clearData(mPm, mAppItem.getPackageName()); mHandler.postDelayed(mClearRunnable, 500); } private void clearAppCache() { AppHelper.clearCache(mPm, mAppItem.getPackageName()); mHandler.postDelayed(mClearRunnable, 500); } private void disable() { if (mAppItem == null) return; String cmd; if (mAppItem.isEnabled()) { cmd = "pm disable " + mAppItem.getPackageName() + " 2> /dev/null"; } else { cmd = "pm enable " + mAppItem.getPackageName() + " 2> /dev/null"; } final CommandCapture commandCapture = new CommandCapture(0, cmd) { @Override public void commandTerminated(int id, String reason) { updateAppEnabled(); } @Override public void commandCompleted(int id, int exitcode) { updateAppEnabled(); } }; try { RootTools.getShell(true).add(commandCapture); } catch (Exception ignored) { /* ignored */ } } private void uninstall() { // try via native package manager api AppHelper.uninstallPackage(mPm, mAppItem.getPackageName()); // build our command final StringBuilder sb = new StringBuilder(); sb.append(String.format("pm uninstall %s;", mAppItem.getPackageName())); if (mAppItem.isSystemApp()) { sb.append("busybox mount -o rw,remount /system;"); } sb.append(String.format("rm -rf %s;", mAppItem.getApplicationInfo().publicSourceDir)); sb.append(String.format("rm -rf %s;", mAppItem.getApplicationInfo().sourceDir)); sb.append(String.format("rm -rf %s;", mAppItem.getApplicationInfo().dataDir)); if (mAppItem.isSystemApp()) { sb.append("busybox mount -o ro,remount /system;"); } final String cmd = sb.toString(); Logger.v(this, cmd); // create the dialog (will not be shown for a long amount of time though) final ProgressDialog dialog; dialog = new ProgressDialog(this); dialog.setTitle(R.string.uninstalling); dialog.setMessage(getString(R.string.applying_wait)); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.setCancelable(false); new AsyncTask<Void, Void, Void>() { @Override protected void onPreExecute() { dialog.show(); } @Override protected Void doInBackground(Void... voids) { Utils.runRootCommand(cmd, true); return null; } @Override protected void onPostExecute(Void aVoid) { dialog.dismiss(); Toast.makeText(AppDetailsActivity.this, getString(R.string.uninstall_success, mAppItem.getLabel()), Toast.LENGTH_SHORT).show(); mAppItem = null; finish(); } }.execute(); } private void updateAppEnabled() { if (mAppItem == null) { return; } runOnUiThread(new Runnable() { @Override public void run() { invalidateOptionsMenu(); final boolean isEnabled = !mAppItem.isEnabled(); mAppItem.setEnabled(isEnabled); if (mAppContainer != null) { mAppContainer .setBackgroundResource(isEnabled ? android.R.color.transparent : R.color.darker_gray); } } }); } @Override public void onPackageStats(final PackageStats packageStats) { Logger.i(this, "onAppSizeEvent()"); if (packageStats == null) return; final long totalSize = packageStats.codeSize + packageStats.dataSize + packageStats.externalCodeSize + packageStats.externalDataSize + packageStats.externalMediaSize + packageStats.externalObbSize + packageStats.cacheSize + packageStats.externalCacheSize; if (mCacheInfo != null) { mCacheInfo.removeAllViews(); mCacheInfo.addView(addCacheWidget(R.string.total, AppHelper.convertSize(totalSize))); mCacheInfo.addView(addCacheWidget(R.string.app, AppHelper.convertSize(packageStats.codeSize))); mCacheInfo.addView( addCacheWidget(R.string.ext_app, AppHelper.convertSize(packageStats.externalCodeSize))); mCacheInfo.addView(addCacheWidget(R.string.data, AppHelper.convertSize(packageStats.dataSize))); mCacheInfo.addView( addCacheWidget(R.string.ext_data, AppHelper.convertSize(packageStats.externalDataSize))); mCacheInfo.addView( addCacheWidget(R.string.ext_media, AppHelper.convertSize(packageStats.externalMediaSize))); mCacheInfo .addView(addCacheWidget(R.string.ext_obb, AppHelper.convertSize(packageStats.externalObbSize))); mCacheInfo.addView(addCacheWidget(R.string.cache, AppHelper.convertSize(packageStats.cacheSize))); mCacheInfo.addView( addCacheWidget(R.string.ext_cache, AppHelper.convertSize(packageStats.externalCacheSize))); } if (mCacheGraph != null) { final ArrayList<Entry> sliceList = new ArrayList<>(); final ArrayList<String> textList = new ArrayList<>(); // App --------------------------------------------------------------------------------- textList.add(getString(R.string.app)); sliceList.add(new Entry(packageStats.codeSize + packageStats.externalCodeSize, 0)); // Data -------------------------------------------------------------------------------- textList.add(getString(R.string.data)); sliceList.add(new Entry(packageStats.dataSize + packageStats.externalDataSize, 1)); // External ------------------------------------------------------------------------ textList.add(getString(R.string.ext)); sliceList.add(new Entry(packageStats.externalMediaSize + packageStats.externalObbSize, 2)); // Cache ------------------------------------------------------------------------------- textList.add(getString(R.string.cache)); sliceList.add(new Entry(packageStats.cacheSize + packageStats.externalCacheSize, 3)); final PieDataSet dataSet = new PieDataSet(sliceList, getString(R.string.app_size)); dataSet.setSliceSpace(5f); dataSet.setColors(ColorTemplate.createColors(ColorTemplate.VORDIPLOM_COLORS)); final PieData data = new PieData(textList, dataSet); data.getDataSet().setDrawValues(false); mCacheGraph.setData(data); mCacheGraph.highlightValues(null); mCacheGraph.setCenterText( String.format("%s\n%s", getString(R.string.total), AppHelper.convertSize(totalSize))); // setup legend final Legend l = mCacheGraph.getLegend(); l.setPosition(Legend.LegendPosition.BELOW_CHART_CENTER); l.setXEntrySpace(7f); l.setYEntrySpace(5f); final int color = Application.get().isDarkTheme() ? Color.WHITE : Color.BLACK; l.setTextColor(color); // we are ready mCacheGraph.animateXY(1000, 1000); } } private View addCacheWidget(final int txtId, final String text) { final View v = getLayoutInflater().inflate(R.layout.widget_app_cache, mCacheInfo, false); final TextView tvLeft = (TextView) v.findViewById(R.id.widget_app_cache_left); tvLeft.setText(txtId); final TextView tvRight = (TextView) v.findViewById(R.id.widget_app_cache_right); tvRight.setText(text); return v; } private final Runnable mClearRunnable = new Runnable() { @Override public void run() { try { AppHelper.getSize(mPm, AppDetailsActivity.this, mAppItem.getPackageName()); } catch (Exception e) { Logger.e(this, "AppHelper.getSize(): " + e); } } }; private void showConfirmationDialog(final int type) { if (mAppItem == null) return; final AlertDialog.Builder builder = new AlertDialog.Builder(this); final String message; final int positiveButton; switch (type) { case DIALOG_TYPE_DISABLE: { message = getString(mAppItem.isEnabled() ? R.string.disable_msg : R.string.enable_msg, mAppItem.getLabel()); positiveButton = mAppItem.isEnabled() ? R.string.disable : R.string.enable; break; } case DIALOG_TYPE_UNINSTALL: { if (mAppItem.isSystemApp()) { message = String.format("%s\n\n%s", getString(R.string.uninstall_msg, mAppItem.getLabel()), getString(R.string.uninstall_msg_system_app)); } else { message = getString(R.string.uninstall_msg, mAppItem.getLabel()); } positiveButton = android.R.string.yes; break; } default: { return; } } builder.setMessage(message).setPositiveButton(positiveButton, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch (type) { case DIALOG_TYPE_DISABLE: { disable(); break; } case DIALOG_TYPE_UNINSTALL: { uninstall(); break; } default: { dialog.dismiss(); break; } } } }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.show(); } class AppPagerAdapter extends PagerAdapter { private final String[] mTitles; public AppPagerAdapter(final String[] titles) { mTitles = titles; } @Override public String getPageTitle(final int position) { return mTitles[position]; } @Override public Object instantiateItem(ViewGroup container, int position) { final int resId; switch (position) { default: case 0: { resId = R.id.page_app_details; break; } // TODO: make visible once done //case 1: { // resId = R.id.page_app_details; // break; //} } return findViewById(resId); } @Override public int getCount() { return mTitles.length; } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } } }