diff --git a/RotationControl/build.gradle.kts b/RotationControl/build.gradle.kts index 581ca0d..72a97fb 100644 --- a/RotationControl/build.gradle.kts +++ b/RotationControl/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.buildlogic.android.application) + alias(libs.plugins.kotlin.android) } android { @@ -9,6 +10,11 @@ android { defaultConfig { applicationId = packageName minSdk = 24 - targetSdk = 33 + targetSdk = 36 } } + +dependencies { + implementation(libs.androidx.fragment.ktx) + implementation(libs.androidx.preference.ktx) +} diff --git a/RotationControl/src/main/AndroidManifest.xml b/RotationControl/src/main/AndroidManifest.xml index f84483a..b8980a2 100644 --- a/RotationControl/src/main/AndroidManifest.xml +++ b/RotationControl/src/main/AndroidManifest.xml @@ -2,18 +2,31 @@ - + + + + + + + + = mutableListOf() + + @SuppressLint("WorldReadableFiles") + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME + preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE + setPreferencesFromResource(R.xml.root_preferences, rootKey) + val preferenceCategory = findPreference("category_rotation_mode")!! + val context = requireContext() + + for (rotationMode in ROTATION_MODE.entries) { + val preference = RadioPreference(context) + preference.key = rotationMode.key + preference.title = rotationMode.title + preference.summary = rotationMode.summary + preference.setDefaultValue(rotationMode == ROTATION_MODE_DEFAULT) + radioPreferences.add(preference) + preference.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { preference, newValue -> + radioPreferences.forEach { it.onRadioPreferenceSelected(preference as RadioPreference) } + true + } + preferenceCategory.addPreference(preference) + if (rotationMode in rewriteLockedOrientation.keys) preference.dependency = "rewrite_locked_orientations" + if (rotationMode in rewriteSensorOrientation.keys) preference.dependency = "rewrite_sensor_orientations" + } + + preferenceScreen.setIconSpaceReservedRecursive() + } + + private fun Preference.setIconSpaceReservedRecursive(iconSpaceReserved: Boolean = false) { + this.isIconSpaceReserved = iconSpaceReserved + if (this is PreferenceGroup) this.children.forEach { it.setIconSpaceReservedRecursive(iconSpaceReserved) } + } + } +} diff --git a/RotationControl/src/main/java/com/programminghoch10/RotationControl/XposedHook.java b/RotationControl/src/main/java/com/programminghoch10/RotationControl/XposedHook.java deleted file mode 100644 index 05cdd98..0000000 --- a/RotationControl/src/main/java/com/programminghoch10/RotationControl/XposedHook.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.programminghoch10.RotationControl; - -import android.app.Activity; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.pm.ActivityInfo; -import android.view.Window; - -import de.robv.android.xposed.IXposedHookLoadPackage; -import de.robv.android.xposed.XC_MethodHook; -import de.robv.android.xposed.XposedHelpers; -import de.robv.android.xposed.callbacks.XC_LoadPackage; - -public class XposedHook implements IXposedHookLoadPackage { - @Override - public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { - XposedHelpers.findAndHookMethod( - Activity.class, "setRequestedOrientation", int.class, new XC_MethodHook() { - @Override - protected void beforeHookedMethod(MethodHookParam param) { - param.args[0] = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR; - } - } - ); - XposedHelpers.findAndHookMethod( - "com.android.internal.policy.PhoneWindow", - lpparam.classLoader, - "generateLayout", - "com.android.internal.policy.DecorView", - new XC_MethodHook() { - @Override - protected void beforeHookedMethod(MethodHookParam param) { - Context context = ((Window) param.thisObject).getContext(); - - while (context instanceof ContextWrapper) { - if (context instanceof Activity activity) { - activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); - return; - } - context = ((ContextWrapper) context).getBaseContext(); - } - } - } - ); - } -} diff --git a/RotationControl/src/main/java/com/programminghoch10/RotationControl/XposedHook.kt b/RotationControl/src/main/java/com/programminghoch10/RotationControl/XposedHook.kt new file mode 100644 index 0000000..03fde61 --- /dev/null +++ b/RotationControl/src/main/java/com/programminghoch10/RotationControl/XposedHook.kt @@ -0,0 +1,61 @@ +package com.programminghoch10.RotationControl + +import android.app.Activity +import android.content.ContextWrapper +import android.content.SharedPreferences +import android.content.pm.ActivityInfo +import android.view.Window +import de.robv.android.xposed.IXposedHookLoadPackage +import de.robv.android.xposed.XC_MethodHook +import de.robv.android.xposed.XSharedPreferences +import de.robv.android.xposed.XposedHelpers +import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam + +class XposedHook : IXposedHookLoadPackage { + override fun handleLoadPackage(lpparam: LoadPackageParam) { + if (lpparam.packageName == BuildConfig.APPLICATION_ID) return + + XposedHelpers.findAndHookMethod( + Activity::class.java, + "setRequestedOrientation", + Int::class.java, + object : XC_MethodHook() { + override fun beforeHookedMethod(param: MethodHookParam) { + val originalRotationMode = ROTATION_MODE.entries.find { it.value == param.args[0] } ?: ROTATION_MODE.SCREEN_ORIENTATION_UNSET + val sharedPreferences: SharedPreferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME) + var selectedRotationMode = ROTATION_MODE.entries.find { sharedPreferences.getBoolean(it.key, false) } ?: ROTATION_MODE_DEFAULT + if (selectedRotationMode == ROTATION_MODE.SCREEN_ORIENTATION_UNSET) selectedRotationMode = originalRotationMode + if (sharedPreferences.getBoolean("rewrite_locked_orientations", false)) { + selectedRotationMode = rewriteLockedOrientation.get(selectedRotationMode) ?: selectedRotationMode + } + if (sharedPreferences.getBoolean("rewrite_sensor_orientations", false)) { + selectedRotationMode = rewriteSensorOrientation.get(selectedRotationMode) ?: selectedRotationMode + } + if (selectedRotationMode == ROTATION_MODE.SCREEN_ORIENTATION_UNSET) return + param.args[0] = selectedRotationMode.value + } + }, + ) + + XposedHelpers.findAndHookMethod( + "com.android.internal.policy.PhoneWindow", + lpparam.classLoader, + "generateLayout", + "com.android.internal.policy.DecorView", + object : XC_MethodHook() { + override fun beforeHookedMethod(param: MethodHookParam) { + var context = (param.thisObject as Window).context + while (context is ContextWrapper) { + if (context is Activity) { + // we only need to call setRequestedOrientation + // the value doesn't matter, since the hook above replaces it + context.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + return + } + context = context.baseContext + } + } + }, + ) + } +} diff --git a/RotationControl/src/main/res/layout/radio.xml b/RotationControl/src/main/res/layout/radio.xml new file mode 100644 index 0000000..41de0b3 --- /dev/null +++ b/RotationControl/src/main/res/layout/radio.xml @@ -0,0 +1,9 @@ + + diff --git a/RotationControl/src/main/res/layout/settings_activity.xml b/RotationControl/src/main/res/layout/settings_activity.xml new file mode 100644 index 0000000..c716b52 --- /dev/null +++ b/RotationControl/src/main/res/layout/settings_activity.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/RotationControl/src/main/res/values-v21/themes.xml b/RotationControl/src/main/res/values-v21/themes.xml new file mode 100644 index 0000000..e15c2b9 --- /dev/null +++ b/RotationControl/src/main/res/values-v21/themes.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/RotationControl/src/main/res/xml/root_preferences.xml b/RotationControl/src/main/res/xml/root_preferences.xml new file mode 100644 index 0000000..d4e59b8 --- /dev/null +++ b/RotationControl/src/main/res/xml/root_preferences.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c816fe7..6c57813 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ androidTools = "31.12.1" androidxAnnotation = "1.9.1" collection = "1.5.0" core = "1.17.0" +fragment = "1.8.9" githubApi = "1.329" hiddenapibypass = "6.1" jebrainsAnnotations = "26.0.2" @@ -20,7 +21,9 @@ android-tools-common = { module = "com.android.tools:common", version.ref = "and androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidxAnnotation" } androidx-collection-ktx = { module = "androidx.collection:collection-ktx", version.ref = "collection" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "core" } +androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "fragment" } androidx-preference = { group = "androidx.preference", name = "preference", version.ref = "preference" } +androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference" } github-api = { module = "org.kohsuke:github-api", version.ref = "githubApi" } hiddenapibypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenapibypass" } jebtrains-annotations = { module = "org.jetbrains:annotations", version.ref = "jebrainsAnnotations" }