diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
index 9969487..4d0cdf6 100644
--- a/.idea/codeStyles/Project.xml
+++ b/.idea/codeStyles/Project.xml
@@ -5,24 +5,26 @@
+
-
+
-
+
diff --git a/AlwaysAllowMultiInstanceSplit/Readme.md b/AlwaysAllowMultiInstanceSplit/Readme.md
deleted file mode 100644
index 851e246..0000000
--- a/AlwaysAllowMultiInstanceSplit/Readme.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# AlwaysAllowMultiInstanceSplit
-
-Allow all apps to be launched twice side-by-side in a split screen.
-
-Undefined behaviour ahead!
-
-
diff --git a/AlwaysAllowMultiInstanceSplit/build.gradle.kts b/AlwaysAllowMultiInstanceSplit/build.gradle.kts
deleted file mode 100644
index c23eaed..0000000
--- a/AlwaysAllowMultiInstanceSplit/build.gradle.kts
+++ /dev/null
@@ -1,18 +0,0 @@
-plugins {
- alias(libs.plugins.buildlogic.android.application)
- alias(libs.plugins.buildlogic.kotlin.android)
-}
-
-android {
- namespace = "de.binarynoise.AlwaysAllowMultiInstanceSplit"
-
- defaultConfig {
- minSdk = 33
- targetSdk = 35
- }
-}
-
-dependencies {
- implementation(project(":reflection"))
- implementation(project(":logger"))
-}
diff --git a/AlwaysAllowMultiInstanceSplit/src/main/AndroidManifest.xml b/AlwaysAllowMultiInstanceSplit/src/main/AndroidManifest.xml
deleted file mode 100644
index 3667ace..0000000
--- a/AlwaysAllowMultiInstanceSplit/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/AlwaysAllowMultiInstanceSplit/src/main/assets/xposed_init b/AlwaysAllowMultiInstanceSplit/src/main/assets/xposed_init
deleted file mode 100644
index d9b11dd..0000000
--- a/AlwaysAllowMultiInstanceSplit/src/main/assets/xposed_init
+++ /dev/null
@@ -1 +0,0 @@
-de.binarynoise.AlwaysAllowMultiInstanceSplit.Hook
diff --git a/CodecMod/src/main/java/com/programminghoch10/CodecMod/CodecStore.java b/CodecMod/src/main/java/com/programminghoch10/CodecMod/CodecStore.java
index 0b93df7..cc821b8 100644
--- a/CodecMod/src/main/java/com/programminghoch10/CodecMod/CodecStore.java
+++ b/CodecMod/src/main/java/com/programminghoch10/CodecMod/CodecStore.java
@@ -19,7 +19,6 @@ public class CodecStore {
private final SharedPreferences sharedPreferences;
private final List receivers = new LinkedList<>();
- @SuppressLint("WorldReadableFiles")
CodecStore(Context context) {
this.sharedPreferences = context.getSharedPreferences(PREFERENCES, MODE_WORLD_READABLE);
}
diff --git a/KeepSplitScreenRatio/Readme.md b/KeepSplitScreenRatio/Readme.md
deleted file mode 100644
index d3fbc20..0000000
--- a/KeepSplitScreenRatio/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# KeepSplitScreenRatio
-
-Keep the split screen ratio, when switching one of the split apps.
-
-Only required on Android 14 and later, since previous versions did not force a split resize in this situation.
diff --git a/KeepSplitScreenRatio/build.gradle.kts b/KeepSplitScreenRatio/build.gradle.kts
deleted file mode 100644
index 5ba30dd..0000000
--- a/KeepSplitScreenRatio/build.gradle.kts
+++ /dev/null
@@ -1,13 +0,0 @@
-plugins {
- alias(libs.plugins.buildlogic.android.application)
- alias(libs.plugins.buildlogic.kotlin.android)
-}
-
-android {
- namespace = "com.programminghoch10.KeepSplitScreenRatio"
-
- defaultConfig {
- minSdk = 34
- targetSdk = 36
- }
-}
diff --git a/KeepSplitScreenRatio/src/main/AndroidManifest.xml b/KeepSplitScreenRatio/src/main/AndroidManifest.xml
deleted file mode 100644
index cd5b2b9..0000000
--- a/KeepSplitScreenRatio/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/KeepSplitScreenRatio/src/main/assets/xposed_init b/KeepSplitScreenRatio/src/main/assets/xposed_init
deleted file mode 100644
index 420f227..0000000
--- a/KeepSplitScreenRatio/src/main/assets/xposed_init
+++ /dev/null
@@ -1 +0,0 @@
-com.programminghoch10.KeepSplitScreenRatio.Hook
diff --git a/KeepSplitScreenRatio/src/main/java/com/programminghoch10/KeepSplitScreenRatio/Hook.kt b/KeepSplitScreenRatio/src/main/java/com/programminghoch10/KeepSplitScreenRatio/Hook.kt
deleted file mode 100644
index 1526f4c..0000000
--- a/KeepSplitScreenRatio/src/main/java/com/programminghoch10/KeepSplitScreenRatio/Hook.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.programminghoch10.KeepSplitScreenRatio
-
-import android.os.IBinder
-import de.robv.android.xposed.IXposedHookLoadPackage
-import de.robv.android.xposed.XposedHelpers
-import de.robv.android.xposed.callbacks.XC_LoadPackage
-import de.robv.android.xposed.XC_MethodHook as MethodHook
-
-class Hook : IXposedHookLoadPackage {
-
- override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
- if (lpparam.packageName != "com.android.systemui") return
-
- val SplitScreenTransitionsClass = XposedHelpers.findClass("com.android.wm.shell.splitscreen.SplitScreenTransitions", lpparam.classLoader)
- XposedHelpers.findAndHookMethod(
- SplitScreenTransitionsClass,
- "setEnterTransition",
- IBinder::class.java,
- "android.window.RemoteTransition",
- Int::class.java,
- Boolean::class.java,
- object : MethodHook() {
- override fun beforeHookedMethod(param: MethodHookParam) {
- param.args[3] = false
- }
- },
- )
- }
-}
diff --git a/KeepSplitScreenRatio/src/main/res/values/arrays.xml b/KeepSplitScreenRatio/src/main/res/values/arrays.xml
deleted file mode 100644
index f8495b0..0000000
--- a/KeepSplitScreenRatio/src/main/res/values/arrays.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- - com.android.systemui
-
-
diff --git a/PreventAudioFocus/src/main/java/com/programminghoch10/PreventAudioFocus/SettingsActivity.kt b/PreventAudioFocus/src/main/java/com/programminghoch10/PreventAudioFocus/SettingsActivity.kt
index 583206d..b4821ae 100644
--- a/PreventAudioFocus/src/main/java/com/programminghoch10/PreventAudioFocus/SettingsActivity.kt
+++ b/PreventAudioFocus/src/main/java/com/programminghoch10/PreventAudioFocus/SettingsActivity.kt
@@ -22,7 +22,6 @@ class SettingsActivity : FragmentActivity() {
}
class SettingsFragment : PreferenceFragmentCompat() {
- @SuppressLint("WorldReadableFiles")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
diff --git a/README.md b/README.md
index 1341bd3..be57bfb 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,6 @@ A collection of small Xposed Modules.
| Module | Author | Description | Releases |
|-|-|-|-|
| [AlwaysAllowChargingFeedback](AlwaysAllowChargingFeedback) | [@binarynoise](https://github.com/binarynoise) | Always allow charging feedback, even in DnD mode | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=AlwaysAllowChargingFeedback) |
-| [AlwaysAllowMultiInstanceSplit](AlwaysAllowMultiInstanceSplit) | [@binarynoise](https://github.com/binarynoise) & [@programminghoch10](https://github.com/programminghoch10) | Allow all apps to be launched twice in a split screen | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=AlwaysAllowMultiInstanceSplit) |
| [AnimationScaleMod](AnimationScaleMod) | [@programminghoch10](https://github.com/programminghoch10) | Add more animation scale multipliers | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=AnimationScaleMod) |
| [AntiBrightnessChange](AntiBrightnessChange) | [@programminghoch10](https://github.com/programminghoch10) | Prevent apps from changing display brightness | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=AntiBrightnessChange) [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.programminghoch10.AntiBrightnessChange) |
| [AutomaticAdvancedSettingsExpander](AutomaticAdvancedSettingsExpander) | [@binarynoise](https://github.com/binarynoise) | Automatically expands the advanced settings in the Settings app | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=AutomaticAdvancedSettingsExpander) [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/de.binarynoise.AutomaticAdvancedSettingsExpander) |
@@ -22,6 +21,7 @@ A collection of small Xposed Modules.
| [PersistentForegroundServiceNotifications](PersistentForegroundServiceNotifications) | [@binarynoise](https://github.com/binarynoise) | Make notifications of foreground services persistent again | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=persistentForegroundServiceNotifications) |
| [ResetAllNotificationChannels](ResetAllNotificationChannels) | [@binarynoise](https://github.com/binarynoise) | Reset all Notification Channels: vibrations, ringtones, importance etc. | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=resetAllNotificationChannels) [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/de.binarynoise.resetAllNotificationChannels) |
| [RotationControl](RotationControl) | [@programminghoch10](https://github.com/programminghoch10) | Force rotation for selected packages | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=RotationControl) [IzzyOnDroid](https://apt.izzysoft.de/fdroid/index/apk/com.programminghoch10.RotationControl) |
+| [SplitScreenMods](SplitScreenMods) | [@binarynoise](https://github.com/binarynoise) & [@programminghoch10](https://github.com/programminghoch10) | A collection of various SplitScreen modifications. | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=SplitScreenMods) |
| [UpsideWifi](UpsideWifi) | [@programminghoch10](https://github.com/programminghoch10) | Turn the WiFi icon upside down | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=UpsideWifi) |
| [VolumeStepsIncrease](VolumeStepsIncrease) | [@programminghoch10](https://github.com/programminghoch10) | Increase the amount of volume steps | [GitHub](https://github.com/binarynoise/XposedModulets/releases?q=VolumeStepsIncrease) |
diff --git a/RotationControl/src/main/java/com/programminghoch10/RotationControl/SettingsActivity.kt b/RotationControl/src/main/java/com/programminghoch10/RotationControl/SettingsActivity.kt
index 7c2d840..0769552 100644
--- a/RotationControl/src/main/java/com/programminghoch10/RotationControl/SettingsActivity.kt
+++ b/RotationControl/src/main/java/com/programminghoch10/RotationControl/SettingsActivity.kt
@@ -31,7 +31,6 @@ class SettingsActivity : FragmentActivity() {
class SettingsFragment : PreferenceFragmentCompat() {
val radioPreferences: MutableList = mutableListOf()
- @SuppressLint("WorldReadableFiles")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
diff --git a/SensorMod/src/main/java/com/programminghoch10/SensorMod/SettingsActivity.java b/SensorMod/src/main/java/com/programminghoch10/SensorMod/SettingsActivity.java
index ac121ec..0cfb46d 100644
--- a/SensorMod/src/main/java/com/programminghoch10/SensorMod/SettingsActivity.java
+++ b/SensorMod/src/main/java/com/programminghoch10/SensorMod/SettingsActivity.java
@@ -32,7 +32,6 @@ protected void onCreate(Bundle savedInstanceState) {
}
public static class SettingsFragment extends PreferenceFragmentCompat {
- @SuppressLint("WorldReadableFiles")
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
PreferenceManager preferenceManager = getPreferenceManager();
diff --git a/SplitScreenMods/Readme.md b/SplitScreenMods/Readme.md
new file mode 100644
index 0000000..4e6498a
--- /dev/null
+++ b/SplitScreenMods/Readme.md
@@ -0,0 +1,46 @@
+# SplitScreenMods
+
+A collection of various SplitScreen modifications.
+
+## AlwaysAllowMultiInstanceSplit
+
+Allow all apps to be launched twice side-by-side in a split screen.
+
+Undefined behaviour ahead!
+
+
+
+## KeepSplitScreenRatio
+
+Keep the split screen ratio, when switching one of the split apps.
+
+Only usable on Android 14 and later,
+since previous versions did not force a split resize in this situation.
+
+## KeepSwapRatio
+
+Keep the split ratio while swapping sides.
+
+## DisableSwapAnimation
+
+Disable the swapping animation.
+
+## SnapMode
+
+Change the snap mode used by the system:
+
+* `16:9` snapping useful for playing videos in split screen
+* `FIXED` snapping allows for custom ratios and [additional snap targets](#additionalsnaptargets)
+* `1:1` only allows the middle 50:50 split
+
+## FreeSnap
+
+Allow any split ratio instead of snapping to predefined ratios.
+
+## AdditionalSnapTargets
+
+Add additional snapping targets to snap to.
+
+## RemoveMinimalTaskSize
+
+Remove the minimal task size limit to allow split screen with unreasonably small apps.
diff --git a/SplitScreenMods/build.gradle.kts b/SplitScreenMods/build.gradle.kts
new file mode 100644
index 0000000..29f7a12
--- /dev/null
+++ b/SplitScreenMods/build.gradle.kts
@@ -0,0 +1,21 @@
+plugins {
+ alias(libs.plugins.buildlogic.android.application)
+ alias(libs.plugins.buildlogic.kotlin.android)
+}
+
+android {
+ namespace = "com.programminghoch10.SplitScreenMods"
+
+ defaultConfig {
+ minSdk = 33
+ targetSdk = 36
+ buildConfigField("String", "SHARED_PREFERENCES_NAME", "\"splitscreen\"")
+ }
+}
+
+dependencies {
+ implementation(libs.androidx.fragment.ktx)
+ implementation(libs.androidx.preference.ktx)
+ implementation(project(":logger"))
+ compileOnly(libs.androidx.performance.unsafe)
+}
diff --git a/SplitScreenMods/src/main/AndroidManifest.xml b/SplitScreenMods/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..b3547a0
--- /dev/null
+++ b/SplitScreenMods/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SplitScreenMods/src/main/assets/xposed_init b/SplitScreenMods/src/main/assets/xposed_init
new file mode 100644
index 0000000..7d67d9b
--- /dev/null
+++ b/SplitScreenMods/src/main/assets/xposed_init
@@ -0,0 +1,10 @@
+com.programminghoch10.SplitScreenMods.AdditionalSnapTargetsHook
+com.programminghoch10.SplitScreenMods.AlwaysAllowMultiInstanceSplitHook
+com.programminghoch10.SplitScreenMods.CalculateRatiosHook
+com.programminghoch10.SplitScreenMods.CustomFixedRatioHook
+com.programminghoch10.SplitScreenMods.DisableSwapAnimationHook
+com.programminghoch10.SplitScreenMods.FreeSnapHook
+com.programminghoch10.SplitScreenMods.KeepSplitScreenRatioHook
+com.programminghoch10.SplitScreenMods.KeepSwapRatioHook
+com.programminghoch10.SplitScreenMods.RemoveMinimalTaskSizeHook
+com.programminghoch10.SplitScreenMods.SnapModeHook
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/AdditionalSnapTargetsHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/AdditionalSnapTargetsHook.kt
new file mode 100644
index 0000000..b8224df
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/AdditionalSnapTargetsHook.kt
@@ -0,0 +1,134 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.content.res.Resources
+import android.graphics.Rect
+import android.os.Build
+import com.programminghoch10.SplitScreenMods.AdditionalSnapTargetsHookConfig.enabled
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookInitPackageResources
+import de.robv.android.xposed.IXposedHookLoadPackage
+import de.robv.android.xposed.XC_MethodHook
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.XposedBridge
+import de.robv.android.xposed.XposedHelpers
+import de.robv.android.xposed.callbacks.XC_InitPackageResources
+import de.robv.android.xposed.callbacks.XC_LoadPackage
+import sun.misc.Unsafe
+
+// https://github.com/LineageOS/android_frameworks_base/blob/f0964915f5992fd38cf1e5e3f87c0d3ac719aa09/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/split/SplitScreenConstants.java
+const val SNAP_TO_2_33_66 = 0
+const val SNAP_TO_2_50_50 = 1
+const val SNAP_TO_2_66_33 = 2
+const val SNAP_TO_NONE = 10
+const val SNAP_TO_START_AND_DISMISS = 11
+const val SNAP_TO_END_AND_DISMISS = 12
+
+object AdditionalSnapTargetsHookConfig {
+ val enabled = SnapModeHookConfig.enabled && CustomFixedRatioHookConfig.enabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+}
+
+class AdditionalSnapTargetsHook : IXposedHookLoadPackage, IXposedHookInitPackageResources {
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleLoadPackage(${lpparam.packageName} in process ${lpparam.processName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val selectedSnapTargetsString = preferences.getString("SnapTargets", "SYSTEM")!!
+ if (selectedSnapTargetsString == "SYSTEM" || selectedSnapTargetsString == "CUSTOM") return
+ val selectedSnapTargets = selectedSnapTargetsString.split(",").map { it.toFloat() }.sorted().drop(1)
+ log("additional snap targets are ${selectedSnapTargets.joinToString()}")
+
+ if (selectedSnapTargets.isEmpty()) return // handled by resource hook below
+
+ val DividerSnapAlgorithmClass = XposedHelpers.findClass("com.android.wm.shell.common.split.DividerSnapAlgorithm", lpparam.classLoader)
+ val SnapTargetClass = XposedHelpers.findClass(DividerSnapAlgorithmClass.name + "\$SnapTarget", lpparam.classLoader)
+ val UnsafeClass = XposedHelpers.findClass("sun.misc.Unsafe", lpparam.classLoader)
+ val unsafe = XposedHelpers.callStaticMethod(UnsafeClass, "getUnsafe") as Unsafe
+ fun createNewSnapTargetInstance(position: Int, snapPosition: Int): Any {
+ val snapTarget = unsafe.allocateInstance(SnapTargetClass)
+ XposedHelpers.setIntField(snapTarget, "position", position)
+ XposedHelpers.setIntField(snapTarget, "snapPosition", snapPosition)
+ XposedHelpers.setFloatField(snapTarget, "distanceMultiplier", 1f)
+ return snapTarget
+ }
+
+ XposedBridge.hookAllConstructors(DividerSnapAlgorithmClass, object : XC_MethodHook() {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ log("after ${DividerSnapAlgorithmClass.simpleName} constructor")
+ val res = param.args[0] as Resources
+ val mTargets = XposedHelpers.getObjectField(param.thisObject, "mTargets") as ArrayList
+ if (mTargets.isEmpty()) return
+
+ // reimplementation of inlined methods getStartInset() and getEndInset()
+ val mIsLeftRightSplit = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) XposedHelpers.getBooleanField(
+ param.thisObject,
+ "mIsLeftRightSplit",
+ )
+ else XposedHelpers.getBooleanField(param.thisObject, "mIsHorizontalDivision")
+ val mInsets = XposedHelpers.getObjectField(param.thisObject, "mInsets") as Rect
+ val startInset = if (mIsLeftRightSplit) mInsets.left else mInsets.top
+ val endInset = if (mIsLeftRightSplit) mInsets.right else mInsets.bottom
+
+ // basically reimplemented addFixedDivisionTargets
+ val mDisplayWidth = XposedHelpers.getIntField(param.thisObject, "mDisplayWidth")
+ val mDisplayHeight = XposedHelpers.getIntField(param.thisObject, "mDisplayHeight")
+ val start = startInset
+ val end = if (mIsLeftRightSplit) mDisplayWidth - endInset else mDisplayHeight - endInset
+ val mDividerSize = XposedHelpers.getIntField(param.thisObject, "mDividerSize")
+ val mCalculateRatiosBasedOnAvailableSpace = getCalculateRatiosBasedOnAvailableSpace(res)
+ log("mCalculateRatiosBasedOnAvailableSpace=$mCalculateRatiosBasedOnAvailableSpace")
+ val mMinimalSizeResizableTask = XposedHelpers.getIntField(param.thisObject, "mMinimalSizeResizableTask")
+ log("mMinimalSizeResizableTask=$mMinimalSizeResizableTask")
+ fun getSize(ratio: Float): Int {
+ var size = (ratio * (end - start)).toInt() - mDividerSize / 2
+ if (mCalculateRatiosBasedOnAvailableSpace) size = Math.max(size, mMinimalSizeResizableTask)
+ return size
+ }
+
+ for (snapTargetRatio in selectedSnapTargets) {
+ val size = getSize(snapTargetRatio)
+ val startPosition = start + size
+ val endPosition = end - size
+
+ // slightly changed reimplementation of inlined method maybeAddTarget()
+ fun maybeAddTarget(position: Int, size: Int, snapPosition: Int?) {
+ if (size < mMinimalSizeResizableTask) {
+ log("Cannot add snap target ${snapTargetRatio} because it's size of ${size} would be less than minimal size ${mMinimalSizeResizableTask}!")
+ return
+ }
+ log("adding snap target ${snapTargetRatio} at $position of size $size")
+ val snapTarget = createNewSnapTargetInstance(position, snapPosition ?: SNAP_TO_NONE)
+ mTargets.add(snapTarget)
+ }
+
+ maybeAddTarget(startPosition, startPosition - startInset, SNAP_TO_2_33_66)
+ maybeAddTarget(endPosition, end - endPosition, SNAP_TO_2_66_33)
+ }
+
+ log("final mTargets[${mTargets.size}]: ${mTargets.joinToString()}")
+ }
+ })
+ }
+
+ fun getCalculateRatiosBasedOnAvailableSpace(res: Resources): Boolean {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) return false
+ val mCalculateRatiosBasedOnAvailableSpaceId = res.getIdentifier("config_flexibleSplitRatios", "bool", "android")
+ return res.getBoolean(mCalculateRatiosBasedOnAvailableSpaceId)
+ }
+
+ override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ if (resparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleInitPackageResources(${resparam.packageName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val selectedSnapTargetsString = preferences.getString("SnapTargets", "SYSTEM")!!
+ if (selectedSnapTargetsString == "SYSTEM" || selectedSnapTargetsString == "CUSTOM") return
+ val selectedSnapTargets = selectedSnapTargetsString.split(",")
+ if (selectedSnapTargets.isEmpty()) return
+ val customRatio = selectedSnapTargets.map { it.toFloat() }.minOf { it }
+ log("min split ratio is ${customRatio}")
+ if (customRatio < 0 || customRatio >= 0.5f) return
+ CustomFixedRatioHook.overrideFixedRatio(resparam, customRatio)
+ }
+}
diff --git a/AlwaysAllowMultiInstanceSplit/src/main/java/de/binarynoise/AlwaysAllowMultiInstanceSplit/Hook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/AlwaysAllowMultiInstanceSplitHook.kt
similarity index 56%
rename from AlwaysAllowMultiInstanceSplit/src/main/java/de/binarynoise/AlwaysAllowMultiInstanceSplit/Hook.kt
rename to SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/AlwaysAllowMultiInstanceSplitHook.kt
index d06cda1..15c159d 100644
--- a/AlwaysAllowMultiInstanceSplit/src/main/java/de/binarynoise/AlwaysAllowMultiInstanceSplit/Hook.kt
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/AlwaysAllowMultiInstanceSplitHook.kt
@@ -1,33 +1,56 @@
-package de.binarynoise.AlwaysAllowMultiInstanceSplit
+package com.programminghoch10.SplitScreenMods
import android.content.ComponentName
import android.content.pm.ActivityInfo
import android.os.Build
+import com.programminghoch10.SplitScreenMods.AlwaysAllowMultiInstanceSplitHookConfig.enabled
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
import de.binarynoise.logger.Logger.log
import de.robv.android.xposed.IXposedHookLoadPackage
+import de.robv.android.xposed.XC_MethodHook
import de.robv.android.xposed.XC_MethodReplacement
+import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import de.robv.android.xposed.callbacks.XC_LoadPackage
-import de.robv.android.xposed.XC_MethodHook as MethodHook
-class Hook : IXposedHookLoadPackage {
+object AlwaysAllowMultiInstanceSplitHookConfig {
+ @JvmField
+ val enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+}
+
+class AlwaysAllowMultiInstanceSplitHook : IXposedHookLoadPackage {
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (!enabled) return
log("handleLoadPackage(${lpparam.packageName} in process ${lpparam.processName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val enabled = preferences.getBoolean("AlwaysAllowMultiInstanceSplit", false)
+ if (!enabled) return
when (lpparam.packageName) {
"com.android.systemui" -> {
try {
- val method = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
- XposedHelpers.findMethodExact(
- Class.forName("com.android.wm.shell.common.MultiInstanceHelper", false, lpparam.classLoader),
+ val method = when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA -> XposedHelpers.findMethodExact(
+ Class.forName(
+ "com.android.wm.shell.common.MultiInstanceHelper", false, lpparam.classLoader
+ ),
+ "supportsMultiInstanceSplit",
+ Int::class.java, // wrong order in compiled code
+ ComponentName::class.java,
+ )
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> XposedHelpers.findMethodExact(
+ Class.forName(
+ "com.android.wm.shell.common.MultiInstanceHelper", false, lpparam.classLoader
+ ),
"supportsMultiInstanceSplit",
ComponentName::class.java,
)
- } else {
- XposedHelpers.findMethodExact(
- Class.forName("com.android.wm.shell.splitscreen.SplitScreenController", false, lpparam.classLoader),
+ else -> XposedHelpers.findMethodExact(
+ Class.forName(
+ "com.android.wm.shell.splitscreen.SplitScreenController", false, lpparam.classLoader
+ ),
"supportMultiInstancesSplit",
ComponentName::class.java,
)
@@ -46,7 +69,7 @@ class Hook : IXposedHookLoadPackage {
ActivityStarterClass,
"executeRequest",
ActivityStarterRequestClass,
- object : MethodHook() {
+ object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam) {
val request = param.args[0]
val aInfo = XposedHelpers.getObjectField(request, "activityInfo") as ActivityInfo
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/CalculateRatiosHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/CalculateRatiosHook.kt
new file mode 100644
index 0000000..1bfd909
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/CalculateRatiosHook.kt
@@ -0,0 +1,27 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.os.Build
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.CalculateRatiosHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookInitPackageResources
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.callbacks.XC_InitPackageResources
+
+object CalculateRatiosHookConfig {
+ val enabled = SnapModeHookConfig.enabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+}
+
+class CalculateRatiosHook : IXposedHookInitPackageResources {
+ override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ if (resparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleInitPackageResources(${resparam.packageName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ if (!preferences.contains("CalculateRatios")) return
+
+ val enabled = preferences.getBoolean("CalculateRatios", false)
+ log("set config_flexibleSplitRatios to $enabled")
+ resparam.res.setReplacement("android", "bool", "config_flexibleSplitRatios", enabled)
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/CustomFixedRatioHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/CustomFixedRatioHook.kt
new file mode 100644
index 0000000..b4b1a43
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/CustomFixedRatioHook.kt
@@ -0,0 +1,50 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.content.res.AssetManager
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.content.res.XResForwarder
+import android.util.DisplayMetrics
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.CustomFixedRatioHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookInitPackageResources
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.XposedHelpers
+import de.robv.android.xposed.callbacks.XC_InitPackageResources
+
+object CustomFixedRatioHookConfig {
+ val enabled = SnapModeHookConfig.enabled
+}
+
+class CustomFixedRatioHook : IXposedHookInitPackageResources {
+ override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ if (resparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleInitPackageResources(${resparam.packageName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ if (preferences.getString("SnapMode", "SYSTEM") != "FIXED") return
+ val selectedSnapTargetsString = preferences.getString("SnapTargets", "SYSTEM")!!
+ if (selectedSnapTargetsString != "CUSTOM") return
+ if (!preferences.contains("CustomRatio")) return
+ val customFixedRatio = preferences.getInt("CustomRatio", 33) / 100f
+ overrideFixedRatio(resparam, customFixedRatio)
+ }
+
+ companion object {
+ fun overrideFixedRatio(resparam: XC_InitPackageResources.InitPackageResourcesParam, customFixedRatio: Float) {
+ log("overriding fixed ratio with $customFixedRatio")
+ resparam.res.setReplacement("android", "fraction", "docked_stack_divider_fixed_ratio", FractionReplacement(customFixedRatio))
+ }
+
+ fun FractionReplacement(fraction: Float): XResForwarder {
+ val assetManager = XposedHelpers.getStaticObjectField(AssetManager::class.java, "sSystem") as AssetManager
+ val res = object : Resources(assetManager, DisplayMetrics(), Configuration()) {
+ override fun getFraction(id: Int, base: Int, pbase: Int): Float {
+ return fraction
+ }
+ }
+ return XResForwarder(res, 0)
+ }
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/DisableSwapAnimationHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/DisableSwapAnimationHook.kt
new file mode 100644
index 0000000..fa91361
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/DisableSwapAnimationHook.kt
@@ -0,0 +1,40 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.animation.Animator
+import android.os.Build
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.DisableSwapAnimationHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookLoadPackage
+import de.robv.android.xposed.XC_MethodHook
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.XposedBridge
+import de.robv.android.xposed.XposedHelpers
+import de.robv.android.xposed.callbacks.XC_LoadPackage
+
+object DisableSwapAnimationHookConfig {
+ @JvmField
+ val enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM
+}
+
+class DisableSwapAnimationHook : IXposedHookLoadPackage {
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleLoadPackage(${lpparam.packageName} in process ${lpparam.processName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val enabled = preferences.getBoolean("DisableSwapAnimation", false)
+ if (!enabled) return
+
+ val SplitLayoutClass = XposedHelpers.findClass("com.android.wm.shell.common.split.SplitLayout", lpparam.classLoader)
+ val playSwapAnimationMethod = SplitLayoutClass.declaredMethods.single { it.name == "playSwapAnimation" }
+
+ XposedBridge.hookMethod(playSwapAnimationMethod, object : XC_MethodHook(PRIORITY_LOWEST) {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ log("skipping scheduled swap animation")
+ val mSwapAnimator = XposedHelpers.getObjectField(param.thisObject, "mSwapAnimator") as Animator
+ mSwapAnimator.end()
+ }
+ })
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/FreeSnapHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/FreeSnapHook.kt
new file mode 100644
index 0000000..f607dd4
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/FreeSnapHook.kt
@@ -0,0 +1,35 @@
+package com.programminghoch10.SplitScreenMods
+
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.FreeSnapHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookLoadPackage
+import de.robv.android.xposed.XC_MethodHook
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.XposedBridge
+import de.robv.android.xposed.XposedHelpers
+import de.robv.android.xposed.callbacks.XC_LoadPackage
+
+object FreeSnapHookConfig {
+ @JvmField
+ val enabled = SnapModeHookConfig.enabled
+}
+
+class FreeSnapHook : IXposedHookLoadPackage {
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleLoadPackage(${lpparam.packageName} in process ${lpparam.processName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val enabled = preferences.getBoolean("FreeSnap", false)
+ if (!enabled) return
+
+ val DividerSnapAlgorithmClass = XposedHelpers.findClass("com.android.wm.shell.common.split.DividerSnapAlgorithm", lpparam.classLoader)
+ XposedBridge.hookAllConstructors(DividerSnapAlgorithmClass, object : XC_MethodHook() {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ XposedHelpers.setBooleanField(param.thisObject, "mFreeSnapMode", true)
+ log("${DividerSnapAlgorithmClass.simpleName} mFreeSnapMode constant changed to true")
+ }
+ })
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSplitScreenRatioHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSplitScreenRatioHook.kt
new file mode 100644
index 0000000..ecd72f7
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSplitScreenRatioHook.kt
@@ -0,0 +1,111 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.os.Build
+import android.os.IBinder
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.KeepSplitScreenRatioHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookLoadPackage
+import de.robv.android.xposed.XC_MethodReplacement
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.XposedBridge
+import de.robv.android.xposed.XposedHelpers
+import de.robv.android.xposed.callbacks.XC_LoadPackage
+import de.robv.android.xposed.XC_MethodHook as MethodHook
+
+
+object KeepSplitScreenRatioHookConfig {
+ @JvmField
+ val enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+}
+
+class KeepSplitScreenRatioHook : IXposedHookLoadPackage {
+
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleLoadPackage(${lpparam.packageName} in process ${lpparam.processName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val enabled = preferences.getBoolean("KeepSplitScreenRatio", false)
+ if (!enabled) return
+
+ val SplitLayoutClass = XposedHelpers.findClass("com.android.wm.shell.common.split.SplitLayout", lpparam.classLoader)
+ val StageCoordinatorClass = XposedHelpers.findClass("com.android.wm.shell.splitscreen.StageCoordinator", lpparam.classLoader)
+
+ /*
+ Currently this module disables SplitScreen enter and dismiss animations completely.
+
+ Before animating, the Divider state is initialized in
+ https://github.com/LineageOS/android_frameworks_base/blob/2dc97cf3d6234c87497ca78b2734bb3ed604c349/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java#L1849
+ then a "fling to center" is started in
+ https://github.com/LineageOS/android_frameworks_base/blob/2dc97cf3d6234c87497ca78b2734bb3ed604c349/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java#L3586
+ Since this "fling to center" is embedded deeply,
+ we currently simply disable it.
+ */
+
+ XposedHelpers.findAndHookMethod(
+ StageCoordinatorClass,
+ "onSnappedToDismiss",
+ Int::class.java,
+ Boolean::class.java,
+ object : MethodHook() {
+ override fun afterHookedMethod(param: MethodHookParam) {
+ // reset the divider position after a fling to dismiss is finished,
+ // since the SplitScreen enter animation won't take care of it anymore
+ val mSplitLayout = XposedHelpers.getObjectField(param.thisObject, "mSplitLayout")
+ //XposedHelpers.callMethod(mSplitLayout, "resetDividerPosition")
+ XposedBridge.invokeOriginalMethod(
+ XposedHelpers.findMethodExact(
+ SplitLayoutClass,
+ "resetDividerPosition",
+ *arrayOf(),
+ ),
+ mSplitLayout,
+ arrayOf(),
+ )
+ log("${StageCoordinatorClass.simpleName} onSnappedToDismiss called")
+ }
+ },
+ )
+
+ // disable resetDividerPosition entirely
+ XposedHelpers.findAndHookMethod(
+ SplitLayoutClass,
+ "resetDividerPosition",
+ XC_MethodReplacement.DO_NOTHING,
+ )
+
+ XposedHelpers.findAndHookMethod(
+ StageCoordinatorClass,
+ "prepareSplitLayout",
+ "android.window.WindowContainerTransaction",
+ Boolean::class.java,
+ object : MethodHook() {
+ override fun beforeHookedMethod(param: MethodHookParam) {
+ // disabling resizeAnim makes the method call resetDividerPosition()
+ log("prepareSplitLayout: disable resizeAnim")
+ param.args[1] = false
+ }
+ },
+ )
+
+ val startPendingAnimationMethod =
+ StageCoordinatorClass.declaredMethods.find { it.name == "startPendingAnimation" && it.parameterTypes[0] == IBinder::class.java }!!
+ XposedBridge.hookMethod(
+ startPendingAnimationMethod,
+ object : MethodHook() {
+ override fun beforeHookedMethod(param: MethodHookParam) {
+ val binder = param.args[0] as IBinder
+ val stageCoordinator = param.thisObject
+ val mSplitTransitions = XposedHelpers.getObjectField(stageCoordinator, "mSplitTransitions")
+ val isPendingEnter = XposedHelpers.callMethod(mSplitTransitions, "isPendingEnter", binder) as Boolean
+ log("${startPendingAnimationMethod.name}: isPendingEnter=$isPendingEnter")
+ if (!isPendingEnter) return
+ val mPendingEnter = XposedHelpers.getObjectField(mSplitTransitions, "mPendingEnter")
+ // disable resizeAnim when entering SplitScreen
+ XposedHelpers.setBooleanField(mPendingEnter, "mResizeAnim", false)
+ }
+ },
+ )
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSwapRatioHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSwapRatioHook.kt
new file mode 100644
index 0000000..efefc71
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSwapRatioHook.kt
@@ -0,0 +1,166 @@
+package com.programminghoch10.SplitScreenMods
+
+import java.util.function.*
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Rect
+import android.os.Build
+import android.view.SurfaceControl
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.KeepSwapRatioHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookLoadPackage
+import de.robv.android.xposed.XC_MethodReplacement
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.XposedBridge
+import de.robv.android.xposed.XposedHelpers
+import de.robv.android.xposed.callbacks.XC_LoadPackage
+
+object KeepSwapRatioHookConfig {
+ @JvmField
+ val enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+}
+
+class KeepSwapRatioHook : IXposedHookLoadPackage {
+ val SWAP_ANIMATION_TOTAL_DURATION = 500L
+
+ override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
+ if (lpparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleLoadPackage(${lpparam.packageName} in process ${lpparam.processName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val enabled = preferences.getBoolean("KeepSwapRatio", false)
+ if (!enabled) return
+ val disableAnimation = preferences.getBoolean("DisableSwapAnimation", false)
+
+ val SplitLayoutClass = XposedHelpers.findClass("com.android.wm.shell.common.split.SplitLayout", lpparam.classLoader)
+ val playSwapAnimationMethod =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) SplitLayoutClass.declaredMethods.single { it.name == "playSwapAnimation" }
+ else SplitLayoutClass.declaredMethods.single { it.name == "splitSwitching" }
+ val getDisplayStableInsetsMethod = XposedHelpers.findMethodExact(SplitLayoutClass, "getDisplayStableInsets", Context::class.java)
+
+ XposedBridge.hookMethod(
+ playSwapAnimationMethod,
+ object : XC_MethodReplacement() {
+ override fun replaceHookedMethod(param: MethodHookParam): Any? {
+ @Suppress("UNCHECKED_CAST")
+ val finishCallback = param.args[3] as Consumer
+ val mContext = XposedHelpers.getObjectField(param.thisObject, "mContext") as Context
+ val insets = getDisplayStableInsetsMethod.invoke(param.thisObject, mContext) as Rect
+ val mIsLeftRightSplit = XposedHelpers.getBooleanField(param.thisObject, "mIsLeftRightSplit")
+ insets.set(
+ if (mIsLeftRightSplit) insets.left else 0,
+ if (mIsLeftRightSplit) 0 else insets.top,
+ if (mIsLeftRightSplit) insets.right else 0,
+ if (mIsLeftRightSplit) 0 else insets.bottom,
+ )
+ log("running own implementation of ${playSwapAnimationMethod.name} to prevent swapping")
+ if (disableAnimation) {
+ finishCallback.accept(insets)
+ return null
+ }
+
+ val t = param.args[0] as SurfaceControl.Transaction
+ val topLeftStage = param.args[1]
+ val bottomRightStage = param.args[2]
+
+ val shouldVeil = insets.left != 0 || insets.top != 0 || insets.right != 0 || insets.bottom != 0
+
+ val endBounds1 = Rect()
+ val endBounds2 = Rect()
+
+ // Compute destination bounds.
+ val dividerPos = XposedHelpers.getIntField(param.thisObject, "mDividerPosition")
+ XposedHelpers.callMethod(
+ param.thisObject,
+ "updateBounds",
+ dividerPos,
+ endBounds2,
+ endBounds1,
+ Rect(),
+ false, /* setEffectBounds */
+ )
+
+ val mRootBounds = XposedHelpers.getObjectField(param.thisObject, "mRootBounds") as Rect
+ // Offset to real position under root container.
+ endBounds1.offset(-mRootBounds.left, -mRootBounds.top)
+ endBounds2.offset(-mRootBounds.left, -mRootBounds.top)
+
+ fun moveSurface(stage: Any, startRect: Rect, endRect: Rect, offsetX: Float, offsetY: Float): ValueAnimator {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) XposedHelpers.callMethod(
+ param.thisObject,
+ "moveSurface",
+ t,
+ stage,
+ startRect,
+ endRect,
+ offsetX,
+ offsetY,
+ true, /* roundCorners */
+ true, /* isGoingBehind */
+ shouldVeil,
+ ) as ValueAnimator
+ else XposedHelpers.callMethod(
+ param.thisObject,
+ "moveSurface",
+ t,
+ stage,
+ startRect,
+ endRect,
+ offsetX,
+ offsetY,
+ ) as ValueAnimator
+ }
+
+ fun getRefBounds(topLeft: Boolean): Rect {
+ @Suppress("KotlinConstantConditions")
+ val methodName = when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && topLeft -> "getTopLeftBounds"
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && !topLeft -> "getBottomRightBounds"
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && topLeft -> "getBounds1"
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !topLeft -> "getBounds2"
+ else -> throw IllegalStateException("no method name matched for getting bounds")
+ }
+ val bounds = XposedHelpers.callMethod(param.thisObject, methodName) as Rect
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) bounds.offset(-mRootBounds.left, -mRootBounds.top)
+ return bounds
+ }
+
+ val topLeftBounds = getRefBounds(true)
+ val animator1 = moveSurface(
+ topLeftStage,
+ topLeftBounds,
+ endBounds1,
+ -insets.left.toFloat(),
+ -insets.top.toFloat(),
+ )
+ val bottomRightBounds = getRefBounds(false)
+ val animator2 = moveSurface(
+ bottomRightStage,
+ bottomRightBounds,
+ endBounds2,
+ insets.left.toFloat(),
+ insets.top.toFloat(),
+ )
+
+ val mSwapAnimator = AnimatorSet()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ XposedHelpers.setObjectField(param.thisObject, "mSwapAnimator", mSwapAnimator)
+ }
+ mSwapAnimator.playTogether(animator1, animator2)
+ mSwapAnimator.duration = SWAP_ANIMATION_TOTAL_DURATION
+ mSwapAnimator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ finishCallback.accept(insets)
+ }
+ })
+ mSwapAnimator.start()
+ return null
+ }
+ },
+ )
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/RemoveMinimalTaskSizeHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/RemoveMinimalTaskSizeHook.kt
new file mode 100644
index 0000000..4939b93
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/RemoveMinimalTaskSizeHook.kt
@@ -0,0 +1,35 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.content.res.XResources
+import android.os.Build
+import android.util.TypedValue
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.RemoveMinimalTaskSizeHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookInitPackageResources
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.callbacks.XC_InitPackageResources
+
+object RemoveMinimalTaskSizeHookConfig {
+ @JvmField
+ val enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+}
+
+class RemoveMinimalTaskSizeHook : IXposedHookInitPackageResources {
+ override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ if (resparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleInitPackageResources(${resparam.packageName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val enabled = preferences.getBoolean("RemoveMinimalTaskSize", false)
+ if (!enabled) return
+
+ resparam.res.setReplacement(
+ "android",
+ "dimen",
+ "default_minimal_size_resizable_task",
+ object : XResources.DimensionReplacement(0f, TypedValue.COMPLEX_UNIT_DIP) {},
+ )
+ log("set replacement for default_minimal_size_resizable_task")
+ }
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SNAP_MODE.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SNAP_MODE.kt
new file mode 100644
index 0000000..2798cf2
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SNAP_MODE.kt
@@ -0,0 +1,46 @@
+package com.programminghoch10.SplitScreenMods
+
+// yoinked from https://github.com/LineageOS/android_frameworks_base/blob/2dc97cf3d6234c87497ca78b2734bb3ed604c349/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerSnapAlgorithm.java
+
+enum class SNAP_MODE(val key: String, val value: Int) {
+
+ SNAP_MODE_UNCHANGED(
+ "SYSTEM",
+ -1,
+ ),
+
+ SNAP_MODE_16_9(
+ "16_9",
+ 0,
+ ),
+
+ SNAP_FIXED_RATIO(
+ "FIXED",
+ 1,
+ ),
+
+ SNAP_ONLY_1_1(
+ "1_1",
+ 2,
+ ),
+
+ /*
+ While this target does work,
+ it is clearly intended for "select a second task to split" mode,
+ where only one task's icon is shown as a preview for split selection.
+ */
+ SNAP_MODE_MINIMIZED(
+ "MINIMIZED",
+ 3,
+ ),
+
+ /*
+ This target provides the offscreen ratios,
+ but the required functionality seems to be stripped out on current builds.
+ The fallback ratio is 33%, which can be obtained with the SNAP_FIXED_RATIO as well.
+ */
+ SNAP_FLEXIBLE_SPLIT(
+ "FLEXIBLE",
+ 4,
+ ),
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SettingsActivity.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SettingsActivity.kt
new file mode 100644
index 0000000..498a18a
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SettingsActivity.kt
@@ -0,0 +1,99 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.content.SharedPreferences
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import androidx.preference.ListPreference
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceFragmentCompat
+import androidx.preference.SeekBarPreference
+import androidx.preference.SwitchPreference
+import androidx.preference.children
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+
+class SettingsActivity : FragmentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.settings_activity)
+ if (savedInstanceState == null) {
+ supportFragmentManager.beginTransaction().replace(R.id.settings, SettingsFragment()).commit()
+ }
+ actionBar?.setDisplayHomeAsUpEnabled(true)
+ }
+
+ override fun onNavigateUp(): Boolean {
+ finish()
+ return true
+ }
+
+ class SettingsFragment : PreferenceFragmentCompat() {
+
+ lateinit var onSharedPreferencesChangedListener: SharedPreferences.OnSharedPreferenceChangeListener
+
+ override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
+ preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
+ preferenceManager.sharedPreferencesMode = MODE_WORLD_READABLE
+ setPreferencesFromResource(R.xml.root_preferences, rootKey)
+
+ val alwaysAllowMultiInstanceSplitPreference = preferenceScreen.findPreference("AlwaysAllowMultiInstanceSplit")!!
+ val keepSplitScreenRatioPreference = preferenceScreen.findPreference("KeepSplitScreenRatio")!!
+ val keepSwapRatioPreference = preferenceScreen.findPreference("KeepSwapRatio")!!
+ val disableSwapAnimationPreference = preferenceScreen.findPreference("DisableSwapAnimation")!!
+ val snapModePreference = preferenceScreen.findPreference("SnapMode")!!
+ val freeSnapPreference = preferenceScreen.findPreference("FreeSnap")!!
+ val snapTargetsPreference = preferenceScreen.findPreference("SnapTargets")!!
+ val customRatioPreference = preferenceScreen.findPreference("CustomRatio")!!
+ val calculateRatiosPreference = preferenceScreen.findPreference("CalculateRatios")!!
+ val removeMinimalTaskSizePreference = preferenceScreen.findPreference("RemoveMinimalTaskSize")!!
+
+ fun calculateDependencies() {
+ alwaysAllowMultiInstanceSplitPreference.setEnabledAndVisible(AlwaysAllowMultiInstanceSplitHookConfig.enabled)
+ keepSplitScreenRatioPreference.setEnabledAndVisible(KeepSplitScreenRatioHookConfig.enabled)
+ keepSwapRatioPreference.setEnabledAndVisible(KeepSwapRatioHookConfig.enabled)
+ disableSwapAnimationPreference.setEnabledAndVisible(DisableSwapAnimationHookConfig.enabled || (KeepSwapRatioHookConfig.enabled && keepSwapRatioPreference.isChecked))
+ snapModePreference.setEnabledAndVisible(SnapModeHookConfig.enabled)
+ val is1_1SnapMode = snapModePreference.value == SNAP_MODE.SNAP_ONLY_1_1.key
+ val isFixedRatioSnapMode = snapModePreference.value == SNAP_MODE.SNAP_FIXED_RATIO.key
+ freeSnapPreference.setEnabledAndVisible(FreeSnapHookConfig.enabled && snapModePreference.isEnabled && !is1_1SnapMode)
+ with(snapTargetsPreference) {
+ setEnabledAndVisible(snapModePreference.isEnabled && CustomFixedRatioHookConfig.enabled && isFixedRatioSnapMode)
+ setEntries(
+ when {
+ !AdditionalSnapTargetsHookConfig.enabled -> R.array.CUSTOM_ONLY_SNAP_TARGET_TITLES
+ freeSnapPreference.isChecked -> R.array.SINGLE_SNAP_TARGET_TITLES
+ else -> R.array.SNAP_TARGET_TITLES
+ }
+ )
+ setEntryValues(
+ when {
+ !AdditionalSnapTargetsHookConfig.enabled -> R.array.CUSTOM_ONLY_SNAP_TARGET_KEYS
+ freeSnapPreference.isChecked -> R.array.SINGLE_SNAP_TARGET_KEYS
+ else -> R.array.SNAP_TARGET_KEYS
+ }
+ )
+ }
+ customRatioPreference.setEnabledAndVisible(snapTargetsPreference.isEnabled && snapTargetsPreference.value == "CUSTOM")
+ removeMinimalTaskSizePreference.setEnabledAndVisible(RemoveMinimalTaskSizeHookConfig.enabled && !is1_1SnapMode)
+ calculateRatiosPreference.setEnabledAndVisible(CalculateRatiosHookConfig.enabled && snapModePreference.isEnabled && !removeMinimalTaskSizePreference.isChecked && isFixedRatioSnapMode)
+
+ preferenceScreen.children.filterIsInstance().forEach { preferenceCategory ->
+ preferenceCategory.isVisible = preferenceCategory.children.any { it.isEnabled }
+ }
+ }
+
+ onSharedPreferencesChangedListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
+ calculateDependencies()
+ }
+ preferenceManager.sharedPreferences!!.registerOnSharedPreferenceChangeListener(onSharedPreferencesChangedListener)
+
+ calculateDependencies()
+ }
+ }
+}
+
+fun Preference.setEnabledAndVisible(enabled: Boolean) {
+ this.isEnabled = enabled
+ this.isVisible = enabled
+}
diff --git a/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SnapModeHook.kt b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SnapModeHook.kt
new file mode 100644
index 0000000..29b97d6
--- /dev/null
+++ b/SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SnapModeHook.kt
@@ -0,0 +1,29 @@
+package com.programminghoch10.SplitScreenMods
+
+import android.os.Build
+import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
+import com.programminghoch10.SplitScreenMods.SnapModeHookConfig.enabled
+import de.binarynoise.logger.Logger.log
+import de.robv.android.xposed.IXposedHookInitPackageResources
+import de.robv.android.xposed.XSharedPreferences
+import de.robv.android.xposed.callbacks.XC_InitPackageResources
+
+object SnapModeHookConfig {
+ @JvmField
+ val enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+}
+
+class SnapModeHook : IXposedHookInitPackageResources {
+ override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
+ if (resparam.packageName != "com.android.systemui") return
+ if (!enabled) return
+ log("handleInitPackageResources(${resparam.packageName})")
+ val preferences = XSharedPreferences(BuildConfig.APPLICATION_ID, SHARED_PREFERENCES_NAME)
+ val selectedSnapMode = preferences.getString("SnapMode", "SYSTEM")
+ if (selectedSnapMode == "SYSTEM") return
+
+ val selectedSnapModeId = SNAP_MODE.entries.find { it.key == selectedSnapMode }!!.value
+ resparam.res.setReplacement("android", "integer", "config_dockedStackDividerSnapMode", selectedSnapModeId)
+ log("overwriting SnapMode with $selectedSnapMode aka $selectedSnapModeId")
+ }
+}
diff --git a/SplitScreenMods/src/main/res/layout/settings_activity.xml b/SplitScreenMods/src/main/res/layout/settings_activity.xml
new file mode 100644
index 0000000..d4662df
--- /dev/null
+++ b/SplitScreenMods/src/main/res/layout/settings_activity.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/SplitScreenMods/src/main/res/values-v21/themes.xml b/SplitScreenMods/src/main/res/values-v21/themes.xml
new file mode 100644
index 0000000..6afefdc
--- /dev/null
+++ b/SplitScreenMods/src/main/res/values-v21/themes.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
diff --git a/AlwaysAllowMultiInstanceSplit/src/main/res/values/arrays.xml b/SplitScreenMods/src/main/res/values/arrays.xml
similarity index 100%
rename from AlwaysAllowMultiInstanceSplit/src/main/res/values/arrays.xml
rename to SplitScreenMods/src/main/res/values/arrays.xml
diff --git a/SplitScreenMods/src/main/res/values/snapmodes.xml b/SplitScreenMods/src/main/res/values/snapmodes.xml
new file mode 100644
index 0000000..f214d9e
--- /dev/null
+++ b/SplitScreenMods/src/main/res/values/snapmodes.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+ - -1
+ - 0
+ - 1
+ - 2
+
+
+
+
+
+ - SYSTEM
+ - 16_9
+ - FIXED
+ - 1_1
+
+
+
+
+
+ - UNCHANGED
+ - SNAP_MODE_16_9
+ - SNAP_FIXED_RATIO
+ - SNAP_ONLY_1_1
+
+
+
+
+
+ - Don\'t change default snapping behavior
+ - 3 snap targets: left/top has 16:9 ratio (for videos), 1:1, and right/bottom has 16:9 ratio
+ - 3 snap targets: fixed ratio, 1:1, (1 - fixed ratio)
+ - 1 snap target: 1:1
+
+
+
+
diff --git a/SplitScreenMods/src/main/res/values/snaptargets.xml b/SplitScreenMods/src/main/res/values/snaptargets.xml
new file mode 100644
index 0000000..e83d3b8
--- /dev/null
+++ b/SplitScreenMods/src/main/res/values/snaptargets.xml
@@ -0,0 +1,45 @@
+
+
+
+ - SYSTEM
+ - CUSTOM
+ - 0.33
+ - 0.25
+ - 0.25,0.33
+ - 0.25,0.33,0.4
+
+
+ - System default
+ - Custom
+ - ⅓
+ - ¼
+ - ⅓ & ¼
+ - ⅓ & ¼ & ⅖
+
+
+
+ - SYSTEM
+ - CUSTOM
+ - 0.33
+ - 0.25
+
+
+ - System default
+ - Custom
+ - ⅓
+ - ¼
+
+
+
+ - SYSTEM
+ - CUSTOM
+
+
+ - System default
+ - Custom
+
+
+
diff --git a/SplitScreenMods/src/main/res/values/strings.xml b/SplitScreenMods/src/main/res/values/strings.xml
new file mode 100644
index 0000000..41347c2
--- /dev/null
+++ b/SplitScreenMods/src/main/res/values/strings.xml
@@ -0,0 +1,45 @@
+
+
+ SplitScreenMods
+ @string/app_name
+
+ Collection of various SplitScreen modifications.
+
+ AlwaysAllowMultiInstanceSplit
+
+ Allow all apps to be launched twice side-by-side in a split screen.
+ \nUndefined behaviour ahead!
+
+ KeepSplitScreenRatio
+
+ Keep the split screen ratio, when switching one of the split apps.
+
+ A restart is required to apply changes!
+ FreeSnap
+
+ Allow any split ratio instead of snapping to predefined ratios between the outermost snapping points.
+
+ Behaviour Modifications
+ Snapping Modifications
+ SnapMode
+
+ Select your desired snapping mode
+
+ CalculateRatios
+
+ Allows split ratios to be calculated dynamically.
+ \nThis will force your selected fixed ratio to be at least the minimum task size.
+
+ RemoveMinimalTaskSize
+
+ Remove the minimum split app size limit.
+ \nYou don\'t usually need to enable this,
+ since the default minimum task size is usually small enough.
+
+ SnapTargets
+ Custom Fixed Ratio
+ Keep split ratio when swapping sides
+ KeepSwapRatio
+ DisableSwapAnimation
+ Disable swapping animation
+
diff --git a/SplitScreenMods/src/main/res/values/themes.xml b/SplitScreenMods/src/main/res/values/themes.xml
new file mode 100644
index 0000000..7984353
--- /dev/null
+++ b/SplitScreenMods/src/main/res/values/themes.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/SplitScreenMods/src/main/res/xml/root_preferences.xml b/SplitScreenMods/src/main/res/xml/root_preferences.xml
new file mode 100644
index 0000000..5f2ca7f
--- /dev/null
+++ b/SplitScreenMods/src/main/res/xml/root_preferences.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VolumeStepsIncrease/src/main/java/com/programminghoch10/VolumeStepsIncrease/SettingsActivity.kt b/VolumeStepsIncrease/src/main/java/com/programminghoch10/VolumeStepsIncrease/SettingsActivity.kt
index d683375..efb50f6 100644
--- a/VolumeStepsIncrease/src/main/java/com/programminghoch10/VolumeStepsIncrease/SettingsActivity.kt
+++ b/VolumeStepsIncrease/src/main/java/com/programminghoch10/VolumeStepsIncrease/SettingsActivity.kt
@@ -34,7 +34,6 @@ class SettingsActivity : FragmentActivity() {
}
class SettingsFragment : PreferenceFragmentCompat() {
- @SuppressLint("WorldReadableFiles")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey)
preferenceManager.sharedPreferencesName = SHARED_PREFERENCES_NAME
diff --git a/build-logic/convention/src/main/kotlin/Common.kt b/build-logic/convention/src/main/kotlin/Common.kt
index 4fb9f09..2b6d13d 100644
--- a/build-logic/convention/src/main/kotlin/Common.kt
+++ b/build-logic/convention/src/main/kotlin/Common.kt
@@ -171,9 +171,11 @@ private class CommonAndroid : Plugin {
disable += "ExpiredTargetSdkVersion"
disable += "MissingApplicationIcon"
disable += "MissingPermission"
+ disable += "ObsoleteSdkInt"
disable += "OldTargetApi"
disable += "PrivateApi"
disable += "UnusedAttribute"
+ disable += "WorldReadableFiles"
}
compileOptions {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 14890dc..6a6f5b6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -11,6 +11,7 @@ hiddenapibypass = "6.1"
jebrainsAnnotations = "26.0.2-1"
kotlin = "2.2.20"
libsu = "6.0.0"
+performanceUnsafe = "1.0.0-alpha01"
preference = "1.2.1"
xposed = "82"
@@ -22,6 +23,7 @@ androidx-annotation = { module = "androidx.annotation:annotation", version.ref =
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-performance-unsafe = { module = "androidx.performance:performance-unsafe", version.ref = "performanceUnsafe" }
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" }
diff --git a/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/full_description.txt b/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/full_description.txt
deleted file mode 100644
index 52731c0..0000000
--- a/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/full_description.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Keep the split screen ratio, when switching one of the split apps.
-
-Only required on Android 14 and later, since previous versions did not force a split resize in this situation.
diff --git a/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/short_description.txt b/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/short_description.txt
deleted file mode 100644
index 0662932..0000000
--- a/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Keep the split screen ratio, when switching one of the split apps.
diff --git a/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/title.txt b/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/title.txt
deleted file mode 100644
index ef58b8a..0000000
--- a/metadata/com.programminghoch10.KeepSplitScreenRatio/en-US/title.txt
+++ /dev/null
@@ -1 +0,0 @@
-KeepSplitScreenRatio
diff --git a/metadata/com.programminghoch10.SplitScreenMods/en-US/full_description.txt b/metadata/com.programminghoch10.SplitScreenMods/en-US/full_description.txt
new file mode 100644
index 0000000..112ac46
--- /dev/null
+++ b/metadata/com.programminghoch10.SplitScreenMods/en-US/full_description.txt
@@ -0,0 +1,51 @@
+A collection of various SplitScreen modifications.
+
+* AlwaysAllowMultiInstanceSplit
+* KeepSplitScreenRatio
+* KeepSwapRatio
+* DisableSwapAnimation
+* SnapMode
+* FreeSnap
+* AdditionalSnapTargets
+* RemoveMinimalTaskSize
+
+## AlwaysAllowMultiInstanceSplit
+
+Allow all apps to be launched twice side-by-side in a split screen.
+
+Undefined behaviour ahead!
+
+## KeepSplitScreenRatio
+
+Keep the split screen ratio, when switching one of the split apps.
+
+Only usable on Android 14 and later,
+since previous versions did not force a split resize in this situation.
+
+## KeepSwapRatio
+
+Keep the split ratio while swapping sides.
+
+## DisableSwapAnimation
+
+Disable the swapping animation.
+
+## SnapMode
+
+Change the snap mode used by the system:
+
+* `16:9` snapping useful for playing videos in split screen
+* `FIXED` snapping allows for custom ratios and [additional snap targets](#additionalsnaptargets)
+* `1:1` only allows the middle 50:50 split
+
+## FreeSnap
+
+Allow any split ratio instead of snapping to predefined ratios.
+
+## AdditionalSnapTargets
+
+Add additional snapping targets to snap to.
+
+## RemoveMinimalTaskSize
+
+Remove the minimal task size limit to allow split screen with unreasonably small apps.
diff --git a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/images/phoneScreenshots/1-screenshot.png b/metadata/com.programminghoch10.SplitScreenMods/en-US/images/phoneScreenshots/1-screenshot.png
similarity index 100%
rename from metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/images/phoneScreenshots/1-screenshot.png
rename to metadata/com.programminghoch10.SplitScreenMods/en-US/images/phoneScreenshots/1-screenshot.png
diff --git a/metadata/com.programminghoch10.SplitScreenMods/en-US/short_description.txt b/metadata/com.programminghoch10.SplitScreenMods/en-US/short_description.txt
new file mode 100644
index 0000000..ee54b71
--- /dev/null
+++ b/metadata/com.programminghoch10.SplitScreenMods/en-US/short_description.txt
@@ -0,0 +1 @@
+A collection of various SplitScreen modifications.
diff --git a/metadata/com.programminghoch10.SplitScreenMods/en-US/title.txt b/metadata/com.programminghoch10.SplitScreenMods/en-US/title.txt
new file mode 100644
index 0000000..1b97486
--- /dev/null
+++ b/metadata/com.programminghoch10.SplitScreenMods/en-US/title.txt
@@ -0,0 +1 @@
+SplitScreenMods
diff --git a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/full_description.txt b/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/full_description.txt
deleted file mode 100644
index 0a20e1b..0000000
--- a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/full_description.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Allow all apps to be launched twice side-by-side in a split screen.
-
-Undefined behaviour ahead!
diff --git a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/short_description.txt b/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/short_description.txt
deleted file mode 100644
index 40ab737..0000000
--- a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/short_description.txt
+++ /dev/null
@@ -1 +0,0 @@
-Allow all apps to be launched twice in a split screen
diff --git a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/title.txt b/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/title.txt
deleted file mode 100644
index 64d7a5b..0000000
--- a/metadata/de.binarynoise.AlwaysAllowMultiInstanceSplit/en-US/title.txt
+++ /dev/null
@@ -1 +0,0 @@
-AlwaysAllowMultiInstanceSplit
diff --git a/modules.gradle.kts b/modules.gradle.kts
index a3d053f..689e2ea 100644
--- a/modules.gradle.kts
+++ b/modules.gradle.kts
@@ -1,5 +1,4 @@
include(":AlwaysAllowChargingFeedback")
-include(":AlwaysAllowMultiInstanceSplit")
include(":AnimationScaleMod")
include(":AntiBrightnessChange")
include(":AutomaticAdvancedSettingsExpander")
@@ -11,7 +10,6 @@ include(":DontResetIfBootedAndConnected")
include(":EnableCallRecording")
include(":FreeNotifications")
include(":GalaxyWearable")
-include(":KeepSplitScreenRatio")
include(":MotionEventMod")
include(":MuteSlf4jWarnings")
include(":OpenWifiOnTop")
@@ -20,5 +18,6 @@ include(":PreventAudioFocus")
include(":ResetAllNotificationChannels")
include(":RotationControl")
include(":SensorMod")
+include(":SplitScreenMods")
include(":UpsideWifi")
include(":VolumeStepsIncrease")
diff --git a/template/src/main/AndroidManifest.xml b/template/src/main/AndroidManifest.xml
index e4a2062..19a0c4f 100644
--- a/template/src/main/AndroidManifest.xml
+++ b/template/src/main/AndroidManifest.xml
@@ -13,7 +13,7 @@
/>