package tk.wasdennnoch.androidn_ify.systemui;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.XModuleResources;
import android.os.Handler;
import android.os.Process;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.widget.Toast;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import de.robv.android.xposed.XC_MethodHook;
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.misc.SafeRunnable;
import tk.wasdennnoch.androidn_ify.systemui.notifications.NotificationPanelHooks;
import tk.wasdennnoch.androidn_ify.systemui.notifications.StatusBarHeaderHooks;
import tk.wasdennnoch.androidn_ify.systemui.qs.QSTileHostHooks;
import tk.wasdennnoch.androidn_ify.systemui.qs.TilesManager;
import tk.wasdennnoch.androidn_ify.systemui.qs.tiles.misc.BatteryInfoManager;
import tk.wasdennnoch.androidn_ify.ui.AddTileActivity;
import tk.wasdennnoch.androidn_ify.ui.PlatLogoActivity;
import tk.wasdennnoch.androidn_ify.ui.SettingsActivity;
import tk.wasdennnoch.androidn_ify.utils.ConfigUtils;
import tk.wasdennnoch.androidn_ify.utils.RomUtils;
@SuppressLint("StaticFieldLeak")
public class SystemUIHooks {
private static final String TAG = "SystemUIHooks";
private static final String CLASS_SYSTEMUI_APPLICATION = "com.android.systemui.SystemUIApplication";
private static final String CLASS_PHONE_STATUS_BAR = "com.android.systemui.statusbar.phone.PhoneStatusBar";
private static ClassLoader mClassLoader;
private static Context mContext;
private static Object mPhoneStatusBar;
private static Handler mHandler;
public static BatteryInfoManager batteryInfoManager;
public static int R_drawable_ic_qs_data_disabled;
public static int R_drawable_stat_sys_data_disabled;
public static void hookSystemUI(ClassLoader classLoader) {
mClassLoader = classLoader;
hookStart(classLoader);
XposedHelpers.findAndHookMethod(CLASS_SYSTEMUI_APPLICATION, classLoader, "onCreate", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedHook.logD(TAG, "SystemUIApplication created, init");
final Application app = (Application) param.thisObject;
final Handler handler = new Handler(app.getMainLooper());
TilesManager.setNekoEnabled(PreferenceManager.getDefaultSharedPreferences(app).getBoolean("enable_neko", false));
batteryInfoManager = new BatteryInfoManager(app);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SettingsActivity.ACTION_GENERAL);
intentFilter.addAction(SettingsActivity.ACTION_FIX_INVERSION);
intentFilter.addAction(SettingsActivity.ACTION_KILL_SYSTEMUI);
intentFilter.addAction(AddTileActivity.ACTION_ADD_TILE);
intentFilter.addAction(PlatLogoActivity.ACTION_TOGGLE_NEKO);
intentFilter.addAction(XposedHook.ACTION_MARK_UNSTABLE);
app.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
XposedHook.logD(TAG, "Broadcast received, action: " + intent.getAction());
switch (intent.getAction()) {
case SettingsActivity.ACTION_GENERAL:
if (intent.hasExtra(SettingsActivity.EXTRA_GENERAL_DEBUG_LOG)) {
XposedHook.debug = intent.getBooleanExtra(SettingsActivity.EXTRA_GENERAL_DEBUG_LOG, false);
XposedHook.logI(TAG, "Debug log " + (XposedHook.debug ? "enabled" : "disabled"));
}
break;
case SettingsActivity.ACTION_FIX_INVERSION:
Settings.Secure.putInt(app.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0);
break;
case SettingsActivity.ACTION_KILL_SYSTEMUI:
handler.postDelayed(new Runnable() {
@Override
public void run() {
Process.killProcess(Process.myPid());
}
}, 100);
break;
case AddTileActivity.ACTION_ADD_TILE:
if (intent.hasExtra(AddTileActivity.EXTRA_TILE_SPEC)) {
NotificationPanelHooks.invalidateTileAdapter();
QSTileHostHooks.addSpec(context, intent.getStringExtra(AddTileActivity.EXTRA_TILE_SPEC));
if (!ConfigUtils.M)
QSTileHostHooks.recreateTiles();
}
NotificationPanelHooks.expandWithQs();
NotificationPanelHooks.showQsCustomizer(StatusBarHeaderHooks.mRecords, true);
break;
case PlatLogoActivity.ACTION_TOGGLE_NEKO:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean enableNeko;
enableNeko = !prefs.getBoolean("enable_neko", false);
prefs.edit().putBoolean("enable_neko", enableNeko).apply();
toastUp(enableNeko ? "\uD83D\uDC31" : "\uD83D\uDEAB", app);
TilesManager.setNekoEnabled(enableNeko);
NotificationPanelHooks.invalidateTileAdapter();
break;
case XposedHook.ACTION_MARK_UNSTABLE:
XposedHook.markUnstable();
break;
}
}
}, intentFilter);
// Give the settings enough time to load in the background
handler.postDelayed(new SafeRunnable() {
@Override
public void runSafe() {
XposedHook.debug = ConfigUtils.getInstance().getPrefs().getBoolean("debug_log", false);
//RomUtils.init(ConfigUtils.getInstance().getPrefs());
RomUtils.initRemote();
}
}, 2000);
}
});
}
static private void toastUp(String s, Application app) {
Toast toast = Toast.makeText(app, s, Toast.LENGTH_SHORT);
toast.getView().setBackgroundDrawable(null);
toast.show();
}
public static void hookResSystemUI(XC_InitPackageResources.InitPackageResourcesParam resparam, String modulePath) {
XModuleResources modRes = XModuleResources.createInstance(modulePath, resparam.res);
R_drawable_ic_qs_data_disabled = resparam.res.addResource(modRes, R.drawable.ic_qs_data_disabled);
R_drawable_stat_sys_data_disabled = resparam.res.addResource(modRes, R.drawable.stat_sys_data_disabled);
}
private static void hookStart(ClassLoader classLoader) {
XposedHelpers.findAndHookMethod(CLASS_PHONE_STATUS_BAR, classLoader, "start", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
mPhoneStatusBar = param.thisObject;
mHandler = (Handler) XposedHelpers.getObjectField(mPhoneStatusBar, "mHandler");
mContext = (Context) XposedHelpers.getObjectField(mPhoneStatusBar, "mContext");
}
});
}
public static void startRunnableDismissingKeyguard(final Runnable runnable) {
mHandler.post(new Runnable() {
@Override
public void run() {
try {
if (ConfigUtils.M) {
XposedHelpers.setBooleanField(mPhoneStatusBar, "mLeaveOpenOnKeyguardHide", true);
XposedHelpers.callMethod(mPhoneStatusBar, "executeRunnableDismissingKeyguard", runnable, null, false, false);
} else {
XposedHelpers.callMethod(mPhoneStatusBar, "dismissKeyguardThenExecute", createOnDismissAction(runnable), true);
}
} catch (Throwable t) {
XposedHook.logE(TAG, "Error in startRunnableDismissingKeyguard, executing instantly (" + t.toString() + ")", null);
runnable.run();
}
}
});
}
public static void post(Runnable r) {
mHandler.post(r);
}
private static Object createOnDismissAction(final Runnable runnable) {
Class<?> classOnDismissAction = XposedHelpers.findClass("com.android.keyguard.KeyguardHostView.OnDismissAction", mClassLoader);
return Proxy.newProxyInstance(mContext.getClassLoader(), new Class<?>[]{classOnDismissAction}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
switch (method.getName()) {
case "onDismiss":
runnable.run();
}
return false;
}
});
}
}