You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CustoMIUIzer/app/src/main/java/name/mikanoshi/customiuizer/utils/Helpers.java

2289 lines
81 KiB
Java

package name.mikanoshi.customiuizer.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
import android.Manifest;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.Application;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.PowerManager.WakeLock;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.provider.Settings;
import android.text.InputType;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import android.util.Log;
import android.util.LruCache;
import android.util.Pair;
import android.util.TypedValue;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParser;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import miui.app.AlertDialog;
import miui.os.SystemProperties;
import miui.util.HapticFeedbackUtil;
import name.mikanoshi.customiuizer.MainModule;
import name.mikanoshi.customiuizer.R;
import name.mikanoshi.customiuizer.SharedPrefsProvider;
import name.mikanoshi.customiuizer.mods.GlobalActions;
import name.mikanoshi.customiuizer.prefs.PreferenceCategoryEx;
@SuppressWarnings("WeakerAccess")
public class Helpers {
@SuppressLint("StaticFieldLeak")
public static Context mModuleContext = null;
public static final String modulePkg = "name.mikanoshi.customiuizer";
public static final String prefsName = "customiuizer_prefs";
public static final String prefsPath = "/data/user_de/0/" + modulePkg + "/shared_prefs";
public static final String prefsFile = prefsPath + "/" + prefsName + ".xml";
public static final String externalFolder = "/CustoMIUIzer/";
public static final String backupFile = "settings_backup";
public static final String logFile = "xposed_log";
public static final String versionFile = "xposed_version";
public static final String wallpaperFile = "lockscreen_wallpaper";
//public static final String xposedRepo = "https://code.highspec.ru/repo/full.xml.gz";
public static final String ANDROID_NS = "http://schemas.android.com/apk/res/android";
public static final String MIUIZER_NS = "http://schemas.android.com/apk/res-auto";
public static final String ACCESS_SECURITY_CENTER = "com.miui.securitycenter.permission.ACCESS_SECURITY_CENTER_PROVIDER";
public static final String NEW_MODS_SEARCH_QUERY = "\uD83C\uDD95";
public static SharedPreferences prefs = null;
public static String prefsPathCurrent = null;
public static String prefsFileCurrent = null;
public static ArrayList<AppData> shareAppsList = null;
public static ArrayList<AppData> openWithAppsList = null;
public static ArrayList<AppData> installedAppsList = null;
public static ArrayList<AppData> launchableAppsList = null;
public static ArrayList<ModData> allModsList = new ArrayList<ModData>();
public static int xposedVersion = 0;
public static final int markColor = Color.rgb(205, 73, 97);
public static final int markColorVibrant = Color.rgb(222, 45, 73);
public static final int REQUEST_PERMISSIONS_BACKUP = 1;
public static final int REQUEST_PERMISSIONS_RESTORE = 2;
public static final int REQUEST_PERMISSIONS_WIFI = 3;
public static final int REQUEST_PERMISSIONS_REPORT = 4;
public static LruCache<String, Bitmap> memoryCache = new LruCache<String, Bitmap>((int)(Runtime.getRuntime().maxMemory() / 1024) / 2) {
@Override
protected int sizeOf(String key, Bitmap icon) {
if (icon != null)
return icon.getAllocationByteCount() / 1024;
else
return 130 * 130 * 4 / 1024;
}
};
public static WakeLock mWakeLock;
public static ValueAnimator shimmerAnim;
public static boolean showNewMods = true;
public static boolean miuizerModuleActive = false;
public static final HashSet<String> newMods = new HashSet<String>(Arrays.asList(
"pref_key_various_hiddenfeatures_cat",
"pref_key_launcher_nozoomanim",
"pref_key_launcher_horizwidgetmargin"
));
public static final HashMap<String, String> l10nProgress = new HashMap<String, String>() {{
put("de", "86.6");
put("es", "98.7");
put("it", "98");
put("pt-BR", "94.7");
put("ru-RU", "100");
put("tr", "86.6");
put("uk-UK", "87.6");
put("zh-CN", "98.1");
put("fr", "24.9");
put("id", "13.1");
put("sk", "4.2");
}};
public static final HashSet<String> xposedManagers = new HashSet<String>(Arrays.asList(
"org.lsposed.manager",
"io.github.lsposed.manager",
"org.meowcat.edxposed.manager",
"com.solohsu.android.edxp.manager",
"de.robv.android.xposed.installer",
"me.weishu.exp"
));
public static final ArrayList<String> shortcutIcons = new ArrayList<String>();
public static Holidays currentHoliday = Holidays.NONE;
public enum Holidays {
NONE, NEWYEAR, LUNARNEWYEAR, PANDEMIC, CRYPTO
}
public enum SettingsType {
Preference, Edit
}
public enum AppAdapterType {
Default, Standalone, Mutli, CustomTitles, Activities
}
public enum ActionBarType {
HomeUp, Edit
}
public static class MimeType {
public static int IMAGE = 1;
public static int AUDIO = 2;
public static int VIDEO = 4;
public static int DOCUMENT = 8;
public static int ARCHIVE = 16;
public static int LINK = 32;
public static int OTHERS = 64;
public static int ALL = IMAGE | AUDIO | VIDEO | DOCUMENT | ARCHIVE | LINK | OTHERS;
}
public static int getSystemBackgroundColor(Context context) {
int black = Color.BLACK;
int white = Color.WHITE;
try {
black = context.getResources().getColor(context.getResources().getIdentifier("black", "color", "miui"), context.getTheme());
white = context.getResources().getColor(context.getResources().getIdentifier("white", "color", "miui"), context.getTheme());
} catch (Throwable ignore) {}
return isNightMode(context) ? black : white;
}
public static void setMiuiTheme(Activity act, int overrideTheme) {
setMiuiTheme(act, overrideTheme, false);
}
public static void setMiuiTheme(Activity act, int overrideTheme, boolean noBackground) {
int themeResId = 0;
try { themeResId = act.getResources().getIdentifier("Theme.DayNight", "style", "miui"); } catch (Throwable ignore) {}
if (themeResId == 0) themeResId = act.getResources().getIdentifier(isNightMode(act) ? "Theme.Dark" : "Theme.Light", "style", "miui");
act.setTheme(themeResId);
if (!is11()) act.getTheme().applyStyle(R.style.ActivityAnimation10, true);
act.getTheme().applyStyle(overrideTheme, true);
act.getWindow().setBackgroundDrawable(noBackground ? null : new ColorDrawable(getSystemBackgroundColor(act)));
}
public static void setMiuiCheckbox(CheckBox checkbox) {
checkbox.setBackground(null);
int btnResID = checkbox.getResources().getIdentifier(isNightMode(checkbox.getContext()) ? "btn_checkbox_dark" : "btn_checkbox_light", "drawable", "miui");
try {
checkbox.setButtonDrawable(btnResID == 0 ? R.drawable.btn_checkbox : btnResID);
} catch (Throwable t) {
checkbox.setButtonDrawable(R.drawable.btn_checkbox);
}
}
public static void setMiuiPrefItem(View item) {
item.setBackgroundResource(is11() ? R.drawable.list_item_bg : R.drawable.am_list_item_background);
TextView title = item.findViewById(android.R.id.title);
if (is12()) {
int resId = item.getResources().getIdentifier("preference_item_bg", "drawable", "miui");
if (resId != 0) item.setBackgroundResource(resId);
resId = item.getResources().getIdentifier("normal_text_size", "dimen", "miui");
if (resId != 0 && title != null) {
title.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
title.setTextSize(TypedValue.COMPLEX_UNIT_PX, item.getResources().getDimensionPixelSize(resId));
}
resId = item.getResources().getIdentifier("secondary_text_size", "dimen", "miui");
if (resId != 0) {
TextView summary = item.findViewById(android.R.id.summary);
TextView text1 = item.findViewById(android.R.id.text1);
TextView text2 = item.findViewById(android.R.id.text2);
int size = item.getResources().getDimensionPixelSize(resId);
if (summary != null) summary.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
if (text1 != null) text1.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
if (text2 != null) text2.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
}
if (title != null && "header".equals(title.getTag())) {
int resIdSize = item.getResources().getIdentifier("preference_category_text_size", "dimen", "miui");
if (resIdSize != 0) title.setTextSize(TypedValue.COMPLEX_UNIT_PX, item.getResources().getDimensionPixelSize(resIdSize));
}
int resIdLeft = item.getResources().getIdentifier("preference_item_padding_left", "dimen", "miui");
int resIdRight = item.getResources().getIdentifier("preference_item_padding_right", "dimen", "miui");
int resIdTop = item.getResources().getIdentifier("preference_item_padding_top", "dimen", "miui");
int resIdBottom = item.getResources().getIdentifier("preference_item_padding_bottom", "dimen", "miui");
int paddingLeft = resIdLeft == 0 ? item.getPaddingLeft() : item.getResources().getDimensionPixelSize(resIdLeft);
int paddingRight = resIdRight == 0 ? item.getPaddingRight() : item.getResources().getDimensionPixelSize(resIdRight);
int paddingTop = resIdTop == 0 ? item.getPaddingTop() : item.getResources().getDimensionPixelSize(resIdTop);
int paddingBottom = resIdBottom == 0 ? item.getPaddingBottom() : item.getResources().getDimensionPixelSize(resIdBottom);
item.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
@SuppressWarnings("ConstantConditions")
public static void detectHoliday() {
currentHoliday = Holidays.NONE;
String opt = prefs.getString("pref_key_miuizer_holiday", "0");
int holiday = Integer.parseInt(opt);
if (holiday > 0) currentHoliday = Holidays.values()[holiday];
if (holiday == 0) {
Calendar cal = Calendar.getInstance();
int month = cal.get(Calendar.MONTH);
int monthDay = cal.get(Calendar.DAY_OF_MONTH);
int year = cal.get(Calendar.YEAR);
// Lunar NY
if ((month == 0 && monthDay > 15) || month == 1) currentHoliday = Holidays.LUNARNEWYEAR;
// NY
else if (month == 0 || month == 11) currentHoliday = Holidays.NEWYEAR;
// COVID19
else if (year <= 2020) currentHoliday = Holidays.PANDEMIC;
// Crypto
else if (year == 2021 || year == 2022) currentHoliday = Holidays.CRYPTO;
}
}
public static boolean is10() {
return SystemProperties.getInt("ro.miui.ui.version.code", 7) <= 8;
}
public static boolean is11() {
return SystemProperties.getInt("ro.miui.ui.version.code", 8) >= 9;
}
public static boolean is12() {
return SystemProperties.getInt("ro.miui.ui.version.code", 9) >= 10;
}
public static boolean is125() {
return SystemProperties.getInt("ro.miui.ui.version.code", 10) >= 11;
}
public static boolean isNightMode(Context context) {
return (context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
}
public static boolean isNougat() {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.O;
}
public static boolean isPiePlus() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
public static boolean isQPlus() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
}
public static boolean isRPlus() {
return Build.VERSION.SDK_INT >= 30;
}
public static boolean isDeviceEncrypted(Context context) {
DevicePolicyManager policyMgr = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
int encryption = policyMgr.getStorageEncryptionStatus();
return
encryption == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE ||
encryption == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING ||
encryption == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
}
// public static boolean isLauncherIconVisible(Context context) {
// return context.getPackageManager().getComponentEnabledSetting(new ComponentName(context, GateWayLauncher.class)) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
// }
public static boolean isXposedInstallerInstalled(Context context) {
boolean skip = prefs.getBoolean("pref_key_miuizer_assumelsposed", false);
if (skip) return true;
PackageManager pm = context.getPackageManager();
for (String manager: xposedManagers) try {
pm.getPackageInfo(manager, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {}
return false;
}
public static String isLSPosedManagerInstalled(Context context) {
boolean lsposed = prefs.getBoolean("pref_key_miuizer_assumelsposed", false);
if (lsposed) return "Assumed LSPosed";
try {
PackageInfo info = context.getPackageManager().getPackageInfo("org.lsposed.manager", 0);
return info.versionName + " (" + info.versionCode + ")";
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
// public static boolean isUnsupportedManager(Context context) {
// try {
// return context.getPackageManager().getPackageInfo("org.meowcat.edxposed.manager", 0).versionCode > 45700;
// } catch (PackageManager.NameNotFoundException e) {
// return true;
// }
// }
public static boolean areXposedResourceHooksDisabled() {
File d1 = new File("/data/user_de/0/org.meowcat.edxposed.manager/conf/disable_resources");
File d2 = new File("/data/user_de/0/com.solohsu.android.edxp.manager/conf/disable_resources");
File d3 = new File("/data/user_de/0/de.robv.android.xposed.installer/conf/disable_resources");
return d1.exists() || d2.exists() || d3.exists();
}
public static boolean areXposedBlacklistsEnabled() {
File d1 = new File("/data/user_de/0/org.meowcat.edxposed.manager/conf/blackwhitelist");
File d2 = new File("/data/user_de/0/com.solohsu.android.edxp.manager/conf/blackwhitelist");
File d3 = new File("/data/user_de/0/de.robv.android.xposed.installer/conf/blackwhitelist");
return d1.exists() || d2.exists() || d3.exists();
}
public static boolean isXposedScopeEnabled(Context context) {
try {
return "true".equals(context.getPackageManager().getInstallerPackageName("EdXposedScope"));
} catch (Throwable t) {
return false;
}
}
public static boolean openXposedApp(Context context) {
for (String manager: xposedManagers) try {
Intent intent = context.getPackageManager().getLaunchIntentForPackage(manager);
if (intent == null) continue;
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
context.startActivity(intent);
return true;
} catch (Throwable ignore) {}
boolean lsposed = prefs.getBoolean("pref_key_miuizer_assumelsposed", false);
if (lsposed) try {
context.sendBroadcast(new Intent(GlobalActions.ACTION_PREFIX + "RunParasitic"));
return true;
} catch (Throwable ignore) {}
Toast.makeText(context, R.string.xposed_not_found, Toast.LENGTH_LONG).show();
return false;
}
public static String getNewEdXposedPath() {
File[] files = new File("/data/misc").listFiles();
String edxpPath = null;
if (files != null && files.length > 0)
for (File file: files)
if (file.getName().startsWith("edxp_")) {
edxpPath = file.getName();
break;
}
return edxpPath;
}
public static String makeNewEdXposedPathReadable() {
return makeNewEdXposedPathReadable(null);
}
@SuppressLint("SetWorldReadable")
public static String makeNewEdXposedPathReadable(String path) {
try {
String baseDir;
if (path == null) {
String edxpPath = getNewEdXposedPath();
if (edxpPath == null) return null;
baseDir = "/data/misc/" + edxpPath + "/0/log";
} else {
baseDir = path + "log";
}
File file;
file = new File(baseDir);
if (!file.exists()) return null;
try { file.setExecutable(true, false); } catch (Throwable t) { XposedBridge.log(t); }
file = new File(baseDir + "/all.log");
if (!file.exists()) file = new File(baseDir + "/error.log");
if (!file.exists()) file = new File(baseDir + "/modules.log");
if (!file.exists()) return null;
try { file.setReadable(true, false); } catch (Throwable t) { XposedBridge.log(t); }
return file.getAbsolutePath();
} catch (Throwable t) {
return null;
}
}
public static String getXposedPropVersion(File propFile, boolean fromHook) {
String version = "unknown";
try (FileInputStream inputStream = new FileInputStream(propFile)) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {
while (true) {
String readLine = null;
try {
readLine = bufferedReader.readLine();
} catch (Throwable t) {
t.printStackTrace();
}
if (readLine == null) break;
String[] split = readLine.split("=", 2);
if (split.length == 2) {
String line = split[0].trim();
if (line.charAt(0) != '#' && "version".equals(line)) {
version = split[1].trim();
break;
}
}
}
} catch (Throwable t) {
t.printStackTrace();
}
} catch (Throwable t) {
t.printStackTrace();
}
if (fromHook) xposedVersion = XposedBridge.getXposedVersion();
return "unknown".equals(version) ? version + " (" + xposedVersion + ")" : version;
}
public static String getXposedInstallerErrorLog(Context context) {
String baseDir = null;
File file;
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo("org.meowcat.edxposed.manager", PackageManager.GET_ACTIVITIES);
baseDir = "/data/user_de/0/org.meowcat.edxposed.manager/";
file = new File(baseDir + "log/all.log");
if (file.exists()) return baseDir + "log/all.log";
file = new File(baseDir + "log/error.log");
if (file.exists()) return baseDir + "log/error.log";
} catch (Throwable ignore) {}
try {
pm.getPackageInfo("com.solohsu.android.edxp.manager", PackageManager.GET_ACTIVITIES);
baseDir = "/data/user_de/0/com.solohsu.android.edxp.manager/";
file = new File(baseDir + "log/all.log");
if (file.exists()) return baseDir + "log/all.log";
file = new File(baseDir + "log/error.log");
if (file.exists()) return baseDir + "log/error.log";
} catch (Throwable ignore) {}
try {
pm.getPackageInfo("de.robv.android.xposed.installer", PackageManager.GET_ACTIVITIES);
baseDir = "/data/user_de/0/de.robv.android.xposed.installer/";
file = new File(baseDir + "log/error.log");
if (file.exists()) return baseDir + "log/error.log";
baseDir = null;
} catch (Throwable ignore) {}
if (baseDir == null)
return null;
else
return baseDir + "log/error.log";
}
public static boolean isSysAppUpdaterInstalled(Context context) {
PackageManager pm = context.getPackageManager();
boolean res = false;
try {
pm.getPackageInfo("com.xiaomi.discover", PackageManager.GET_ACTIVITIES);
res = true;
} catch (PackageManager.NameNotFoundException e) {}
return res;
}
public static void launchActivity(Activity act, String pkg, String cmp) {
launchActivity(act, pkg, cmp, false);
}
public static boolean launchActivity(Activity act, String pkg, String cmp, boolean silent) {
PackageManager pm = act.getPackageManager();
try {
pm.getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setComponent(new ComponentName(pkg, cmp));
act.startActivity(intent);
act.overridePendingTransition(R.anim.activity_open_enter, R.anim.activity_open_exit);
return true;
} catch (Throwable t) {
if (!silent) Toast.makeText(act, R.string.various_hiddenfeatures_not_found, Toast.LENGTH_LONG).show();
return false;
}
}
// public static boolean isScreenOn(Context context) {
// DisplayManager dispMgr = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
// for (Display display: dispMgr.getDisplays())
// if (display.getState() != Display.STATE_OFF) return true;
// return false;
// }
public static void hideKeyboard(Activity act, View view) {
try {
Context context = act == null ? view.getContext() : act;
InputMethodManager inputManager = (InputMethodManager)context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputManager == null) return;
IBinder token = null;
View currentFocusedView = act == null ? view : act.getCurrentFocus();
if (currentFocusedView != null)
token = currentFocusedView.getWindowToken();
if (token != null)
inputManager.hideSoftInputFromWindow(token, InputMethodManager.HIDE_NOT_ALWAYS);
} catch (Throwable t) {
t.printStackTrace();
}
}
public static void showOKDialog(Context context, int title, int text) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(title);
alert.setMessage(text);
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {}
});
alert.show();
}
public interface InputCallback {
void onInputFinished(String key, String text);
}
public static void showInputDialog(Context context, final String key, int titleRes, InputCallback callback) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(titleRes);
final EditText input = new EditText(context);
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL);
input.setText(prefs.getString(key, ""));
builder.setView(input);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onInputFinished(key, input.getText().toString());
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
builder.show();
}
public static boolean checkStorageReadable(Context context) {
String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY) || state.equals(Environment.MEDIA_MOUNTED)) {
return true;
} else {
showOKDialog(context, R.string.warning, R.string.storage_unavailable);
return false;
}
}
public static boolean checkStoragePerm(Activity act, int action) {
if (act.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
act.requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, action);
return false;
} else return true;
}
public static boolean checkSettingsPerm(Activity act) {
return act.checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED;
}
public static boolean checkCoarsePerm(Activity act, int action) {
if (act.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
act.requestPermissions(new String[]{ Manifest.permission.ACCESS_COARSE_LOCATION }, action);
return false;
} else return true;
}
public static boolean checkFinePerm(Activity act, int action) {
if (act.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
act.requestPermissions(new String[]{ Manifest.permission.ACCESS_FINE_LOCATION }, action);
return false;
} else return true;
}
public static boolean preparePathForBackup(Activity act, String path) {
if (!checkStoragePerm(act, REQUEST_PERMISSIONS_BACKUP)) return false;
String state = Environment.getExternalStorageState();
switch (state) {
case Environment.MEDIA_MOUNTED_READ_ONLY:
showOKDialog(act, R.string.warning, R.string.storage_read_only);
return false;
case Environment.MEDIA_MOUNTED:
File file = new File(path);
if (!file.exists() && !file.mkdirs()) {
showOKDialog(act, R.string.warning, R.string.storage_cannot_mkdir);
return false;
}
return true;
default:
showOKDialog(act, R.string.warning, R.string.storage_unavailable);
return false;
}
}
public static void emptyFile(String pathToFile, boolean forceClear) {
File f = new File(pathToFile);
if (f.exists() && (f.length() > 150 * 1024 || forceClear)) {
try (FileOutputStream fOut = new FileOutputStream(f, false)) {
try (OutputStreamWriter output = new OutputStreamWriter(fOut)) {
output.write("");
} catch (Throwable ignore) {}
} catch (Throwable ignore) {}
}
}
@SuppressWarnings("ConstantConditions")
public static long getNextMIUIAlarmTime(Context context) {
String nextAlarm = Settings.System.getString(context.getContentResolver(), "next_alarm_clock_formatted");
long nextTime = 0;
if (!TextUtils.isEmpty(nextAlarm)) try {
TimeZone timeZone = TimeZone.getTimeZone("UTC");
SimpleDateFormat dateFormat = new SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), DateFormat.is24HourFormat(context) ? "EHm" : "Ehma"), Locale.getDefault());
dateFormat.setTimeZone(timeZone);
long nextTimePart = dateFormat.parse(nextAlarm).getTime();
Calendar cal = Calendar.getInstance(timeZone);
cal.setFirstDayOfWeek(Calendar.MONDAY);
cal.setTimeInMillis(nextTimePart);
int targetDay = cal.get(Calendar.DAY_OF_WEEK);
int targetHour = cal.get(Calendar.HOUR_OF_DAY);
int targetMinute = cal.get(Calendar.MINUTE);
cal = Calendar.getInstance();
int diff = targetDay - cal.get(Calendar.DAY_OF_WEEK);
if (diff < 0) diff += 7;
cal.add(Calendar.DAY_OF_MONTH, diff);
cal.set(Calendar.HOUR_OF_DAY, targetHour);
cal.set(Calendar.MINUTE, targetMinute);
cal.clear(Calendar.SECOND);
cal.clear(Calendar.MILLISECOND);
nextTime = cal.getTimeInMillis();
} catch (Throwable t) {
XposedBridge.log(t);
}
return nextTime;
}
public static long getNextStockAlarmTime(Context context) {
AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
if (alarmMgr == null) return 0;
AlarmManager.AlarmClockInfo aci = alarmMgr.getNextAlarmClock();
return aci == null ? 0 : aci.getTriggerTime();
}
@SuppressWarnings("ConstantConditions")
public static void updateNewModsMarking(Context context) {
updateNewModsMarking(context, Integer.parseInt(prefs.getString("pref_key_miuizer_marknewmods", "2")));
}
public static void updateNewModsMarking(Context context, int opt) {
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(modulePkg, 0);
long appInstalled = System.currentTimeMillis() - new File(appInfo.sourceDir).lastModified();
// Log.e("miuizer", "installed: " + appInstalled + " msecs or " + appInstalled / (1000 * 60 * 60) + " hrs");
if (opt == 0)
showNewMods = false;
else if (opt == 4)
showNewMods = true;
else
showNewMods = appInstalled < (opt == 1 ? 1 : (opt == 2 ? 3 : 7)) * 24 * 60 * 60 * 1000;
} catch (Throwable t) {
t.printStackTrace();
}
}
public static void applyNewMod(TextView title) {
CharSequence titleStr = title.getText();
String newModStr = title.getResources().getString(R.string.miuizer_new_mod) + " ";
int start = titleStr.length() + 3;
int end = start + newModStr.length();
SpannableStringBuilder ssb = new SpannableStringBuilder(title.getText() + " " + newModStr);
ssb.setSpan(new ForegroundColorSpan(markColor), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ssb.setSpan(new RelativeSizeSpan(0.75f), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
title.setText(ssb);
}
public static void applyShimmer(TextView title) {
if (title.getPaint().getShader() != null) return;
int width = title.getResources().getDisplayMetrics().widthPixels;
Shader shimmer = new LinearGradient(0, 0, width, 0, new int[]{ 0xFF5DA5FF, 0xFF9B8AFB, 0xFFD176F2, 0xFFFE88B2, 0xFFD176F2, 0xFF9B8AFB }, new float[]{ 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, 1.0f }, Shader.TileMode.REPEAT);
Matrix matrix = new Matrix();
matrix.setTranslate(0, 0);
shimmer.setLocalMatrix(matrix);
title.getPaint().setShader(shimmer);
if (shimmerAnim != null) shimmerAnim.cancel();
shimmerAnim = ValueAnimator.ofFloat(0, width, width / 1.8f, width * 1.3f);
shimmerAnim.removeAllUpdateListeners();
shimmerAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
matrix.setTranslate((float)animation.getAnimatedValue(), 0);
Shader shader = title.getPaint().getShader();
if (shader == null)
shimmerAnim.cancel();
else
shader.setLocalMatrix(matrix);
title.invalidate();
}
});
shimmerAnim.removeAllListeners();
shimmerAnim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animation) {
title.getPaint().setShader(null);
title.invalidate();
}
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationCancel(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
});
shimmerAnim.setStartDelay(0);
shimmerAnim.setDuration(10000);
shimmerAnim.start();
}
public static void openURL(Context context, String url) {
if (context == null) return;
Intent uriIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
if (uriIntent.resolveActivity(context.getPackageManager()) != null)
context.startActivity(uriIntent);
else
showOKDialog(context, R.string.warning, R.string.no_browser);
}
public static void openAppInfo(Context context, String pkg, int user) {
try {
Intent intent = new Intent("miui.intent.action.APP_MANAGER_APPLICATION_DETAIL");
intent.setPackage("com.miui.securitycenter");
intent.putExtra("package_name", pkg);
if (user != 0) intent.putExtra("miui.intent.extra.USER_ID", user);
context.startActivity(intent);
} catch (Throwable t) {
try {
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
intent.setData(Uri.parse("package:" + pkg));
if (user != 0)
XposedHelpers.callMethod(context, "startActivityAsUser", intent, XposedHelpers.newInstance(UserHandle.class, user));
else
context.startActivity(intent);
} catch (Throwable t2) {
XposedBridge.log(t2);
}
}
}
public static boolean isReallyVisible(final View view) {
if (view == null || !view.isShown() || view.getAlpha() == 0) return false;
final Rect actualPosition = new Rect();
view.getGlobalVisibleRect(actualPosition);
return actualPosition.intersect(new Rect(0, 0, view.getResources().getDisplayMetrics().widthPixels, view.getResources().getDisplayMetrics().heightPixels));
}
public static ArrayList<View> getChildViewsRecursive(View view) {
return getChildViewsRecursive(view, true);
}
public static ArrayList<View> getChildViewsRecursive(View view, boolean includeContainers) {
if (view instanceof ViewGroup) {
ArrayList<View> list2 = new ArrayList<View>();
ViewGroup viewgroup = (ViewGroup)view;
int i = 0;
do {
if (i >= viewgroup.getChildCount()) return list2;
View view1 = viewgroup.getChildAt(i);
ArrayList<View> list3 = new ArrayList<View>();
if (includeContainers) list3.add(view);
list3.addAll(getChildViewsRecursive(view1));
list2.addAll(list3);
i++;
} while (true);
} else {
ArrayList<View> list1 = new ArrayList<View>();
list1.add(view);
return list1;
}
}
private static String getModTitle(Resources res, String title) {
if (title == null) return null;
int titleResId = Integer.parseInt(title.substring(1));
if (titleResId <= 0) return null;
return res.getString(titleResId);
}
private static boolean checkMultiUserPermission(Context context) {
return context.getPackageManager().checkPermission("android.permission.INTERACT_ACROSS_USERS", modulePkg) == PackageManager.PERMISSION_GRANTED;
}
@SuppressWarnings({"JavaReflectionInvocation", "ConstantConditions"})
@SuppressLint({"PrivateApi", "DiscouragedPrivateApi"})
public static float getAnimationScale(int type) {
try {
Class<?> smClass = Class.forName("android.os.ServiceManager");
Method getService = smClass.getDeclaredMethod("getService", String.class);
getService.setAccessible(true);
Object manager = getService.invoke(smClass, "window");
Class<?> wmsClass = Class.forName("android.view.IWindowManager$Stub");
Method asInterface = wmsClass.getDeclaredMethod("asInterface", IBinder.class);
asInterface.setAccessible(true);
Object wm = asInterface.invoke(wmsClass, manager);
Method getAnimationScale = wm.getClass().getDeclaredMethod("getAnimationScale", int.class);
getAnimationScale.setAccessible(true);
return (float)getAnimationScale.invoke(wm, type);
} catch (Throwable t) {
t.printStackTrace();
return 1.0f;
}
}
@SuppressWarnings({"JavaReflectionInvocation", "ConstantConditions"})
@SuppressLint({"PrivateApi", "DiscouragedPrivateApi"})
public static void setAnimationScale(int type, float value) {
try {
Class<?> smClass = Class.forName("android.os.ServiceManager");
Method getService = smClass.getDeclaredMethod("getService", String.class);
getService.setAccessible(true);
Object manager = getService.invoke(smClass, "window");
Class<?> wmsClass = Class.forName("android.view.IWindowManager$Stub");
Method asInterface = wmsClass.getDeclaredMethod("asInterface", IBinder.class);
asInterface.setAccessible(true);
Object wm = asInterface.invoke(wmsClass, manager);
Method setAnimationScale = wm.getClass().getDeclaredMethod("setAnimationScale", int.class, float.class);
setAnimationScale.setAccessible(true);
setAnimationScale.invoke(wm, type, value);
} catch (Throwable t) {
t.printStackTrace();
}
}
@SuppressLint("DiscouragedPrivateApi")
private static Method getPackageInfoAsUser(Context context) {
try {
return context.getPackageManager().getClass().getDeclaredMethod("getPackageInfoAsUser", String.class, int.class, int.class);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
public static int getMIUILauncherVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo("com.miui.home", 0);
return packageInfo.versionCode;
} catch (Throwable t) {
return 0;
}
}
public static void getInstalledApps(Context context) {
final PackageManager pm = context.getPackageManager();
boolean includeDualApps = checkMultiUserPermission(context);
Method getPackageInfoAsUser = getPackageInfoAsUser(context);
if (getPackageInfoAsUser == null) includeDualApps = false;
List<ApplicationInfo> packs = pm.getInstalledApplications(PackageManager.GET_META_DATA | PackageManager.MATCH_DISABLED_COMPONENTS);
installedAppsList = new ArrayList<AppData>();
AppData app;
for (ApplicationInfo pack: packs) try {
app = new AppData();
app.enabled = pack.enabled;
app.label = pack.loadLabel(pm).toString();
app.pkgName = pack.packageName;
app.actName = "-";
installedAppsList.add(app);
if (includeDualApps) try {
if (getPackageInfoAsUser.invoke(pm, app.pkgName, 0, 999) != null) {
AppData appDual = new AppData();
appDual.enabled = pack.enabled;
appDual.label = pack.loadLabel(pm).toString();
appDual.pkgName = pack.packageName;
appDual.actName = "-";
appDual.user = 999;
installedAppsList.add(appDual);
}
} catch (Throwable ignore) {}
} catch (Throwable e) {
e.printStackTrace();
}
installedAppsList.sort(new Comparator<AppData>() {
public int compare(AppData app1, AppData app2) {
return app1.label.compareToIgnoreCase(app2.label);
}
});
}
@SuppressLint("DiscouragedPrivateApi")
public static void getLaunchableApps(Context context) {
final PackageManager pm = context.getPackageManager();
boolean includeDualApps = checkMultiUserPermission(context);
Method getPackageInfoAsUser = getPackageInfoAsUser(context);
if (getPackageInfoAsUser == null) includeDualApps = false;
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> packs = pm.queryIntentActivities(mainIntent, 0);
launchableAppsList = new ArrayList<AppData>();
AppData app;
for (ResolveInfo pack: packs) try {
app = new AppData();
app.pkgName = pack.activityInfo.applicationInfo.packageName;
app.actName = pack.activityInfo.name;
app.enabled = pack.activityInfo.enabled;
app.label = pack.loadLabel(pm).toString();
launchableAppsList.add(app);
if (includeDualApps) try {
if (getPackageInfoAsUser.invoke(pm, app.pkgName, 0, 999) != null) {
AppData appDual = new AppData();
appDual.pkgName = pack.activityInfo.applicationInfo.packageName;
appDual.actName = pack.activityInfo.name;
appDual.enabled = pack.activityInfo.enabled;
appDual.label = pack.loadLabel(pm).toString();
appDual.user = 999;
launchableAppsList.add(appDual);
}
} catch (Throwable ignore) {}
} catch (Throwable t) {
t.printStackTrace();
}
launchableAppsList.sort(new Comparator<AppData>() {
public int compare(AppData app1, AppData app2) {
return app1.label.compareToIgnoreCase(app2.label);
}
});
}
public static void getShareApps(Context context) {
PackageManager pm = context.getPackageManager();
boolean includeDualApps = checkMultiUserPermission(context);
Method getPackageInfoAsUser = getPackageInfoAsUser(context);
if (getPackageInfoAsUser == null) includeDualApps = false;
final Intent mainIntent = new Intent();
mainIntent.setAction(Intent.ACTION_SEND);
mainIntent.setType("*/*");
mainIntent.putExtra("CustoMIUIzer", true);
List<ResolveInfo> packs = pm.queryIntentActivities(mainIntent, PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS);
shareAppsList = new ArrayList<AppData>();
AppData app;
for (ResolveInfo pack: packs) try {
boolean exists = false;
for (AppData shareApp: shareAppsList)
if (shareApp.pkgName.equals(pack.activityInfo.applicationInfo.packageName)) {
exists = true;
break;
}
if (exists) continue;
app = new AppData();
app.pkgName = pack.activityInfo.applicationInfo.packageName;
app.actName = "-";
app.enabled = pack.activityInfo.applicationInfo.enabled;
app.label = pack.activityInfo.applicationInfo.loadLabel(pm).toString();
shareAppsList.add(app);
if (includeDualApps) try {
if (getPackageInfoAsUser.invoke(pm, app.pkgName, 0, 999) != null) {
AppData appDual = new AppData();
appDual.pkgName = pack.activityInfo.applicationInfo.packageName;
appDual.actName = "-";
appDual.enabled = pack.activityInfo.applicationInfo.enabled;
appDual.label = pack.activityInfo.applicationInfo.loadLabel(pm).toString();
appDual.user = 999;
shareAppsList.add(appDual);
}
} catch (Throwable ignore) {}
} catch (Throwable e) {
e.printStackTrace();
}
shareAppsList.sort(new Comparator<AppData>() {
public int compare(AppData app1, AppData app2) {
return app1.label.compareToIgnoreCase(app2.label);
}
});
}
public static void getOpenWithApps(Context context) {
PackageManager pm = context.getPackageManager();
boolean includeDualApps = checkMultiUserPermission(context);
Method getPackageInfoAsUser = getPackageInfoAsUser(context);
if (getPackageInfoAsUser == null) includeDualApps = false;
Intent mainIntent = new Intent();
mainIntent.setAction(Intent.ACTION_VIEW);
mainIntent.setDataAndType(Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/test/5"), "*/*");
mainIntent.putExtra("CustoMIUIzer", true);
List<ResolveInfo> packs = pm.queryIntentActivities(mainIntent, PackageManager.MATCH_ALL | PackageManager.MATCH_DISABLED_COMPONENTS);
mainIntent = new Intent();
mainIntent.setAction(Intent.ACTION_VIEW);
mainIntent.setData(Uri.parse("https://google.com"));
mainIntent.putExtra("CustoMIUIzer", true);
List<ResolveInfo> packs2 = pm.queryIntentActivities(mainIntent, PackageManager.MATCH_ALL);
mainIntent = new Intent();
mainIntent.setAction(Intent.ACTION_VIEW);
mainIntent.setData(Uri.parse("vnd.youtube:n9AcG0glVu4"));
mainIntent.putExtra("CustoMIUIzer", true);
List<ResolveInfo> packs3 = pm.queryIntentActivities(mainIntent, PackageManager.MATCH_ALL);
packs.addAll(packs2);
packs.addAll(packs3);
openWithAppsList = new ArrayList<AppData>();
AppData app;
for (ResolveInfo pack: packs) try {
boolean exists = false;
for (AppData openWithApp: openWithAppsList)
if (openWithApp.pkgName.equals(pack.activityInfo.applicationInfo.packageName)) {
exists = true;
break;
}
if (exists) continue;
app = new AppData();
app.pkgName = pack.activityInfo.applicationInfo.packageName;
app.actName = "-";
app.enabled = pack.activityInfo.applicationInfo.enabled;
app.label = pack.activityInfo.applicationInfo.loadLabel(pm).toString();
openWithAppsList.add(app);
if (includeDualApps) try {
if (getPackageInfoAsUser.invoke(pm, app.pkgName, 0, 999) != null) {
AppData appDual = new AppData();
appDual.pkgName = pack.activityInfo.applicationInfo.packageName;
appDual.actName = "-";
appDual.enabled = pack.activityInfo.applicationInfo.enabled;
appDual.label = pack.activityInfo.applicationInfo.loadLabel(pm).toString();
appDual.user = 999;
openWithAppsList.add(appDual);
}
} catch (Throwable ignore) {}
} catch (Throwable e) {
e.printStackTrace();
}
openWithAppsList.sort(new Comparator<AppData>() {
public int compare(AppData app1, AppData app2) {
return app1.label.compareToIgnoreCase(app2.label);
}
});
}
public static CharSequence getAppName(Context context, String pkgActName) {
return getAppName(context, pkgActName, false);
}
public static CharSequence getAppName(Context context, String pkgActName, boolean forcePkg) {
PackageManager pm = context.getPackageManager();
String not_selected = context.getResources().getString(R.string.notselected);
String[] pkgActArray = pkgActName.split("\\|");
ApplicationInfo ai;
if (!pkgActName.equals(not_selected))
if (pkgActArray.length >= 1 && pkgActArray[0] != null) try {
if (!forcePkg && pkgActArray.length >= 2 && pkgActArray[1] != null && !pkgActArray[1].trim().equals("")) {
return pm.getActivityInfo(new ComponentName(pkgActArray[0], pkgActArray[1]), 0).loadLabel(pm).toString();
} else if (!pkgActArray[0].trim().equals("")) {
ai = pm.getApplicationInfo(pkgActArray[0], 0);
return pm.getApplicationLabel(ai);
}
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public static Drawable getAppIcon(Context context, String pkgActName) {
return getAppIcon(context, pkgActName, false);
}
public static Drawable getAppIcon(Context context, String pkgActName, boolean forcePkg) {
PackageManager pm = context.getPackageManager();
String not_selected = context.getResources().getString(R.string.notselected);
String[] pkgActArray = pkgActName.split("\\|");
if (!pkgActName.equals(not_selected))
if (pkgActArray.length >= 1 && pkgActArray[0] != null) try {
if (!forcePkg && pkgActArray.length >= 2 && pkgActArray[1] != null && !pkgActArray[1].trim().equals(""))
return pm.getActivityIcon(new ComponentName(pkgActArray[0], pkgActArray[1]));
else if (!pkgActArray[0].trim().equals(""))
return pm.getApplicationIcon(pkgActArray[0]);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
public static Drawable getShortcutIcon(Context context, String key) {
Drawable shortcutIcon = null;
String shortcutIconPath = getProtectedContext(context).getFilesDir() + "/shortcuts/" + key + "_shortcut.png";
File shortcutIconFile = new File(shortcutIconPath);
if (shortcutIconFile.exists())
shortcutIcon = new BitmapDrawable(context.getResources(), BitmapFactory.decodeFile(shortcutIconFile.getAbsolutePath()));
Drawable[] layers = { shortcutIcon };
LayerDrawable insetShortcutIcon = new LayerDrawable(layers);
int padding = (int)(5 * context.getResources().getDisplayMetrics().density);
insetShortcutIcon.setLayerInset(0, padding, padding, padding, padding);
return insetShortcutIcon;
}
public static String getActionName(Context context, String key) {
try {
int action = getSharedIntPref(context, key + "_action", 1);
Resources modRes = getModuleRes(context);
int resId = GlobalActions.getActionResId(action);
if (resId != 0)
return modRes.getString(resId);
else if (action == 8)
return (String)getAppName(getModuleContext(context), getSharedStringPref(context, key + "_app", ""), true);
else if (action == 9)
return getSharedStringPref(context, key + "_shortcut_name", "");
else if (action == 10) {
int what = getSharedIntPref(context, key + "_toggle", 0);
switch (what) {
case 1: return modRes.getString(R.string.array_global_toggle_wifi);
case 2: return modRes.getString(R.string.array_global_toggle_bt);
case 3: return modRes.getString(R.string.array_global_toggle_gps);
case 4: return modRes.getString(R.string.array_global_toggle_nfc);
case 5: return modRes.getString(R.string.array_global_toggle_sound);
case 6: return modRes.getString(R.string.array_global_toggle_brightness);
case 7: return modRes.getString(R.string.array_global_toggle_rotation);
case 8: return modRes.getString(R.string.array_global_toggle_torch);
case 9: return modRes.getString(R.string.array_global_toggle_mobiledata);
default: return null;
}
} else if (action == 20) {
Context ctx = getModuleContext(context);
String pref = getSharedStringPref(context, key + "_activity", "");
String name = (String)getAppName(ctx, pref);
if (name == null || name.isEmpty()) name = (String)getAppName(ctx, pref, true);
return name;
} else
return null;
} catch (Throwable t) {
XposedBridge.log(t);
return null;
}
}
@SuppressWarnings("ConstantConditions")
public static Pair<String, String> getActionNameLocal(Context context, String key) {
try {
int action = prefs.getInt(key + "_action", 1);
Resources modRes = context.getResources();
Pair<String, String> pair = null;
int resId = GlobalActions.getActionResId(action);
if (resId != 0)
pair = new Pair<>(modRes.getString(resId), "");
else if (action == 8)
pair = new Pair<>(modRes.getString(R.string.array_global_actions_launch), (String)getAppName(context, prefs.getString(key + "_app", ""), true));
else if (action == 9)
pair = new Pair<>(modRes.getString(R.string.array_global_actions_shortcut), prefs.getString(key + "_shortcut_name", ""));
else if (action == 10) {
int what = prefs.getInt(key + "_toggle", 0);
String toggle = modRes.getString(R.string.array_global_actions_toggle);
switch (what) {
case 1: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_wifi)); break;
case 2: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_bt)); break;
case 3: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_gps)); break;
case 4: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_nfc)); break;
case 5: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_sound)); break;
case 6: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_brightness)); break;
case 7: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_rotation)); break;
case 8: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_torch)); break;
case 9: pair = new Pair<>(toggle, modRes.getString(R.string.array_global_toggle_mobiledata)); break;
}
} else if (action == 20) {
String pref = prefs.getString(key + "_activity", "");
String name = (String)getAppName(context, pref);
if (name == null || name.isEmpty()) name = (String)getAppName(context, pref, true);
pair = new Pair<>(modRes.getString(R.string.array_global_actions_activity), name);
}
return pair;
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
public static Drawable getActionImage(Context context, String key) {
try {
int action = getSharedIntPref(context, key + "_action", 1);
Context modCtx = getModuleContext(context);
if (action == 8)
return getAppIcon(modCtx, getSharedStringPref(context, key + "_app", ""));
else if (action == 9)
return getSharedShortcutIconPref(modCtx, key);
else if (action == 20)
return getAppIcon(modCtx, getSharedStringPref(context, key + "_activity", ""), true);
else
return null;
} catch (Throwable t) {
XposedBridge.log(t);
return null;
}
}
@SuppressWarnings("ConstantConditions")
public static Drawable getActionImageLocal(Context context, String key) {
try {
int action = prefs.getInt(key + "_action", 1);
if (action == 8)
return getAppIcon(context, prefs.getString(key + "_app", ""));
else if (action == 9)
return getShortcutIcon(context, key);
else if (action == 20)
return getAppIcon(context, prefs.getString(key + "_activity", ""), true);
else
return null;
} catch (Throwable t) {
XposedBridge.log(t);
return null;
}
}
private static void parsePrefXml(Context context, int xmlResId) {
Resources res = context.getResources();
String lastPrefSub = null;
String lastPrefSubTitle = null;
String lastPrefSubSubTitle = null;
int catResId = 0;
ModData.ModCat catPrefKey = null;
switch(xmlResId) {
case R.xml.prefs_system:
catResId = R.string.system_mods;
catPrefKey = ModData.ModCat.pref_key_system;
break;
case R.xml.prefs_launcher:
catResId = R.string.launcher_title;
catPrefKey = ModData.ModCat.pref_key_launcher;
break;
case R.xml.prefs_controls:
catResId = R.string.controls_mods;
catPrefKey = ModData.ModCat.pref_key_controls;
break;
case R.xml.prefs_various:
catResId = R.string.various_mods;
catPrefKey = ModData.ModCat.pref_key_various;
break;
}
try (XmlResourceParser xml = res.getXml(xmlResId)) {
int eventType = xml.getEventType();
int order = 0;
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG && !PreferenceScreen.class.getSimpleName().equals(xml.getName())) try {
if (xml.getName().equals(PreferenceCategory.class.getSimpleName()) || xml.getName().equals(PreferenceCategoryEx.class.getCanonicalName())) {
if (xml.getAttributeValue(ANDROID_NS, "key") != null) {
lastPrefSub = xml.getAttributeValue(ANDROID_NS, "key");
lastPrefSubTitle = getModTitle(res, xml.getAttributeValue(ANDROID_NS, "title"));
lastPrefSubSubTitle = null;
order = 1;
} else {
lastPrefSubSubTitle = getModTitle(res, xml.getAttributeValue(ANDROID_NS, "title"));
order++;
}
eventType = xml.next();
continue;
}
ModData modData = new ModData();
boolean isChild = xml.getAttributeBooleanValue(MIUIZER_NS, "child", false);
if (!isChild) {
modData.title = getModTitle(res, xml.getAttributeValue(ANDROID_NS, "title"));
if (modData.title != null) {
modData.breadcrumbs = res.getString(catResId) + (lastPrefSubTitle == null ? "" : ("/" + lastPrefSubTitle + (lastPrefSubSubTitle == null ? "" : "/" + lastPrefSubSubTitle)));
modData.key = xml.getAttributeValue(ANDROID_NS, "key");
modData.cat = catPrefKey;
modData.sub = lastPrefSub;
modData.order = order;
allModsList.add(modData);
//Log.e("miuizer", modData.key + " = " + modData.order);
}
}
order++;
} catch (Throwable t) {
t.printStackTrace();
}
eventType = xml.next();
}
} catch (Throwable t) {
t.printStackTrace();
}
}
public static void getAllMods(Context context, boolean force) {
if (force) allModsList.clear();
else if (allModsList.size() > 0) return;
parsePrefXml(context, R.xml.prefs_system);
parsePrefXml(context, R.xml.prefs_launcher);
parsePrefXml(context, R.xml.prefs_controls);
parsePrefXml(context, R.xml.prefs_various);
}
public static void performLightVibration(Context context) {
performLightVibration(context, false);
}
public static void performLightVibration(Context context, boolean ignoreOff) {
performVibration(context, false, ignoreOff);
}
public static void performStrongVibration(Context context) {
performVibration(context, true, false);
}
public static void performStrongVibration(Context context, boolean ignoreOff) {
performVibration(context, true, ignoreOff);
}
public static void performVibration(Context context, boolean isStrong, boolean ignoreOff) {
if (context == null) return;
HapticFeedbackUtil mHapticFeedbackUtil = new HapticFeedbackUtil(context, false);
mHapticFeedbackUtil.performHapticFeedback(isStrong ? HapticFeedbackConstants.LONG_PRESS : HapticFeedbackConstants.VIRTUAL_KEY, ignoreOff);
// int state = 1;
// int level = 1;
// try {
// state = Settings.System.getInt(context.getContentResolver(), "haptic_feedback_enabled");
// level = Settings.System.getInt(context.getContentResolver(), "haptic_feedback_level");
// } catch (Throwable t) {
// XposedBridge.log(t);
// }
// if (state == 0) return;
//
// Vibrator v = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
// if (Build.VERSION.SDK_INT >= 26)
// v.vibrate(VibrationEffect.createOneShot(30, VibrationEffect.DEFAULT_AMPLITUDE));
// else
// v.vibrate(30);
}
public static void performCustomVibration(Context context, int vibration, String ownPattern) {
if (vibration == 0) return;
Vibrator vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
if (vibrator == null) return;
long[] pattern = new long[0];
switch (vibration) {
case 1: vibrator.vibrate(200); return;
case 2: vibrator.vibrate(400); return;
case 3: pattern = new long[] { 0, 250, 250, 250 }; break;
case 4: pattern = new long[] { 0, 250, 150, 125, 100, 125 }; break;
case 5: pattern = new long[] { 0, 150, 150, 100, 250, 150, 150, 100 }; break;
case 6: pattern = new long[] { 0, 100, 150, 100, 150, 100 }; break;
case 7:
if (TextUtils.isEmpty(ownPattern)) return;
pattern = getVibrationPattern(ownPattern);
break;
}
if (!isNougat()) try {
vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1));
} catch (Throwable t) {
//noinspection deprecation
vibrator.vibrate(200);
} else
vibrator.vibrate(200);
}
public static long[] getVibrationPattern(String patternStr) {
try {
if (TextUtils.isEmpty(patternStr)) return new long[0];
String[] sPattern = patternStr.split(",");
long[] pattern = new long[sPattern.length];
for (int i = 0; i < sPattern.length; i++)
pattern[i] = TextUtils.isEmpty(sPattern[i]) ? 0 : Long.parseLong(sPattern[i]);
return pattern;
} catch (Throwable t) {
return new long[0];
}
}
// public static void removePref(PreferenceFragmentBase frag, String prefName, String catName) {
// if (frag.findPreference(prefName) != null) {
// Preference cat = frag.findPreference(catName);
// if (cat instanceof PreferenceScreen) ((PreferenceScreen)cat).removePreference(frag.findPreference(prefName));
// else if (cat instanceof PreferenceCategory) ((PreferenceCategory)cat).removePreference(frag.findPreference(prefName));
// }
// }
//
// public static void disablePref(PreferenceFragmentBase frag, String prefName, String reasonText) {
// Preference pref = frag.findPreference(prefName);
// if (pref != null) {
// pref.setEnabled(false);
// pref.setSummary(reasonText);
// }
// }
@SuppressWarnings("deprecation")
@SuppressLint("WorldReadableFiles")
public static SharedPreferences getSharedPrefs(Context context, boolean protectedStorage, boolean multiProcess) {
if (protectedStorage) context = getProtectedContext(context);
try {
return context.getSharedPreferences(prefsName, multiProcess ? Context.MODE_MULTI_PROCESS | Context.MODE_WORLD_READABLE : Context.MODE_WORLD_READABLE);
} catch (Throwable t) {
return context.getSharedPreferences(prefsName, multiProcess ? Context.MODE_MULTI_PROCESS | Context.MODE_PRIVATE : Context.MODE_PRIVATE);
}
}
public static SharedPreferences getSharedPrefs(Context context, boolean protectedStorage) {
return getSharedPrefs(context, protectedStorage, false);
}
@SuppressWarnings("ConstantConditions")
public static String getSharedPrefsPath() {
if (prefsPathCurrent == null) try {
Field fFile = prefs.getClass().getDeclaredField("mFile");
fFile.setAccessible(true);
prefsPathCurrent = ((File)fFile.get(prefs)).getParentFile().getAbsolutePath();
return prefsPathCurrent;
} catch (Throwable t) {
return prefsPath;
} else return prefsPathCurrent;
}
@SuppressWarnings("ConstantConditions")
public static String getSharedPrefsFile() {
if (prefsFileCurrent == null) try {
Field fFile = prefs.getClass().getDeclaredField("mFile");
fFile.setAccessible(true);
prefsFileCurrent = ((File)fFile.get(prefs)).getAbsolutePath();
return prefsFileCurrent;
} catch (Throwable t) {
return prefsFile;
} else return prefsFileCurrent;
}
public static boolean usingNewSharedPrefs() {
return getSharedPrefsPath().startsWith("/data/misc/");
}
public static String getCacheFilePath(String filename) {
if (new File("/cache").canWrite()) return "/cache/" + filename;
else if (new File("/data/cache").canWrite()) return "/data/cache/" + filename;
else if (new File("/data/tmp").canWrite()) return "/data/tmp/" + filename;
else return null;
}
public static boolean copyFile(String from, String to) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Files.copy(Paths.get(from), Paths.get(to), StandardCopyOption.REPLACE_EXISTING);
return true;
} else try (InputStream in = new FileInputStream(from)) {
try (OutputStream out = new FileOutputStream(to, false)) {
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) out.write(buf, 0, len);
return true;
}
}
} catch (Throwable t) {
t.printStackTrace();
return false;
}
}
public static boolean migratePrefs() {
return copyFile(prefsFile, getSharedPrefsFile());
}
@SuppressLint({"SetWorldReadable", "SetWorldWritable"})
public static void fixPermissionsAsync(Context context) {
AsyncTask.execute(() -> {
try { Thread.sleep(500); } catch (Throwable ignore) {}
File pkgFolder = context.getDataDir();
if (pkgFolder.exists()) {
pkgFolder.setExecutable(true, false);
pkgFolder.setReadable(true, false);
pkgFolder.setWritable(true, false);
}
File sharedPrefsFolder = new File(Helpers.getSharedPrefsPath());
if (sharedPrefsFolder.exists()) {
sharedPrefsFolder.setExecutable(true, false);
sharedPrefsFolder.setReadable(true, false);
sharedPrefsFolder.setWritable(true, false);
}
File sharedPrefsFile = new File(Helpers.getSharedPrefsFile());
if (sharedPrefsFile.exists()) {
sharedPrefsFile.setReadable(true, false);
sharedPrefsFile.setExecutable(true, false);
sharedPrefsFile.setWritable(true, false);
}
});
}
public static boolean containsStringPair(Set<String> hayStack, String needle) {
boolean res = false;
if (hayStack == null || hayStack.size() == 0) return false;
for (String pair: hayStack) {
String[] needles = pair.split("\\|");
if (needles[0].equalsIgnoreCase(needle)) {
res = true;
break;
}
}
return res;
}
public static void addStringPair(Set<String> hayStack, String needle1, String needle2) {
if (hayStack != null) hayStack.add(needle1 + "|" + needle2);
}
public static void removeStringPair(Set<String> hayStack, String needle) {
if (hayStack != null)
for (String pair: hayStack) {
String[] needles = pair.split("\\|", 2);
if (needles[0].equals(needle)) {
hayStack.remove(pair);
return;
}
}
}
public static synchronized Context getLocaleContext(Context context) throws Throwable {
if (prefs != null) {
String locale = prefs.getString("pref_key_miuizer_locale", "auto");
if (locale == null || "auto".equals(locale) || "1".equals(locale)) return context;
Configuration config = context.getResources().getConfiguration();
config.setLocale(Locale.forLanguageTag(locale));
return context.createConfigurationContext(config);
} else {
return context;
}
}
public static synchronized Context getModuleContext(Context context) throws Throwable {
return getModuleContext(context, null);
}
public static synchronized Context getModuleContext(Context context, Configuration config) throws Throwable {
if (mModuleContext == null)
mModuleContext = context.createPackageContext(modulePkg, Context.CONTEXT_IGNORE_SECURITY).createDeviceProtectedStorageContext();
return config == null ? mModuleContext : mModuleContext.createConfigurationContext(config);
}
public static synchronized Context getProtectedContext(Context context) {
return getProtectedContext(context, null);
}
public static synchronized Context getProtectedContext(Context context, Configuration config) {
try {
Context mContext = context.isDeviceProtectedStorage() ? context : context.createDeviceProtectedStorageContext();
return getLocaleContext(config == null ? mContext : mContext.createConfigurationContext(config));
} catch (Throwable t) {
return context;
}
}
public static synchronized Resources getModuleRes(Context context) throws Throwable {
Configuration config = context.getResources().getConfiguration();
Context moduleContext = getModuleContext(context);
return (config == null ? moduleContext.getResources() : moduleContext.createConfigurationContext(config).getResources());
}
public static Uri stringPrefToUri(String name, String defValue) {
return Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/string/" + name + "/" + defValue);
}
public static Uri stringSetPrefToUri(String name) {
return Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/stringset/" + name);
}
public static Uri intPrefToUri(String name, int defValue) {
return Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/integer/" + name + "/" + defValue);
}
public static Uri boolPrefToUri(String name, boolean defValue) {
return Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/boolean/" + name + "/" + (defValue ? '1' : '0'));
}
public static Uri shortcutIconPrefToUri(String name) {
return Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/shortcut_icon/" + name);
}
public static Uri anyPrefToUri() {
return Uri.parse("content://" + SharedPrefsProvider.AUTHORITY + "/pref/");
}
public static String getSharedStringPref(Context context, String name, String defValue) {
Uri uri = stringPrefToUri(name, defValue);
try {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String prefValue = cursor.getString(0);
cursor.close();
return prefValue;
} else log("ContentResolver", "[" + name + "] Cursor fail: " + cursor);
} catch (Throwable t) {
XposedBridge.log(t);
}
if (MainModule.mPrefs.containsKey(name))
return (String)MainModule.mPrefs.getObject(name, defValue);
else
return defValue;
}
@SuppressWarnings("unchecked")
public static Set<String> getSharedStringSetPref(Context context, String name) {
Uri uri = stringSetPrefToUri(name);
try {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
Set<String> prefValue = new LinkedHashSet<String>();
while (cursor.moveToNext()) prefValue.add(cursor.getString(0));
cursor.close();
return prefValue;
} else log("ContentResolver", "[" + name + "] Cursor fail: null");
} catch (Throwable t) {
XposedBridge.log(t);
}
LinkedHashSet<String> empty = new LinkedHashSet<String>();
if (MainModule.mPrefs.containsKey(name))
return (Set<String>)MainModule.mPrefs.getObject(name, empty);
else
return empty;
}
public static int getSharedIntPref(Context context, String name, int defValue) {
Uri uri = intPrefToUri(name, defValue);
try {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int prefValue = cursor.getInt(0);
cursor.close();
return prefValue;
} else log("ContentResolver", "[" + name + "] Cursor fail: " + cursor);
} catch (Throwable t) {
XposedBridge.log(t);
}
if (MainModule.mPrefs.containsKey(name))
return (int)MainModule.mPrefs.getObject(name, defValue);
else
return defValue;
}
public static boolean getSharedBoolPref(Context context, String name, boolean defValue) {
Uri uri = boolPrefToUri(name, defValue);
try {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
int prefValue = cursor.getInt(0);
cursor.close();
return prefValue == 1;
} else log("ContentResolver", "[" + name + "] Cursor fail: " + cursor);
} catch (Throwable t) {
XposedBridge.log(t);
}
if (MainModule.mPrefs.containsKey(name))
return (boolean)MainModule.mPrefs.getObject(name, false);
else
return defValue;
}
public static Drawable getSharedShortcutIconPref(Context context, String name) {
Uri uri = shortcutIconPrefToUri(name);
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
if (inputStream != null && inputStream.available() > 0) {
Bitmap bmp = BitmapFactory.decodeStream(inputStream);
inputStream.close();
if (bmp == null) return null;
Drawable[] layers = { new BitmapDrawable(context.getResources(), bmp) };
LayerDrawable insetShortcutIcon = new LayerDrawable(layers);
int padding = (int)(5 * context.getResources().getDisplayMetrics().density);
insetShortcutIcon.setLayerInset(0, padding, padding, padding, padding);
return insetShortcutIcon;
} else log("ContentResolver", "[" + name + "] InputStream fail: " + inputStream);
} catch (Throwable t) {
XposedBridge.log(t);
}
return null;
}
public static class SharedPrefObserver extends ContentObserver {
enum PrefType {
Any, String, StringSet, Integer//, Boolean
}
PrefType prefType;
Context ctx;
String prefName;
String prefDefValueString;
int prefDefValueInt;
// boolean prefDefValueBool;
public SharedPrefObserver(Context context, Handler handler) {
super(handler);
ctx = context;
prefType = PrefType.Any;
registerObserver();
}
public SharedPrefObserver(Context context, Handler handler, String name, String defValue) {
super(handler);
ctx = context;
prefName = name;
prefType = PrefType.String;
prefDefValueString = defValue;
registerObserver();
}
public SharedPrefObserver(Context context, Handler handler, String name) {
super(handler);
ctx = context;
prefName = name;
prefType = PrefType.StringSet;
registerObserver();
}
public SharedPrefObserver(Context context, Handler handler, String name, int defValue) {
super(handler);
ctx = context;
prefType = PrefType.Integer;
prefName = name;
prefDefValueInt = defValue;
registerObserver();
}
// public SharedPrefObserver(Context context, Handler handler, String name, boolean defValue) {
// super(handler);
// ctx = context;
// prefType = PrefType.Boolean;
// prefName = name;
// prefDefValueBool = defValue;
// registerObserver();
// }
void registerObserver() {
Uri uri = null;
if (prefType == PrefType.String)
uri = stringPrefToUri(prefName, prefDefValueString);
else if (prefType == PrefType.StringSet)
uri = stringSetPrefToUri(prefName);
else if (prefType == PrefType.Integer)
uri = intPrefToUri(prefName, prefDefValueInt);
// else if (prefType == PrefType.Boolean)
// uri = boolPrefToUri(prefName, prefDefValueBool);
else if (prefType == PrefType.Any)
uri = anyPrefToUri();
if (uri != null) ctx.getContentResolver().registerContentObserver(uri, prefType == PrefType.Any, this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (prefType == PrefType.Any)
onChange(uri);
else
onChange(selfChange);
}
@Override
public void onChange(boolean selfChange) {
if (selfChange) return;
if (prefType == PrefType.String)
onChange(prefName, prefDefValueString);
else if (prefType == PrefType.StringSet)
onChange(prefName);
else if (prefType == PrefType.Integer)
onChange(prefName, prefDefValueInt);
// else if (prefType == PrefType.Boolean)
// onChange(prefName, prefDefValueBool);
}
public void onChange(Uri uri) {}
public void onChange(String name) {}
public void onChange(String name, String defValue) {}
public void onChange(String name, int defValue) {}
// public void onChange(String name, boolean defValue) {}
}
private static String getCallerMethod() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement el: stackTrace)
if (el != null && el.getClassName().startsWith(modulePkg + ".mods")) return el.getMethodName();
return stackTrace[4].getMethodName();
}
public static void log(String line) {
XposedBridge.log("[CustoMIUIzer] " + line);
}
public static void log(Throwable t) {
XposedBridge.log("[CustoMIUIzer]\n" + Log.getStackTraceString(t));
}
public static void log(String mod, String line) {
XposedBridge.log("[CustoMIUIzer][" + mod + "] " + line);
}
public static void log(String mod, Throwable t) {
XposedBridge.log("[CustoMIUIzer][" + mod + "]\n" + Log.getStackTraceString(t));
}
public static class MethodHook extends XC_MethodHook {
protected void before(MethodHookParam param) throws Throwable {}
protected void after(MethodHookParam param) throws Throwable {}
public MethodHook() {
super();
}
public MethodHook(int priority) {
super(priority);
}
@Override
public final void beforeHookedMethod(MethodHookParam param) throws Throwable {
try {
this.before(param);
} catch (Throwable t) {
XposedBridge.log(t);
}
}
@Override
public final void afterHookedMethod(MethodHookParam param) throws Throwable {
try {
this.after(param);
} catch (Throwable t) {
XposedBridge.log(t);
}
}
}
public static void hookMethod(Method method, MethodHook callback) {
try {
XposedBridge.hookMethod(method, callback);
} catch (Throwable t) {
log(getCallerMethod(), "Failed to hook " + method.getName() + " method");
}
}
public static void findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) {
try {
XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback);
} catch (Throwable t) {
log(getCallerMethod(), "Failed to hook " + methodName + " method in " + className);
}
}
public static void findAndHookMethod(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
try {
XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback);
} catch (Throwable t) {
log(getCallerMethod(), "Failed to hook " + methodName + " method in " + clazz.getCanonicalName());
}
}
@SuppressWarnings("UnusedReturnValue")
public static boolean findAndHookMethodSilently(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) {
try {
XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback);
return true;
} catch (Throwable t) {
return false;
}
}
@SuppressWarnings("UnusedReturnValue")
public static boolean findAndHookMethodSilently(Class<?> clazz, String methodName, Object... parameterTypesAndCallback) {
try {
XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback);
return true;
} catch (Throwable t) {
return false;
}
}
public static void findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) {
try {
XposedHelpers.findAndHookConstructor(className, classLoader, parameterTypesAndCallback);
} catch (Throwable t) {
log(getCallerMethod(), "Failed to hook constructor in " + className);
}
}
public static void hookAllConstructors(String className, ClassLoader classLoader, MethodHook callback) {
try {
Class<?> hookClass = XposedHelpers.findClassIfExists(className, classLoader);
if (hookClass == null || XposedBridge.hookAllConstructors(hookClass, callback).size() == 0)
log(getCallerMethod(), "Failed to hook " + className + " constructor");
} catch (Throwable t) {
XposedBridge.log(t);
}
}
public static void hookAllConstructors(Class<?> hookClass, MethodHook callback) {
try {
if (XposedBridge.hookAllConstructors(hookClass, callback).size() == 0)
log(getCallerMethod(), "Failed to hook " + hookClass.getCanonicalName() + " constructor");
} catch (Throwable t) {
XposedBridge.log(t);
}
}
public static void hookAllMethods(String className, ClassLoader classLoader, String methodName, XC_MethodHook callback) {
try {
Class<?> hookClass = XposedHelpers.findClassIfExists(className, classLoader);
if (hookClass == null || XposedBridge.hookAllMethods(hookClass, methodName, callback).size() == 0)
log(getCallerMethod(), "Failed to hook " + methodName + " method in " + className);
} catch (Throwable t) {
XposedBridge.log(t);
}
}
public static void hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {
try {
if (XposedBridge.hookAllMethods(hookClass, methodName, callback).size() == 0)
log(getCallerMethod(), "Failed to hook " + methodName + " method in " + hookClass.getCanonicalName());
} catch (Throwable t) {
XposedBridge.log(t);
}
}
public static boolean hookAllMethodsSilently(String className, ClassLoader classLoader, String methodName, XC_MethodHook callback) {
try {
Class<?> hookClass = XposedHelpers.findClassIfExists(className, classLoader);
return hookClass != null && XposedBridge.hookAllMethods(hookClass, methodName, callback).size() > 0;
} catch (Throwable t) {
return false;
}
}
@SuppressWarnings("ConstantConditions")
public static boolean hookAllMethodsSilently(Class<?> hookClass, String methodName, XC_MethodHook callback) {
try {
return hookClass != null && XposedBridge.hookAllMethods(hookClass, methodName, callback).size() > 0;
} catch (Throwable t) {
return false;
}
}
@SuppressWarnings("ConstantConditions")
public static Context findContext() {
Context context = null;
try {
context = (Application)XposedHelpers.callStaticMethod(XposedHelpers.findClass("android.app.ActivityThread", null), "currentApplication");
if (context == null) {
Object currentActivityThread = XposedHelpers.callStaticMethod(XposedHelpers.findClass("android.app.ActivityThread", null), "currentActivityThread");
if (currentActivityThread != null) context = (Context)XposedHelpers.callMethod(currentActivityThread, "getSystemContext");
}
} catch (Throwable ignore) {}
return context;
}
public static Bitmap fastBlur(Bitmap sentBitmap, int radius) {
Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
if (radius < 1) return null;
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int[] pix = new int[w * h];
bitmap.getPixels(pix, 0, w, 0, 0, w, h);
int wm = w - 1;
int hm = h - 1;
int wh = w * h;
int div = radius + radius + 1;
int[] r = new int[wh];
int[] g = new int[wh];
int[] b = new int[wh];
int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
int[] vmin = new int[Math.max(w, h)];
int divsum = (div + 1) >> 1;
divsum *= divsum;
int[] dv = new int[256 * divsum];
for (i = 0; i < 256 * divsum; i++) dv[i] = (i / divsum);
yw = yi = 0;
int[][] stack = new int[div][3];
int stackpointer;
int stackstart;
int[] sir;
int rbs;
int r1 = radius + 1;
int routsum, goutsum, boutsum;
int rinsum, ginsum, binsum;
for (y = 0; y < h; y++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
for (i = -radius; i <= radius; i++) {
p = pix[yi + Math.min(wm, Math.max(i, 0))];
sir = stack[i + radius];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rbs = r1 - Math.abs(i);
rsum += sir[0] * rbs;
gsum += sir[1] * rbs;
bsum += sir[2] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
}
stackpointer = radius;
for (x = 0; x < w; x++) {
if (rsum < dv.length) r[yi] = dv[rsum];
if (gsum < dv.length) g[yi] = dv[gsum];
if (bsum < dv.length) b[yi] = dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (y == 0) {
vmin[x] = Math.min(x + radius + 1, wm);
}
p = pix[yw + vmin[x]];
sir[0] = (p & 0xff0000) >> 16;
sir[1] = (p & 0x00ff00) >> 8;
sir[2] = (p & 0x0000ff);
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[(stackpointer) % div];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi++;
}
yw += w;
}
for (x = 0; x < w; x++) {
rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
yp = -radius * w;
for (i = -radius; i <= radius; i++) {
yi = Math.max(0, yp) + x;
sir = stack[i + radius];
sir[0] = r[yi];
sir[1] = g[yi];
sir[2] = b[yi];
rbs = r1 - Math.abs(i);
rsum += r[yi] * rbs;
gsum += g[yi] * rbs;
bsum += b[yi] * rbs;
if (i > 0) {
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
} else {
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
}
if (i < hm) {
yp += w;
}
}
yi = x;
stackpointer = radius;
for (y = 0; y < h; y++) {
// Preserve alpha channel: ( 0xff000000 & pix[yi] )
pix[yi] = ( 0xff000000 & pix[yi] ) | ( dv[rsum] << 16 ) | ( dv[gsum] << 8 ) | dv[bsum];
rsum -= routsum;
gsum -= goutsum;
bsum -= boutsum;
stackstart = stackpointer - radius + div;
sir = stack[stackstart % div];
routsum -= sir[0];
goutsum -= sir[1];
boutsum -= sir[2];
if (x == 0) {
vmin[y] = Math.min(y + r1, hm) * w;
}
p = x + vmin[y];
sir[0] = r[p];
sir[1] = g[p];
sir[2] = b[p];
rinsum += sir[0];
ginsum += sir[1];
binsum += sir[2];
rsum += rinsum;
gsum += ginsum;
bsum += binsum;
stackpointer = (stackpointer + 1) % div;
sir = stack[stackpointer];
routsum += sir[0];
goutsum += sir[1];
boutsum += sir[2];
rinsum -= sir[0];
ginsum -= sir[1];
binsum -= sir[2];
yi += w;
}
}
bitmap.setPixels(pix, 0, w, 0, 0, w, h);
return bitmap;
}
private static float norm(float f, float f2, float f3) {
return (f3 - f) / (f2 - f);
}
private static float lerp(float f, float f2, float f3) {
return ((f2 - f) * f3) + f;
}
private static float sq(float f) {
return f * f;
}
public static float exp(float f) {
return (float)Math.exp(f);
}
public static float sqrt(float f) {
return (float)Math.sqrt(f);
}
public static float log(float f) {
return (float)Math.log(f);
}
public static int convertGammaToLinear(int val, int min, int max) {
float norm = norm(0.0f, (float)max, (float) val);
val = Math.round(lerp((float) min, (float) max, (norm <= 0.2f ? sq(norm / 0.2f) : exp((norm - 0.221f) / 0.314f) + 0.06f) / 12.0f));
return Math.min(val, max);
}
public static int convertLinearToGamma(int val, int min, int max) {
float norm = norm((float) min, (float)max, (float)val) * 12.0f;
return Math.round(lerp(0.0f, (float)max, norm <= 1.0f ? sqrt(norm) * 0.2f : (log(norm - 0.06f) * 0.314f) + 0.221f));
}
}