/*
* 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.modules.appmanager;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.widget.Toast;
import org.namelessrom.devicecontrol.R;
import org.namelessrom.devicecontrol.utils.AppHelper;
import java.lang.ref.WeakReference;
import at.amartinz.execution.BusyBox;
import at.amartinz.execution.Command;
import at.amartinz.execution.RootShell;
import timber.log.Timber;
public class AppItem {
private final PackageInfo pkgInfo;
private final ApplicationInfo appInfo;
private final String label;
private boolean enabled = false;
public interface DisableEnableListener {
void OnDisabledOrEnabled();
}
public interface UninstallListener {
void OnUninstallComplete();
}
public AppItem(final PackageInfo info, final String label) {
this.pkgInfo = info;
this.appInfo = info.applicationInfo;
this.label = label;
this.enabled = (appInfo != null && appInfo.enabled);
}
public String getLabel() { return label; }
public PackageInfo getPackageInfo() { return pkgInfo; }
public ApplicationInfo getApplicationInfo() { return appInfo; }
public String getPackageName() { return pkgInfo.packageName; }
public String getVersion() {
return String.format("%s (%s)", pkgInfo.versionName, pkgInfo.versionCode);
}
public boolean isSystemApp() {
return isSystemApp(appInfo);
}
public static boolean isSystemApp(ApplicationInfo applicationInfo) {
final int mask = (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP);
return ((applicationInfo.flags & mask) != 0);
}
public boolean isEnabled() { return enabled; }
public static boolean isEnabled(ApplicationInfo applicationInfo) {
return (applicationInfo != null && applicationInfo.enabled);
}
public void setEnabled(final boolean enabled) { this.enabled = enabled; }
public boolean launchActivity(Activity activity) {
return AppItem.launchActivity(activity, this);
}
public static boolean launchActivity(Activity activity, AppItem appItem) {
Intent i = activity.getPackageManager().getLaunchIntentForPackage(appItem.getPackageName());
if (i != null) {
try {
activity.startActivity(i);
return true;
} catch (ActivityNotFoundException ignored) { }
}
return false;
}
public void disable(final DisableEnableListener listener) {
disableOrEnable(String.format("pm disable %s 2> /dev/null;", pkgInfo.packageName), listener);
}
public void enable(final DisableEnableListener listener) {
disableOrEnable(String.format("pm enable %s 2> /dev/null;", pkgInfo.packageName), listener);
}
public void disableOrEnable(final DisableEnableListener listener) {
if (enabled) {
disable(listener);
return;
}
enable(listener);
}
private void disableOrEnable(String cmd, final DisableEnableListener listener) {
final Command command = new DisableEnableCommand(cmd, listener);
RootShell.fireAndForget(command);
}
public boolean isRunning(Context context) {
return AppItem.isRunning(context, this);
}
public static boolean isRunning(Context context, AppItem appItem) {
return AppHelper.isAppRunning(context, appItem.getPackageName());
}
public void forceStop(Context context) {
AppItem.forceStop(context, this);
}
public static void forceStop(Context context, AppItem appItem) {
AppHelper.killProcess(context, appItem.getPackageName());
}
public void uninstall(Activity activity, UninstallListener listener) {
AppItem.uninstall(activity, this, listener);
}
public void uninstall(Activity activity, UninstallListener listener, boolean withFeedback) {
AppItem.uninstall(activity, this, listener, withFeedback);
}
public void clearCache(Context context) {
AppItem.clearCache(context, this);
}
public static void clearCache(Context context, AppItem appItem) {
final PackageManager packageManager = context.getPackageManager();
AppHelper.clearCache(packageManager, appItem.getPackageName());
}
public void clearData(Context context) {
AppItem.clearData(context, this);
}
public static void clearData(Context context, AppItem appItem) {
final PackageManager packageManager = context.getPackageManager();
AppHelper.clearData(packageManager, appItem.getPackageName());
}
public static void uninstall(final Activity activity, final AppItem appItem, final UninstallListener listener) {
uninstall(activity, appItem, listener, true);
}
public static void uninstall(final Activity activity, final AppItem appItem,
final UninstallListener listener, final boolean withFeedback) {
// try via native package manager api
AppHelper.uninstallPackage(activity.getPackageManager(), appItem.getPackageName());
// build our command
final StringBuilder sb = new StringBuilder();
if (appItem.isSystemApp()) {
sb.append(BusyBox.callBusyBoxApplet("mount", "-o rw,remount /system;"));
}
sb.append(String.format("rm -rf %s;", appItem.getApplicationInfo().publicSourceDir));
sb.append(String.format("rm -rf %s;", appItem.getApplicationInfo().sourceDir));
sb.append(String.format("rm -rf %s;", appItem.getApplicationInfo().dataDir));
sb.append("sync;");
if (appItem.isSystemApp()) {
sb.append(BusyBox.callBusyBoxApplet("mount", "-o ro,remount /system;"));
}
sb.append("sync;echo \"Done!\";");
final String cmd = sb.toString();
Timber.v("Created command: %s", cmd);
// create the dialog (will not be shown for a long amount of time though)
new UninstallAsyncTask(withFeedback, activity, appItem, cmd, listener).execute();
}
private static class DisableEnableCommand extends Command {
private final DisableEnableListener listener;
DisableEnableCommand(String cmd, DisableEnableListener listener) {
super(cmd);
this.listener = listener;
}
@Override public void onCommandTerminated(int id, String reason) {
super.onCommandTerminated(id, reason);
if (listener != null) {
listener.OnDisabledOrEnabled();
}
}
@Override public void onCommandCompleted(int id, int exitCode) {
super.onCommandCompleted(id, exitCode);
if (listener != null) {
listener.OnDisabledOrEnabled();
}
}
}
private static class UninstallAsyncTask extends AsyncTask<Void, Void, Void> {
private final boolean withFeedback;
private final WeakReference<Activity> activityReference;
private final AppItem appItem;
private final String cmd;
private final UninstallListener listener;
private final ProgressDialog dialog;
UninstallAsyncTask(boolean withFeedback, Activity activity, AppItem appItem, String cmd, UninstallListener listener) {
this.withFeedback = withFeedback;
this.activityReference = new WeakReference<>(activity);
this.appItem = appItem;
this.cmd = cmd;
this.listener = listener;
if (this.withFeedback) {
this.dialog = new ProgressDialog(activity);
} else {
this.dialog = null;
}
}
@Override protected void onPreExecute() {
if (dialog != null) {
final Activity activity = activityReference.get();
if (activity != null) {
dialog.setTitle(R.string.uninstalling);
dialog.setMessage(activity.getString(R.string.applying_wait));
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
dialog.setCancelable(false);
dialog.show();
}
}
}
@Override protected Void doInBackground(Void... voids) {
final String uninstallCmd = String.format("pm uninstall %s;sync;", appItem.getPackageName());
RootShell.fireAndForget(uninstallCmd);
RootShell.fireAndForget(cmd);
// fake delay to make all more smooth
try {
Thread.sleep(750);
} catch (Exception ignored) { }
return null;
}
@Override protected void onPostExecute(Void aVoid) {
if (withFeedback) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
final Activity activity = activityReference.get();
if (activity != null) {
Toast.makeText(activity, activity.getString(R.string.uninstall_success, appItem.getLabel()),
Toast.LENGTH_SHORT).show();
}
}
if (listener != null) {
listener.OnUninstallComplete();
}
}
}
}