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