package tk.wasdennnoch.androidn_ify.settings; import android.app.Activity; import android.app.Fragment; 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.content.res.TypedArray; import android.os.Bundle; import android.os.SystemClock; import android.os.UserManager; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.os.BatteryStatsHelper; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_InitPackageResources; import tk.wasdennnoch.androidn_ify.R; import tk.wasdennnoch.androidn_ify.XposedHook; import tk.wasdennnoch.androidn_ify.extracted.settingslib.BatteryInfo; import tk.wasdennnoch.androidn_ify.extracted.settingslib.UsageView; import tk.wasdennnoch.androidn_ify.settings.summaries.SummaryTweaks; import tk.wasdennnoch.androidn_ify.ui.PlatLogoActivity; import tk.wasdennnoch.androidn_ify.utils.ConfigUtils; import tk.wasdennnoch.androidn_ify.utils.ResourceUtils; public class SettingsHooks { private static final String TAG = "SettingsHooks"; private static final String KEY_APP_DETAILS_CATEGORY = "AppDetailsCategory"; private static final String KEY_APP_DETAILS = "app_details"; private static final String KEY_BATTERY_INFO = "battery_info"; private static final String KEY_CHART = "chart"; private static final long[] mHits = new long[3]; private static SettingsDrawerHooks mDrawerHooks = new SettingsDrawerHooks(); private static SettingsDashboardHooks mDashboardHooks = new SettingsDashboardHooks(); public static void hook(ClassLoader classLoader) { try { ConfigUtils.SettingsConfig config = ConfigUtils.settings(); if (config.n_style_dashboard) { mDashboardHooks.hook(classLoader); } if (config.enable_drawer) { mDrawerHooks.hook(classLoader); } if (config.enable_summaries) { SummaryTweaks.hookMethods(classLoader); if (ConfigUtils.M) { Class<?> classBatteryHistoryPreference = XposedHelpers.findClass("com.android.settings.fuelgauge.BatteryHistoryPreference", classLoader); XposedHelpers.findAndHookMethod(classBatteryHistoryPreference, "setStats", BatteryStatsHelper.class, new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Preference preference = (Preference) param.thisObject; BatteryStatsHelper batteryStats = (BatteryStatsHelper) param.args[0]; final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000; XposedHelpers.setAdditionalInstanceField(preference, KEY_BATTERY_INFO, BatteryInfo.getBatteryInfo(preference.getContext(), batteryStats.getBatteryBroadcast(), batteryStats.getStats(), elapsedRealtimeUs)); } }); XposedHelpers.findAndHookMethod(classBatteryHistoryPreference, "onBindView", View.class, new XC_MethodReplacement() { @Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable { bindBatteryHistoryPreference((Preference) param.thisObject,(View) param.args[0]); return null; } }); } } if (config.enable_n_platlogo) { Class<?> classDeviceInfoSettings = XposedHelpers.findClass("com.android.settings.DeviceInfoSettings", classLoader); XposedHelpers.findAndHookMethod(classDeviceInfoSettings, "onPreferenceTreeClick", PreferenceScreen.class, Preference.class, onPreferenceTreeClickHook); } if (config.install_source) { Class<?> classInstalledAppDetails = XposedHelpers.findClass("com.android.settings.applications.InstalledAppDetails", classLoader); if (ConfigUtils.M) { XposedHelpers.findAndHookMethod(classInstalledAppDetails, "onActivityCreated", Bundle.class, onActivityCreatedHook); } else { XposedHelpers.findAndHookMethod(classInstalledAppDetails, "onCreateView", LayoutInflater.class, ViewGroup.class, Bundle.class, onCreateViewHook); } XposedHelpers.findAndHookMethod(classInstalledAppDetails, "refreshUi", refreshUiHook); } } catch (Throwable t) { XposedHook.logE(TAG, "Error in hook", t); } } @SuppressWarnings("deprecation") private static void bindBatteryHistoryPreference(Preference preference, View view) { Object field = XposedHelpers.getAdditionalInstanceField(preference, KEY_BATTERY_INFO); Object mChart = XposedHelpers.getAdditionalInstanceField(preference, KEY_CHART); if (field != null && field instanceof BatteryInfo) { if (view.findViewById(R.id.battery_usage) == null) { FrameLayout oldLayout = (FrameLayout) view; oldLayout.removeAllViews(); Context context = oldLayout.getContext(); ResourceUtils res = ResourceUtils.getInstance(context); LinearLayout layout = (LinearLayout) LayoutInflater.from(ResourceUtils.createOwnContext(context)).inflate(R.layout.battery_usage_graph, oldLayout, false); TypedValue colorAccent = new TypedValue(); TypedValue textColorSecondary = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.colorAccent, colorAccent, true); context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, textColorSecondary, true); int colorAccentValue; try { colorAccentValue = context.getResources().getColor(colorAccent.resourceId); } catch (Throwable ignore) { colorAccentValue = 0xFF009688; // default colorAccent for material light } int textColorSecondaryValue; try { textColorSecondaryValue = context.getResources().getColor(textColorSecondary.resourceId); } catch (Throwable ignore) { textColorSecondaryValue = 0x8A000000; // default textColorSecondary for material light } ((TextView) layout.findViewById(R.id.charge)).setTextColor(colorAccentValue); ((TextView) layout.findViewById(R.id.estimation)).setTextColor(textColorSecondaryValue); if (mChart == null || !(mChart instanceof UsageView)) { LinearLayout.LayoutParams usageViewLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, res.getDimensionPixelSize(R.dimen.battery_usage_height)); UsageView usageView = new UsageView(context, textColorSecondaryValue, colorAccentValue, true); usageView.setId(R.id.battery_usage); usageView.setLayoutParams(usageViewLp); usageView.setSideLabels(res.getResources().getTextArray(R.array.battery_labels)); usageView.findViewById(R.id.label_group).setAlpha(.7f); mChart = usageView; XposedHelpers.setAdditionalInstanceField(preference, KEY_CHART, usageView); } else { UsageView usageView = (UsageView) mChart; if (usageView.getParent() != null) { ((ViewGroup) usageView.getParent()).removeView(usageView); } mChart = usageView; } layout.addView((View) mChart); oldLayout.addView(layout); } BatteryInfo batteryInfo = (BatteryInfo) field; ((TextView) view.findViewById(R.id.charge)).setText(batteryInfo.batteryPercentString); ((TextView) view.findViewById(R.id.estimation)).setText(batteryInfo.remainingLabel); UsageView usageView = (UsageView) view.findViewById(R.id.battery_usage); batteryInfo.bindHistory(usageView); } } private static final XC_MethodHook onPreferenceTreeClickHook = new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { Preference preference = (Preference) param.args[1]; PreferenceFragment fragment = (PreferenceFragment) param.thisObject; XposedHook.logD(TAG, "onPreferenceTreeClick " + mHits[0]); final String LOG_TAG = "DeviceInfoSettings"; final String KEY_FIRMWARE_VERSION = "firmware_version"; if (preference.getKey().equals(KEY_FIRMWARE_VERSION)) { System.arraycopy(mHits, 1, mHits, 0, mHits.length - 1); mHits[mHits.length - 1] = SystemClock.uptimeMillis(); if (mHits[0] >= (SystemClock.uptimeMillis() - 500)) { if (ConfigUtils.M) { UserManager um = (UserManager) fragment.getActivity().getSystemService(Context.USER_SERVICE); if (um.hasUserRestriction(UserManager.DISALLOW_FUN)) { Log.d(LOG_TAG, "Sorry, no fun for you!"); param.setResult(false); } } Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClassName("tk.wasdennnoch.androidn_ify", PlatLogoActivity.class.getName()); try { fragment.startActivity(intent); } catch (Exception e) { Log.e(LOG_TAG, "Unable to start activity " + intent.toString()); } param.setResult(true); } } } }; // LP private static final XC_MethodHook onCreateViewHook = new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { View view = (View) param.getResult(); Context context = view.getContext(); ResourceUtils res = ResourceUtils.getInstance(context); LinearLayout allDetails = (LinearLayout) view.findViewById( context.getResources().getIdentifier("all_details", "id", XposedHook.PACKAGE_SETTINGS)); LinearLayout.LayoutParams panelLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); LinearLayout panel = new LinearLayout(context); panel.setLayoutParams(panelLp); panel.setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams headerLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); //TextView header = new TextView(new ContextThemeWrapper(context, com.android.internal.R.style.Widget_DeviceDefault_TextView_ListSeparator), null, 0); //TextView header = new TextView(context, null, com.android.internal.R.attr.listSeparatorTextViewStyle, com.android.internal.R.style.Widget_DeviceDefault_TextView_ListSeparator); TextView header = new TextView(context, null, com.android.internal.R.attr.listSeparatorTextViewStyle); header.setLayoutParams(headerLp); header.setText(res.getString(R.string.store)); LinearLayout.LayoutParams contentLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); //int ps = context.getResources().getDimensionPixelSize(attrToDimension(context, com.android.internal.R.attr.listPreferredItemPaddingStart)); //int pe = context.getResources().getDimensionPixelSize(attrToDimension(context, com.android.internal.R.attr.listPreferredItemPaddingEnd)); int ps = context.getResources().getDimensionPixelSize(attrToDimension(context, context.getResources().getIdentifier("listPreferredItemPaddingStart", "attr", XposedHook.PACKAGE_ANDROID))); int pe = context.getResources().getDimensionPixelSize(attrToDimension(context, context.getResources().getIdentifier("listPreferredItemPaddingEnd", "attr", XposedHook.PACKAGE_ANDROID))); TextView content = new TextView(context); content.setLayoutParams(contentLp); content.setPadding(ps, 0, pe, 0); panel.addView(header); panel.addView(content); allDetails.addView(panel); XposedHelpers.setAdditionalInstanceField(param.thisObject, KEY_APP_DETAILS, content); } }; private static int attrToDimension(Context context, int attr) { TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(attr, typedValue, true); TypedArray a = context.obtainStyledAttributes(typedValue.resourceId, new int[]{attr}); int textSize = a.getDimensionPixelSize(0, 0); a.recycle(); return textSize; } // MM private static final XC_MethodHook onActivityCreatedHook = new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { PreferenceFragment fragment = (PreferenceFragment) param.thisObject; if (XposedHelpers.getAdditionalInstanceField(fragment, KEY_APP_DETAILS_CATEGORY) == null) { Activity context = fragment.getActivity(); ResourceUtils res = ResourceUtils.getInstance(context); PreferenceScreen root = fragment.getPreferenceScreen(); PreferenceCategory storeCat = new PreferenceCategory(context); storeCat.setTitle(res.getString(R.string.store)); root.addPreference(storeCat); Preference appDetailsPreference = new Preference(context); appDetailsPreference.setTitle(res.getString(R.string.app_details)); appDetailsPreference.setEnabled(false); appDetailsPreference.setKey(KEY_APP_DETAILS); storeCat.addPreference(appDetailsPreference); XposedHelpers.setAdditionalInstanceField(fragment, KEY_APP_DETAILS_CATEGORY, storeCat); } } }; private static final XC_MethodHook refreshUiHook = new XC_MethodHook() { @SuppressWarnings("ConstantConditions") @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { if (!(boolean) param.getResult()) return; Context context; String packageName; PackageManager pm; Preference appDetailsPreference = null; TextView lpText = null; if (ConfigUtils.M) { PreferenceCategory storeCat = (PreferenceCategory) XposedHelpers.getAdditionalInstanceField(param.thisObject, KEY_APP_DETAILS_CATEGORY); if (storeCat == null) return; appDetailsPreference = storeCat.findPreference(KEY_APP_DETAILS); if (appDetailsPreference == null) return; packageName = (String) XposedHelpers.getObjectField(param.thisObject, "mPackageName"); } else { lpText = (TextView) XposedHelpers.getAdditionalInstanceField(param.thisObject, KEY_APP_DETAILS); if (lpText == null) return; packageName = ((PackageInfo) XposedHelpers.getObjectField(param.thisObject, "mPackageInfo")).packageName; } context = ((Fragment) param.thisObject).getActivity(); pm = (PackageManager) XposedHelpers.getObjectField(param.thisObject, "mPm"); String installerName = pm.getInstallerPackageName(packageName); ResourceUtils res = ResourceUtils.getInstance(context); String text; if (installerName != null) { try { ApplicationInfo applicationInfo = pm.getApplicationInfo(installerName, 0); String installerLabel = (String) pm.getApplicationLabel(applicationInfo); text = String.format(res.getString(R.string.install_source), installerLabel); } catch (Throwable ignore) { text = res.getString(R.string.from_pm); } } else { text = res.getString(R.string.from_pm); } if (ConfigUtils.M) appDetailsPreference.setSummary(text); else lpText.setText(text); } }; public static void hookRes(XC_InitPackageResources.InitPackageResourcesParam resparam, String modulePath) { ConfigUtils.SettingsConfig config = ConfigUtils.settings(); if (config.n_style_dashboard) { mDashboardHooks.hookRes(resparam, modulePath); } if (config.enable_drawer) { mDrawerHooks.hookRes(resparam, modulePath); } } }