package de.robv.android.xposed.mods.appsettings.hooks;
import static de.robv.android.xposed.XposedBridge.hookAllConstructors;
import static de.robv.android.xposed.XposedBridge.hookMethod;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
import static de.robv.android.xposed.XposedHelpers.findClass;
import static de.robv.android.xposed.XposedHelpers.findMethodExact;
import static de.robv.android.xposed.XposedHelpers.getAdditionalInstanceField;
import static de.robv.android.xposed.XposedHelpers.getObjectField;
import static de.robv.android.xposed.XposedHelpers.getStaticIntField;
import static de.robv.android.xposed.XposedHelpers.setAdditionalInstanceField;
import static de.robv.android.xposed.XposedHelpers.setIntField;
import java.lang.reflect.Method;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.inputmethodservice.InputMethodService;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.mods.appsettings.Common;
import de.robv.android.xposed.mods.appsettings.XposedMod;
public class Activities {
private static final String PROP_FULLSCREEN = "AppSettings-Fullscreen";
private static final String PROP_IMMERSIVE = "AppSettings-Immersive";
private static final String PROP_KEEP_SCREEN_ON = "AppSettings-KeepScreenOn";
private static final String PROP_LEGACY_MENU = "AppSettings-LegacyMenu";
private static final String PROP_ORIENTATION = "AppSettings-Orientation";
private static int FLAG_NEEDS_MENU_KEY = getStaticIntField(WindowManager.LayoutParams.class, "FLAG_NEEDS_MENU_KEY");
public static void hookActivitySettings() {
try {
findAndHookMethod("com.android.internal.policy.impl.PhoneWindow", null, "generateLayout",
"com.android.internal.policy.impl.PhoneWindow.DecorView", new XC_MethodHook() {
@SuppressLint("InlinedApi")
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Window window = (Window) param.thisObject;
View decorView = (View) param.args[0];
Context context = window.getContext();
String packageName = context.getPackageName();
if (!XposedMod.isActive(packageName))
return;
int fullscreen;
try {
fullscreen = XposedMod.prefs.getInt(packageName + Common.PREF_FULLSCREEN,
Common.FULLSCREEN_DEFAULT);
} catch (ClassCastException ex) {
// Legacy boolean setting
fullscreen = XposedMod.prefs.getBoolean(packageName + Common.PREF_FULLSCREEN, false)
? Common.FULLSCREEN_FORCE : Common.FULLSCREEN_DEFAULT;
}
if (fullscreen == Common.FULLSCREEN_FORCE) {
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setAdditionalInstanceField(window, PROP_FULLSCREEN, Boolean.TRUE);
} else if (fullscreen == Common.FULLSCREEN_PREVENT) {
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setAdditionalInstanceField(window, PROP_FULLSCREEN, Boolean.FALSE);
} else if (fullscreen == Common.FULLSCREEN_IMMERSIVE && Build.VERSION.SDK_INT >= 19) {
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
setAdditionalInstanceField(window, PROP_FULLSCREEN, Boolean.TRUE);
setAdditionalInstanceField(decorView, PROP_IMMERSIVE, Boolean.TRUE);
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
if (XposedMod.prefs.getBoolean(packageName + Common.PREF_NO_TITLE, false))
window.requestFeature(Window.FEATURE_NO_TITLE);
if (XposedMod.prefs.getBoolean(packageName + Common.PREF_ALLOW_ON_LOCKSCREEN, false))
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
if (XposedMod.prefs.getBoolean(packageName + Common.PREF_SCREEN_ON, false)) {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setAdditionalInstanceField(window, PROP_KEEP_SCREEN_ON, Boolean.TRUE);
}
if (XposedMod.prefs.getBoolean(packageName + Common.PREF_LEGACY_MENU, false)) {
window.setFlags(FLAG_NEEDS_MENU_KEY, FLAG_NEEDS_MENU_KEY);
setAdditionalInstanceField(window, PROP_LEGACY_MENU, Boolean.TRUE);
}
int orientation = XposedMod.prefs.getInt(packageName + Common.PREF_ORIENTATION, XposedMod.prefs.getInt(Common.PREF_DEFAULT + Common.PREF_ORIENTATION, 0));
if (orientation > 0 && orientation < Common.orientationCodes.length && context instanceof Activity) {
((Activity) context).setRequestedOrientation(Common.orientationCodes[orientation]);
setAdditionalInstanceField(context, PROP_ORIENTATION, orientation);
}
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
try {
findAndHookMethod(Window.class, "setFlags", int.class, int.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
int flags = (Integer) param.args[0];
int mask = (Integer) param.args[1];
if ((mask & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0) {
Boolean fullscreen = (Boolean) getAdditionalInstanceField(param.thisObject, PROP_FULLSCREEN);
if (fullscreen != null) {
if (fullscreen.booleanValue()) {
flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
} else {
flags &= ~WindowManager.LayoutParams.FLAG_FULLSCREEN;
}
param.args[0] = flags;
}
}
if ((mask & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0) {
Boolean keepScreenOn = (Boolean) getAdditionalInstanceField(param.thisObject, PROP_KEEP_SCREEN_ON);
if (keepScreenOn != null) {
if (keepScreenOn.booleanValue()) {
flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
}
param.args[0] = flags;
}
}
if ((mask & FLAG_NEEDS_MENU_KEY) != 0) {
Boolean menu = (Boolean) getAdditionalInstanceField(param.thisObject, PROP_LEGACY_MENU);
if (menu != null) {
if (menu.booleanValue()) {
flags |= FLAG_NEEDS_MENU_KEY;
}
param.args[0] = flags;
}
}
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
if (Build.VERSION.SDK_INT >= 19) {
try {
findAndHookMethod("android.view.ViewRootImpl", null, "dispatchSystemUiVisibilityChanged",
int.class, int.class, int.class, int.class, new XC_MethodHook() {
@TargetApi(19)
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// Has the navigation bar been shown?
int localChanges = (Integer) param.args[3];
if ((localChanges & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0)
return;
// Should it be hidden?
View decorView = (View) getObjectField(param.thisObject, "mView");
Boolean immersive = (decorView == null)
? null
: (Boolean) getAdditionalInstanceField(decorView, PROP_IMMERSIVE);
if (immersive == null || !immersive.booleanValue())
return;
// Enforce SYSTEM_UI_FLAG_HIDE_NAVIGATION and hide changes to this flag
int globalVisibility = (Integer) param.args[1];
int localValue = (Integer) param.args[2];
param.args[1] = globalVisibility | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
param.args[2] = localValue | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
param.args[3] = localChanges & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
}
try {
findAndHookMethod(Activity.class, "setRequestedOrientation", int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Integer orientation = (Integer) getAdditionalInstanceField(param.thisObject, PROP_ORIENTATION);
if (orientation != null)
param.args[0] = Common.orientationCodes[orientation];
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
try {
// Hook one of the several variations of ActivityStack.realStartActivityLocked from different ROMs
Method mthRealStartActivityLocked;
if (Build.VERSION.SDK_INT <= 18) {
try {
mthRealStartActivityLocked = findMethodExact("com.android.server.am.ActivityStack", null, "realStartActivityLocked",
"com.android.server.am.ActivityRecord", "com.android.server.am.ProcessRecord",
boolean.class, boolean.class, boolean.class);
} catch (NoSuchMethodError t) {
mthRealStartActivityLocked = findMethodExact("com.android.server.am.ActivityStack", null, "realStartActivityLocked",
"com.android.server.am.ActivityRecord", "com.android.server.am.ProcessRecord",
boolean.class, boolean.class);
}
} else {
mthRealStartActivityLocked = findMethodExact("com.android.server.am.ActivityStackSupervisor", null, "realStartActivityLocked",
"com.android.server.am.ActivityRecord", "com.android.server.am.ProcessRecord",
boolean.class, boolean.class);
}
hookMethod(mthRealStartActivityLocked, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String pkgName = (String) getObjectField(param.args[0], "packageName");
if (XposedMod.isActive(pkgName, Common.PREF_RESIDENT)) {
int adj = -12;
Object proc = getObjectField(param.args[0], "app");
// Override the *Adj values if meant to be resident in memory
if (proc != null) {
setIntField(proc, "maxAdj", adj);
if (Build.VERSION.SDK_INT <= 18)
setIntField(proc, "hiddenAdj", adj);
setIntField(proc, "curRawAdj", adj);
setIntField(proc, "setRawAdj", adj);
setIntField(proc, "curAdj", adj);
setIntField(proc, "setAdj", adj);
}
}
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
try {
hookAllConstructors(findClass("com.android.server.am.ActivityRecord", null), new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
ActivityInfo aInfo = (ActivityInfo) getObjectField(param.thisObject, "info");
if (aInfo == null)
return;
String pkgName = aInfo.packageName;
if (XposedMod.prefs.getInt(pkgName + Common.PREF_RECENTS_MODE, Common.PREF_RECENTS_DEFAULT) > 0) {
int recentsMode = XposedMod.prefs.getInt(pkgName + Common.PREF_RECENTS_MODE, Common.PREF_RECENTS_DEFAULT);
if (recentsMode == Common.PREF_RECENTS_DEFAULT)
return;
Intent intent = (Intent) getObjectField(param.thisObject, "intent");
if (recentsMode == Common.PREF_RECENTS_FORCE) {
int flags = (intent.getFlags() & ~Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.setFlags(flags);
}
else if (recentsMode == Common.PREF_RECENTS_PREVENT)
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
}
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
try {
findAndHookMethod(InputMethodService.class, "doStartInput",
InputConnection.class, EditorInfo.class, boolean.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
EditorInfo info = (EditorInfo) param.args[1];
if (info != null && info.packageName != null) {
XposedMod.prefs.reload();
if (XposedMod.isActive(info.packageName, Common.PREF_NO_FULLSCREEN_IME))
info.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
}
}
});
} catch (Throwable e) {
XposedBridge.log(e);
}
}
}