Skip to content

Commit 8fb9ff9

Browse files
implement swapping animation for KeepSwapRatio
1 parent ec0719a commit 8fb9ff9

File tree

2 files changed

+116
-4
lines changed

2 files changed

+116
-4
lines changed

SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/KeepSwapRatioHook.kt

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package com.programminghoch10.SplitScreenMods
22

33
import java.util.function.*
4+
import android.animation.Animator
5+
import android.animation.AnimatorListenerAdapter
6+
import android.animation.AnimatorSet
7+
import android.animation.ValueAnimator
48
import android.content.Context
59
import android.graphics.Rect
610
import android.os.Build
11+
import android.view.SurfaceControl
712
import com.programminghoch10.SplitScreenMods.BuildConfig.SHARED_PREFERENCES_NAME
813
import com.programminghoch10.SplitScreenMods.KeepSwapRatioHookConfig.enabled
914
import de.binarynoise.logger.Logger.log
@@ -20,6 +25,8 @@ object KeepSwapRatioHookConfig {
2025
}
2126

2227
class KeepSwapRatioHook : IXposedHookLoadPackage {
28+
val SWAP_ANIMATION_TOTAL_DURATION = 500L
29+
2330
override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
2431
if (lpparam.packageName != "com.android.systemui") return
2532
if (!enabled) return
@@ -39,11 +46,114 @@ class KeepSwapRatioHook : IXposedHookLoadPackage {
3946
object : XC_MethodReplacement() {
4047
override fun replaceHookedMethod(param: MethodHookParam): Any? {
4148
@Suppress("UNCHECKED_CAST")
42-
val callback = param.args[3] as Consumer<Rect>
43-
val context = XposedHelpers.getObjectField(param.thisObject, "mContext") as Context
44-
val insets = getDisplayStableInsetsMethod.invoke(param.thisObject, context) as Rect
45-
callback.accept(insets)
49+
val finishCallback = param.args[3] as Consumer<Rect>
50+
val mContext = XposedHelpers.getObjectField(param.thisObject, "mContext") as Context
51+
val insets = getDisplayStableInsetsMethod.invoke(param.thisObject, mContext) as Rect
52+
val mIsLeftRightSplit = XposedHelpers.getBooleanField(param.thisObject, "mIsLeftRightSplit")
53+
insets.set(
54+
if (mIsLeftRightSplit) insets.left else 0,
55+
if (mIsLeftRightSplit) 0 else insets.top,
56+
if (mIsLeftRightSplit) insets.right else 0,
57+
if (mIsLeftRightSplit) 0 else insets.bottom,
58+
)
59+
//finishCallback.accept(insets)
4660
log("replaced ${playSwapAnimationMethod.name} with dummy implementation to prevent swapping")
61+
62+
val t = param.args[0] as SurfaceControl.Transaction
63+
val topLeftStage = param.args[1]
64+
val bottomRightStage = param.args[2]
65+
66+
val shouldVeil = insets.left != 0 || insets.top != 0 || insets.right != 0 || insets.bottom != 0
67+
68+
val endBounds1 = Rect()
69+
val endBounds2 = Rect()
70+
71+
// Compute destination bounds.
72+
val dividerPos = XposedHelpers.getIntField(param.thisObject, "mDividerPosition")
73+
XposedHelpers.callMethod(
74+
param.thisObject,
75+
"updateBounds",
76+
dividerPos,
77+
endBounds2,
78+
endBounds1,
79+
Rect(),
80+
false, /* setEffectBounds */
81+
)
82+
83+
val mRootBounds = XposedHelpers.getObjectField(param.thisObject, "mRootBounds") as Rect
84+
// Offset to real position under root container.
85+
endBounds1.offset(-mRootBounds.left, -mRootBounds.top)
86+
endBounds2.offset(-mRootBounds.left, -mRootBounds.top)
87+
88+
fun moveSurface(stage: Any, startRect: Rect, endRect: Rect, offsetX: Float, offsetY: Float): ValueAnimator {
89+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) XposedHelpers.callMethod(
90+
param.thisObject,
91+
"moveSurface",
92+
t,
93+
stage,
94+
startRect,
95+
endRect,
96+
offsetX,
97+
offsetY,
98+
true, /* roundCorners */
99+
true, /* isGoingBehind */
100+
shouldVeil,
101+
) as ValueAnimator
102+
else XposedHelpers.callMethod(
103+
param.thisObject,
104+
"moveSurface",
105+
t,
106+
stage,
107+
startRect,
108+
endRect,
109+
offsetX,
110+
offsetY,
111+
) as ValueAnimator
112+
}
113+
114+
fun getRefBounds(topLeft: Boolean): Rect {
115+
@Suppress("KotlinConstantConditions")
116+
val methodName = when {
117+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && topLeft -> "getTopLeftBounds"
118+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && !topLeft -> "getBottomRightBounds"
119+
Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && topLeft -> "getBounds1"
120+
Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && !topLeft -> "getBounds2"
121+
else -> throw IllegalStateException("no method name matched for getting bounds")
122+
}
123+
val bounds = XposedHelpers.callMethod(param.thisObject, methodName) as Rect
124+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) bounds.offset(-mRootBounds.left, -mRootBounds.top)
125+
return bounds
126+
}
127+
128+
val topLeftBounds = getRefBounds(true)
129+
val animator1 = moveSurface(
130+
topLeftStage,
131+
topLeftBounds,
132+
endBounds1,
133+
-insets.left.toFloat(),
134+
-insets.top.toFloat(),
135+
)
136+
val bottomRightBounds = getRefBounds(false)
137+
val animator2 = moveSurface(
138+
bottomRightStage,
139+
bottomRightBounds,
140+
endBounds2,
141+
insets.left.toFloat(),
142+
insets.top.toFloat(),
143+
)
144+
145+
val mSwapAnimator = AnimatorSet()
146+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
147+
XposedHelpers.setObjectField(param.thisObject, "mSwapAnimator", mSwapAnimator)
148+
}
149+
mSwapAnimator.playTogether(animator1, animator2)
150+
mSwapAnimator.duration = SWAP_ANIMATION_TOTAL_DURATION
151+
mSwapAnimator.addListener(object : AnimatorListenerAdapter() {
152+
override fun onAnimationEnd(animation: Animator) {
153+
finishCallback.accept(insets)
154+
}
155+
})
156+
mSwapAnimator.start()
47157
return null
48158
}
49159
},

SplitScreenMods/src/main/java/com/programminghoch10/SplitScreenMods/SettingsActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class SettingsActivity : FragmentActivity() {
3939

4040
val alwaysAllowMultiInstanceSplitPreference = preferenceScreen.findPreference<SwitchPreference>("AlwaysAllowMultiInstanceSplit")!!
4141
val keepSplitScreenRatioPreference = preferenceScreen.findPreference<SwitchPreference>("KeepSplitScreenRatio")!!
42+
val keepSwapRatioPreference = preferenceScreen.findPreference<SwitchPreference>("KeepSwapRatio")!!
4243
val snapModePreference = preferenceScreen.findPreference<ListPreference>("SnapMode")!!
4344
val freeSnapPreference = preferenceScreen.findPreference<SwitchPreference>("FreeSnap")!!
4445
val snapTargetsPreference = preferenceScreen.findPreference<ListPreference>("SnapTargets")!!
@@ -49,6 +50,7 @@ class SettingsActivity : FragmentActivity() {
4950
fun calculateDependencies() {
5051
alwaysAllowMultiInstanceSplitPreference.setEnabledAndVisible(AlwaysAllowMultiInstanceSplitHookConfig.enabled)
5152
keepSplitScreenRatioPreference.setEnabledAndVisible(KeepSplitScreenRatioHookConfig.enabled)
53+
keepSwapRatioPreference.setEnabledAndVisible(KeepSwapRatioHookConfig.enabled)
5254
snapModePreference.setEnabledAndVisible(SnapModeHookConfig.enabled)
5355
val is1_1SnapMode = snapModePreference.value == SNAP_MODE.SNAP_ONLY_1_1.key
5456
val isFixedRatioSnapMode = snapModePreference.value == SNAP_MODE.SNAP_FIXED_RATIO.key

0 commit comments

Comments
 (0)