package de.theknut.xposedgelsettings.ui; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningAppProcessInfo; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Environment; import android.view.View; import android.widget.ImageView; import android.widget.NumberPicker; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.Theme; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import de.theknut.xposedgelsettings.R; import de.theknut.xposedgelsettings.hooks.Common; import eu.chainfire.libsuperuser.Shell; @SuppressLint("WorldReadableFiles") public class CommonUI { public static boolean LOADING_ICONPACK; public static Activity ACTIVITY; public static Bitmap bluredBackground = null; public static Context CONTEXT; public static int TextColor = -1; public static boolean AUTO_BLUR_IMAGE; public static boolean needFullReboot = false; private static Shell.Interactive rootSession; public static boolean NO_BACKGROUND_IMAGE; public static List<String> getIconPacks(Context context) { String[] sIconPackCategories = new String[] { "com.fede.launcher.THEME_ICONPACK", "com.anddoes.launcher.THEME", "com.teslacoilsw.launcher.THEME" }; String[] sIconPackActions = new String[] { "org.adw.launcher.THEMES", "com.gau.go.launcherex.theme" }; Intent i = new Intent(); List<String> packages = new ArrayList<String>(); PackageManager packageManager = context.getPackageManager(); for (String action : sIconPackActions) { i.setAction(action); for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { if (!packages.contains(r.activityInfo.packageName)) packages.add(r.activityInfo.packageName); } } i = new Intent(Intent.ACTION_MAIN); for (String category : sIconPackCategories) { i.addCategory(category); for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { if (!packages.contains(r.activityInfo.packageName)) packages.add(r.activityInfo.packageName); } i.removeCategory(category); } return packages; } // http://stackoverflow.com/questions/3035692/how-to-convert-a-drawable-to-a-bitmap public static Bitmap drawableToBitmap (Drawable drawable) { if (drawable == null) return null; if (drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); } Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } public static View setBackground(View rootView, int layout) { if (layout != R.id.welcomebackground) { return rootView; } if (CommonUI.AUTO_BLUR_IMAGE && CommonUI.setBluredBackground(CONTEXT, rootView, layout)) { return rootView; } try { ImageView background = (ImageView) rootView.findViewById(layout); background.setImageResource(R.drawable.wall); } catch (RuntimeException e) { } catch (Exception e) { } return rootView; } public static boolean setBluredBackground (Context context, View v, int imageViewId) { ImageView background = (ImageView) v.findViewById(imageViewId); if (CommonUI.bluredBackground == null) { String pathBackground = Environment.getExternalStorageDirectory().getPath() + "/XposedGELSettings/bluredbackground.png"; File fileBackground = new File(pathBackground); if (fileBackground.exists()) { CommonUI.bluredBackground = BitmapFactory.decodeFile(pathBackground); background.setImageBitmap(CommonUI.bluredBackground); } else { return false; } } else { background.setImageBitmap(CommonUI.bluredBackground); } return true; } public static void setCustomStyle(View view, boolean setTitle, boolean setSummary) { if (setTitle) { //TextView title = ((TextView) view.findViewById(android.R.id.title)); //title.setTextColor(Color.parseColor("#F4F4F4")); //title.setTextAppearance(CommonUI.CONTEXT, R.style.ShadowText); } if (setSummary) { //TextView summary = ((TextView) view.findViewById(android.R.id.summary)); //summary.setTextColor(Color.parseColor("#F4F4F4")); //summary.setTextAppearance(CommonUI.CONTEXT, R.style.ShadowText); } } public static List<ResolveInfo> getAllApps() { // load all apps which are listed in the app drawer PackageManager pm = CONTEXT.getPackageManager(); final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); final List<ResolveInfo> apps = pm.queryIntentActivities(mainIntent, 0); Collections.sort(apps, new ResolveInfo.DisplayNameComparator(pm)); return apps; } public static void restartLauncherOrDevice() { if (needFullReboot) { new MaterialDialog.Builder(CONTEXT) .theme(Theme.DARK) .title(R.string.alert_reboot_needed_title) .content(R.string.alert_reboot_needed_summary) .positiveText(R.string.full_reboot) .neutralText(R.string.hot_reboot) .negativeText(R.string.launcher_reboot) .callback(new MaterialDialog.ButtonCallback() { @Override public void onNeutral(MaterialDialog materialDialog) { if (!InAppPurchase.isPremium) { Toast.makeText(CONTEXT, CONTEXT.getString(R.string.toast_donate_only), Toast.LENGTH_SHORT).show(); return; } openRootShell(new String[]{ "su", "-c", "killall system_server"}); } @Override public void onPositive(MaterialDialog materialDialog) { if (!InAppPurchase.isPremium) { Toast.makeText(CONTEXT, CONTEXT.getString(R.string.toast_donate_only), Toast.LENGTH_SHORT).show(); return; } openRootShell(new String[]{"su", "-c", "reboot now"}); } @Override public void onNegative(MaterialDialog materialDialog) { restartLauncher(); } }) .build() .show(); } else { restartLauncher(); } } public static boolean restartLauncher(boolean showToast) { ActivityManager am = (ActivityManager) CONTEXT.getSystemService(Context.ACTIVITY_SERVICE); String msg = CONTEXT.getString(R.string.killed); boolean neededRoot = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { for (String packageName : Common.PACKAGE_NAMES) { am.killBackgroundProcesses(packageName); } } else { List<RunningAppProcessInfo> processes = am.getRunningAppProcesses(); for (RunningAppProcessInfo process : processes) { if (Common.PACKAGE_NAMES.contains(process.processName)) { am.killBackgroundProcesses(process.processName); List<RunningAppProcessInfo> processesAfterKill = am.getRunningAppProcesses(); for (RunningAppProcessInfo processAfterKill : processesAfterKill) { if (processAfterKill.pid == process.pid) { // process wasn't killed for some reason // kill it with fire neededRoot = true; CommonUI.openRootShell(new String[]{"su", "kill -9 " + processAfterKill.pid}); } } if (!neededRoot) { msg += process.processName + "\n"; } } } } if (!neededRoot) { if (!msg.equals(CONTEXT.getString(R.string.killed)) && showToast) { msg = msg.substring(0, msg.lastIndexOf('\n')); Toast.makeText(CONTEXT, msg, Toast.LENGTH_LONG).show(); } } return true; } public static NumberPicker getNumberPicker(Context context, SharedPreferences sharedPrefs, String[] values, String key, String defVal) { NumberPicker numberPicker = new NumberPicker(context); numberPicker.setMinValue(0); numberPicker.setMaxValue(values.length - 1); numberPicker.setDisplayedValues(values); int currIdx = Arrays.asList(values).indexOf(sharedPrefs.getString(key, defVal)); numberPicker.setValue(currIdx == -1 ? 0 : currIdx); return numberPicker; } public static boolean restartLauncher() { return restartLauncher(true); } public static void restartActivity() { Intent mStartActivity = new Intent(CONTEXT, MainActivity.class); int mPendingIntentId = 0xBEEF; PendingIntent mPendingIntent = PendingIntent.getActivity(CONTEXT, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT); AlarmManager mgr = (AlarmManager) CONTEXT.getSystemService(Context.ALARM_SERVICE); mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 500, mPendingIntent); System.exit(0); } static boolean isPackageInstalled(String packagename, Context context) { PackageManager pm = context.getPackageManager(); try { pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES); return true; } catch (NameNotFoundException e) { return false; } } @SuppressWarnings("deprecation") static String getAppName(String prefKey) { final PackageManager pm = CONTEXT.getPackageManager(); String app = CONTEXT.getSharedPreferences(Common.PREFERENCES_NAME, Context.MODE_WORLD_READABLE).getString(prefKey + "_launch", ""); ApplicationInfo ai; try { ai = pm.getApplicationInfo(app, 0); } catch (final NameNotFoundException e) { ai = null; } return (String) (ai != null ? pm.getApplicationLabel(ai) : ""); } public static void openRootShell(final String[] command) { if (rootSession != null) { sendRootCommand(command); } else { // We're creating a progress dialog here because we want the user to wait. final ProgressDialog dialog = new ProgressDialog(CommonUI.ACTIVITY); dialog.setTitle(R.string.progress_requesting_root_title); dialog.setMessage(CONTEXT.getString(R.string.progress_requesting_root_summary)); dialog.setIndeterminate(true); dialog.setCancelable(false); dialog.show(); // start the shell in the background and keep it alive as long as the app is running rootSession = new Shell.Builder(). useSU(). setWantSTDERR(true). setWatchdogTimeout(5). setMinimalLogging(true). open(new Shell.OnCommandResultListener() { // Callback to report whether the shell was successfully started up @Override public void onCommandResult(int commandCode, int exitCode, List<String> output) { // note: this will FC if you rotate the phone while the dialog is up dialog.dismiss(); if (exitCode != Shell.OnCommandResultListener.SHELL_RUNNING) { Toast.makeText(CONTEXT, CONTEXT.getString(R.string.error_root_shell) + " " + exitCode, Toast.LENGTH_LONG).show(); } else { // Shell is up: send our first request sendRootCommand(command); } } }); } } private static void sendRootCommand(String[] command) { rootSession.addCommand(command, 0, new Shell.OnCommandResultListener() { public void onCommandResult(int commandCode, int exitCode, List<String> output) { if (exitCode < 0) { Toast.makeText(CONTEXT, CONTEXT.getString(R.string.error_root_shell) + " " + exitCode, Toast.LENGTH_LONG).show(); } else { if (output.size() == 0) { Toast.makeText(CONTEXT, CONTEXT.getString(R.string.success), Toast.LENGTH_LONG).show(); } else { Toast.makeText(CONTEXT, CONTEXT.getString(R.string.failed) + ": " + output, Toast.LENGTH_LONG).show(); } } } }); } public static int getGNLVersion(Context context) throws NameNotFoundException { return context.getPackageManager().getPackageInfo(Common.GEL_PACKAGE, 0).versionCode; } }