fix(QuickSwitch): Transition behaviour compat for 16.0 (#7282)

This commit is contained in:
Pun Butrach authored and GitHub committed 2026-09-13 20:04:50 +07:00
1 parent 12c2ee2219
commit d2d1775131
18 files changed
+294 -36

No files matched your search

@@ -11,6 +11,10 @@ val isOnePlusStock = checkOnePlusStock()
val isGestureNavContractCompatible = checkGestureNavContract() val isGestureNavContractCompatible = checkGestureNavContract()
val isNothingOs = checkNothingStock()
val isAndroidBaklavaInitial = checkGenericBaklavaInitial()
private fun checkOnePlusStock(): Boolean = when { private fun checkOnePlusStock(): Boolean = when {
getSystemProperty("ro.rom.version", "") getSystemProperty("ro.rom.version", "")
.contains(Regex("Oxygen OS|Hydrogen OS|O2_BETA|H2_BETA")) -> true .contains(Regex("Oxygen OS|Hydrogen OS|O2_BETA|H2_BETA")) -> true
@@ -52,6 +56,18 @@ private fun checkMeizuStock(): Boolean = when {
else -> false else -> false
} }
private fun checkNothingStock(): Boolean = when {
getSystemProperty("ro.nothing.version.id", "").isNotEmpty() -> true
getSystemProperty("ro.build.nothing.version", "").isNotEmpty() -> true
getSystemProperty("ro.build.nothing.feature.base", "").isNotEmpty() -> true
else -> false
}
private fun checkGenericBaklavaInitial(): Boolean = when {
arrayOf("BP2A.", "BD1A.").any { prefix -> Build.FINGERPRINT.contains(prefix) } -> true
else -> false
}
private fun checkGestureNavContract(): Boolean = when { private fun checkGestureNavContract(): Boolean = when {
!Utilities.ATLEAST_Q -> false !Utilities.ATLEAST_Q -> false
checkOnePlusStock() -> false checkOnePlusStock() -> false
@@ -1258,7 +1258,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
homeCheck.mRequirements[2].mNot = true; homeCheck.mRequirements[2].mNot = true;
homeCheck.mRequirements[2].mCustomAnimation = true; if (Utilities.ATLEAST_BAKLAVA) { // LC-Note: QuickSwitch compatibility!
homeCheck.mRequirements[2].mCustomAnimation = true;
}
homeCheck.mRequirements[2].mMustBeTask = true; homeCheck.mRequirements[2].mMustBeTask = true;
homeCheck.mRequirements[2].mMustBeIndependent = true; homeCheck.mRequirements[2].mMustBeIndependent = true;
@@ -25,6 +25,7 @@ import android.window.RemoteTransition
import android.window.TransitionFilter import android.window.TransitionFilter
import android.window.TransitionFilter.CONTAINER_ORDER_TOP import android.window.TransitionFilter.CONTAINER_ORDER_TOP
import com.android.internal.jank.Cuj import com.android.internal.jank.Cuj
import com.android.launcher3.Utilities
import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType
import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.quickstep.SystemUiProxy import com.android.quickstep.SystemUiProxy
@@ -74,7 +75,8 @@ class DesktopAppLaunchTransitionManager(
} }
private fun shouldRegisterTransitions(): Boolean = private fun shouldRegisterTransitions(): Boolean =
DesktopModeStatus.canEnterDesktopMode(context) && Utilities.ATLEAST_BAKLAVA && // LC-Note: This is only available for Baklava
DesktopModeStatus.canEnterDesktopMode(context) &&
DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue
companion object { companion object {
@@ -15,6 +15,7 @@
*/ */
package com.android.launcher3.taskbar package com.android.launcher3.taskbar
import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.graphics.Insets import android.graphics.Insets
@@ -22,6 +23,7 @@ import android.graphics.Paint
import android.graphics.Rect import android.graphics.Rect
import android.graphics.Region import android.graphics.Region
import android.os.Binder import android.os.Binder
import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.view.DisplayInfo import android.view.DisplayInfo
import android.view.Gravity import android.view.Gravity
@@ -50,6 +52,7 @@ import androidx.core.graphics.toRegion
import com.android.internal.policy.GestureNavigationSettingsObserver import com.android.internal.policy.GestureNavigationSettingsObserver
import com.android.launcher3.DeviceProfile import com.android.launcher3.DeviceProfile
import com.android.launcher3.R import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.anim.AlphaUpdateListener import com.android.launcher3.anim.AlphaUpdateListener
import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION
import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate
@@ -89,12 +92,29 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
onTaskbarOrBubblebarWindowHeightOrInsetsChanged() onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
} }
private val gestureNavSettingsObserver = private val gestureNavSettingsObserver =
GestureNavigationSettingsObserver( if (Utilities.ATLEAST_V) { // LC-Note: This is only available for V
context.mainThreadHandler, GestureNavigationSettingsObserver(
Executors.UI_HELPER_EXECUTOR.handler, context.mainThreadHandler,
context, Executors.UI_HELPER_EXECUTOR.handler,
this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged, context,
) this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged,
)
} else {
try {
GestureNavigationSettingsObserver::class.java
.getConstructor(Handler::class.java, Context::class.java, Runnable::class.java)
.newInstance(
context.mainThreadHandler,
context,
Runnable { onTaskbarOrBubblebarWindowHeightOrInsetsChanged() },
)
} catch (e: ReflectiveOperationException) {
throw IllegalStateException(
"LC: Unable to create legacy GestureNavigationSettingsObserver! QuickSwitch is tested for Android 14 above!",
e,
)
}
}
@VisibleForTesting val debugTouchableRegion = DebugTouchableRegion() @VisibleForTesting val debugTouchableRegion = DebugTouchableRegion()
// Initialized in init. // Initialized in init.
@@ -81,6 +81,7 @@ import android.graphics.Matrix;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF; import android.graphics.RectF;
import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
@@ -1687,8 +1688,19 @@ public abstract class AbsSwipeUpHandler<
boolean hasValidLeash = runningTaskTarget != null boolean hasValidLeash = runningTaskTarget != null
&& runningTaskTarget.leash != null && runningTaskTarget.leash != null
&& runningTaskTarget.leash.isValid(); && runningTaskTarget.leash.isValid();
boolean enableDesktopWindowingPipCompat = false;
// Literal int because https://github.com/LawnchairLauncher/lawnchair/issues/6817
if (Build.VERSION.SDK_INT >= 36 && Build.VERSION.SDK_INT_FULL >= 3600001) {
try {
enableDesktopWindowingPipCompat = DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue();
} catch (Exception e) {
Log.d("LC-AbsSwipeUpHandler", "Failed to get ENABLE_DESKTOP_WINDOWING_PIP flag, defaulting to false", e);
}
}
final boolean swipeUpInDesktopWindowing = final boolean swipeUpInDesktopWindowing =
DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() enableDesktopWindowingPipCompat
&& runningTaskTarget != null && runningTaskTarget != null
&& runningTaskTarget.taskInfo.getWindowingMode() && runningTaskTarget.taskInfo.getWindowingMode()
== WINDOWING_MODE_FREEFORM; == WINDOWING_MODE_FREEFORM;
@@ -42,7 +42,7 @@ private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPL
class AllAppsActionManager( class AllAppsActionManager(
private val context: Context, private val context: Context,
private val bgExecutor: Executor, private val bgExecutor: Executor,
private val quickstepKeyGestureEventsManager: QuickstepKeyGestureEventsManager, private val quickstepKeyGestureEventsManager: QuickstepKeyGestureEventsManager?, // LC-Note: Nullable because this isn't available for U and lower
private val createAllAppsPendingIntent: () -> PendingIntent, private val createAllAppsPendingIntent: () -> PendingIntent,
) { ) {
@@ -104,12 +104,12 @@ class AllAppsActionManager(
), ),
GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
) )
quickstepKeyGestureEventsManager.registerAllAppsKeyGestureEvent( quickstepKeyGestureEventsManager?.registerAllAppsKeyGestureEvent( // LC-Note: QuickSwitch compatibility!
allAppsPendingIntent allAppsPendingIntent
) )
} else { } else {
accessibilityManager.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS) accessibilityManager.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS)
quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent() quickstepKeyGestureEventsManager?.unregisterAllAppsKeyGestureEvent() // LC-Note: QuickSwitch compatibility!
} }
} }
} }
@@ -119,7 +119,7 @@ class AllAppsActionManager(
context context
.getSystemService(AccessibilityManager::class.java) .getSystemService(AccessibilityManager::class.java)
?.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS) ?.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS)
quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent() quickstepKeyGestureEventsManager?.unregisterAllAppsKeyGestureEvent() // LC-Note: QuickSwitch compatibility!
SettingsCache.INSTANCE[context].unregister( SettingsCache.INSTANCE[context].unregister(
USER_SETUP_COMPLETE_URI, USER_SETUP_COMPLETE_URI,
onSettingsChangeListener, onSettingsChangeListener,
@@ -26,10 +26,12 @@ import android.content.Intent
import android.content.pm.ShortcutInfo import android.content.pm.ShortcutInfo
import android.graphics.Point import android.graphics.Point
import android.graphics.Rect import android.graphics.Rect
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.os.Message import android.os.Message
import android.os.Parcel
import android.os.RemoteException import android.os.RemoteException
import android.os.Trace import android.os.Trace
import android.os.Trace.traceBegin import android.os.Trace.traceBegin
@@ -52,12 +54,14 @@ import android.window.WindowContainerTransaction
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread import androidx.annotation.WorkerThread
import app.lawnchair.util.isAndroidBaklavaInitial
import app.lawnchair.util.isNothingOs
//import app.lawnchair.gestures.type.GestureType //import app.lawnchair.gestures.type.GestureType
import com.android.internal.logging.InstanceId import com.android.internal.logging.InstanceId
import com.android.internal.util.ScreenshotRequest import com.android.internal.util.ScreenshotRequest
import com.android.internal.view.AppearanceRegion import com.android.internal.view.AppearanceRegion
import com.android.launcher3.Flags import com.android.launcher3.Flags
import com.android.launcher3.Utilities.ATLEAST_BAKLAVA import com.android.launcher3.Utilities
import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppComponent import com.android.launcher3.dagger.LauncherAppComponent
import com.android.launcher3.dagger.LauncherAppSingleton import com.android.launcher3.dagger.LauncherAppSingleton
@@ -214,6 +218,36 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
} }
} }
private fun startInitialAndroid16RecentsTransition(
recentTasks: IRecentTasks,
pendingIntent: PendingIntent,
intent: Intent?,
options: Bundle,
listener: IRecentsAnimationRunner,
): Boolean {
val data = Parcel.obtain(recentTasks.asBinder())
return try {
data.writeInterfaceToken(IRecentTasks.DESCRIPTOR)
data.writeTypedObject(pendingIntent, 0)
data.writeTypedObject(intent, 0)
data.writeTypedObject(options, 0)
data.writeStrongInterface(context.iApplicationThread)
data.writeStrongInterface(listener)
recentTasks.asBinder().transact(
if (usesNothingOs4BaklavaInitialRecentsTransitionAidl()) {
LC_TRANSACTION_startRecentsTransition_NothingOS_4_BaklavaInitial
} else {
LC_TRANSACTION_startRecentsTransition_AOSP_BaklavaInitial
},
data,
null,
IBinder.FLAG_ONEWAY,
)
} finally {
data.recycle()
}
}
fun onBackEvent(backEvent: KeyEvent?) = fun onBackEvent(backEvent: KeyEvent?) =
executeWithErrorLog({ "Failed call onBackPressed" }) { executeWithErrorLog({ "Failed call onBackPressed" }) {
systemUiProxy?.onBackEvent(backEvent) systemUiProxy?.onBackEvent(backEvent)
@@ -1108,7 +1142,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
private fun shouldEnableRunningTasksForDesktopMode(): Boolean = private fun shouldEnableRunningTasksForDesktopMode(): Boolean =
DesktopModeStatus.canEnterDesktopMode(context) && DesktopModeStatus.canEnterDesktopMode(context) &&
if (ATLEAST_BAKLAVA) { if (Utilities.ATLEAST_BAKLAVA) {
ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue
} else { } else {
false false
@@ -1271,6 +1305,24 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
displayId: Int, displayId: Int,
): Boolean { ): Boolean {
executeWithErrorLog({ "Error starting recents via shell" }) { executeWithErrorLog({ "Error starting recents via shell" }) {
if (usesAOSPBaklavaInitialRecentsTransitionAidl()) {
if (wct != null) {
Log.w("LC-SystemUiProxy", "Android 16 initial does not support WCT-backed recents transitions")
return false
}
val recentTasks = recentTasks ?: return false
return startInitialAndroid16RecentsTransition(
recentTasks,
getRecentsPendingIntent(displayId),
intent,
options.toBundle().apply {
if (useSyntheticRecentsTransition) {
putBoolean("is_synthetic_recents_transition", true)
}
},
RecentsAnimationListenerStub(listener),
)
}
recentTasks?.startRecentsTransition( recentTasks?.startRecentsTransition(
getRecentsPendingIntent(displayId), getRecentsPendingIntent(displayId),
intent, intent,
@@ -1284,8 +1336,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
RecentsAnimationListenerStub(listener), RecentsAnimationListenerStub(listener),
) )
?: run { ?: run {
// LC-Ignored if (Utilities.ATLEAST_S) ActiveGestureProtoLogProxy.logRecentTasksMissing()
//ActiveGestureProtoLogProxy.logRecentTasksMissing()
return false return false
} }
return true return true
@@ -1295,6 +1346,45 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
private class RecentsAnimationListenerStub(val listener: RecentsAnimationListener) : private class RecentsAnimationListenerStub(val listener: RecentsAnimationListener) :
IRecentsAnimationRunner.Stub() { IRecentsAnimationRunner.Stub() {
override fun onTransact(
code: Int,
data: Parcel,
reply: Parcel?,
flags: Int,
): Boolean {
if (usesNothingOs4BaklavaInitialRecentsTransitionAidl()
&& code == LC_TRANSACTION_onAnimationStartWithSurfaceTransaction) {
// LC-Note: What even the fuck this is (this handles a Nothing OS 4 binder transaction)
data.enforceInterface(IRecentsAnimationRunner.DESCRIPTOR) // This can be mistaken for IRecentsAnimationController, keep it this way
val controller = IRecentsAnimationController.Stub.asInterface(
data.readStrongBinder())
val transitionInfo = data.readTypedObject(TransitionInfo.CREATOR)
val transaction = data.readTypedObject(Transaction.CREATOR)
val apps = data.createTypedArray(RemoteAnimationTarget.CREATOR)
val wallpapers = data.createTypedArray(RemoteAnimationTarget.CREATOR)
val homeContentInsets = data.readTypedObject(Rect.CREATOR)
val minimizedHomeBounds = data.readTypedObject(Rect.CREATOR)
val extras = data.readTypedObject(Bundle.CREATOR)
data.enforceNoDataAvail()
try {
transaction?.apply()
} finally {
transaction?.close()
}
onAnimationStart(
controller,
apps,
wallpapers,
homeContentInsets,
minimizedHomeBounds,
extras,
transitionInfo,
)
return true
}
return super.onTransact(code, data, reply, flags)
}
override fun onAnimationStart( override fun onAnimationStart(
controller: IRecentsAnimationController, controller: IRecentsAnimationController,
apps: Array<RemoteAnimationTarget>?, apps: Array<RemoteAnimationTarget>?,
@@ -1397,6 +1487,31 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context:
companion object { companion object {
private const val TAG = "SystemUiProxy" private const val TAG = "SystemUiProxy"
/** LC-Note: Android 16.0 start recents transition binder code */
private const val LC_TRANSACTION_startRecentsTransition_AOSP_BaklavaInitial =
IBinder.FIRST_CALL_TRANSACTION + 4
/** LC-Note: Android 16.0, Nothing OS 4.0 start recents transition binder code whose value
* is higher than AOSP 16.0 or [LC_TRANSACTION_startRecentsTransition_AOSP_BaklavaInitial] */
private const val LC_TRANSACTION_startRecentsTransition_NothingOS_4_BaklavaInitial =
IBinder.FIRST_CALL_TRANSACTION + 5
/** LC-Note: onAnimationStartWithSurfaceTransac binder code */
private const val LC_TRANSACTION_onAnimationStartWithSurfaceTransaction =
IBinder.FIRST_CALL_TRANSACTION + 4
/** LC-Note: Should use AOSP 16.0 recents transition AIDL? */
private fun usesAOSPBaklavaInitialRecentsTransitionAidl(): Boolean {
return Build.VERSION.SDK_INT == Build.VERSION_CODES.BAKLAVA
&& isAndroidBaklavaInitial
}
/** LC-Note: Should use Nothing OS 16.0 recents transition AIDL? */
private fun usesNothingOs4BaklavaInitialRecentsTransitionAidl(): Boolean {
return usesAOSPBaklavaInitialRecentsTransitionAidl()
&& isNothingOs
}
@JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getSystemUiProxy) @JvmField val INSTANCE = DaggerSingletonObject(LauncherAppComponent::getSystemUiProxy)
private const val MSG_SET_SHELF_HEIGHT = 1 private const val MSG_SET_SHELF_HEIGHT = 1
@@ -337,7 +337,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
final ActivityOptions options = ActivityOptions.makeBasic(); final ActivityOptions options = ActivityOptions.makeBasic();
options.setPendingIntentBackgroundActivityStartMode( options.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS); Utilities.ATLEAST_BAKLAVA // LC-Note: QuickSwitch compatibility!
? ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
: ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
options.setTransientLaunch(); options.setTransientLaunch();
options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
options.setLaunchDisplayId(mDisplayId); options.setLaunchDisplayId(mDisplayId);
@@ -47,6 +47,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.dagger.ApplicationContext; import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppSingleton; import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.statehandlers.DesktopVisibilityController; import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -161,7 +162,7 @@ public class TopTaskTracker extends ISplitScreenListener.Stub implements TaskSta
// task is brought to front, then mark other tasks behind as not-visible // task is brought to front, then mark other tasks behind as not-visible
if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME
|| (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
&& !taskInfo.isActivityStackTransparent)) { && (!Utilities.ATLEAST_BAKLAVA || !taskInfo.isActivityStackTransparent))) { // LC-Note: QuickSwitch compatibility!
// We've moved the task to the front of the list above, so only iterate the tasks after // We've moved the task to the front of the list above, so only iterate the tasks after
for (int i = 1; i < mOrderedTaskList.size(); i++) { for (int i = 1; i < mOrderedTaskList.size(); i++) {
final TaskInfo info = mOrderedTaskList.get(i); final TaskInfo info = mOrderedTaskList.get(i);
@@ -731,6 +731,7 @@ public class TouchInteractionService extends Service {
private DisplayRepository mDisplayRepository; private DisplayRepository mDisplayRepository;
@Nullable // LC-Note: QuickSwitch compatibility!
private QuickstepKeyGestureEventsManager mQuickstepKeyGestureEventsHandler; private QuickstepKeyGestureEventsManager mQuickstepKeyGestureEventsHandler;
private DisplaysWithDecorationsRepositoryCompat mDisplaysWithDecorationsRepositoryCompat; private DisplaysWithDecorationsRepositoryCompat mDisplaysWithDecorationsRepositoryCompat;
private CoroutineDispatcher mCoroutineDispatcher; private CoroutineDispatcher mCoroutineDispatcher;
@@ -749,7 +750,9 @@ public class TouchInteractionService extends Service {
mRotationTouchHelperRepository = RotationTouchHelper.REPOSITORY_INSTANCE.get(this); mRotationTouchHelperRepository = RotationTouchHelper.REPOSITORY_INSTANCE.get(this);
mRecentsWindowManagerRepository = RecentsWindowManager.REPOSITORY_INSTANCE.get(this); mRecentsWindowManagerRepository = RecentsWindowManager.REPOSITORY_INSTANCE.get(this);
mSystemDecorationChangeObserver = SystemDecorationChangeObserver.getINSTANCE().get(this); mSystemDecorationChangeObserver = SystemDecorationChangeObserver.getINSTANCE().get(this);
mQuickstepKeyGestureEventsHandler = new QuickstepKeyGestureEventsManager(this); if (Utilities.ATLEAST_V) { // LC-Note: QuickSwitch compatibility!
mQuickstepKeyGestureEventsHandler = new QuickstepKeyGestureEventsManager(this);
}
mCoroutineDispatcher = ProductionDispatchers.INSTANCE.getMain(); mCoroutineDispatcher = ProductionDispatchers.INSTANCE.getMain();
mDisplaysWithDecorationsRepositoryCompat = mDisplaysWithDecorationsRepositoryCompat =
LauncherDisplaysWithDecorationsRepositoryCompat.getINSTANCE().get(this); LauncherDisplaysWithDecorationsRepositoryCompat.getINSTANCE().get(this);
@@ -884,8 +887,10 @@ public class TouchInteractionService extends Service {
onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame()); onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame());
mTaskbarManager.onUserUnlocked(); mTaskbarManager.onUserUnlocked();
mQuickstepKeyGestureEventsHandler.registerOverviewKeyGestureEvent( if (mQuickstepKeyGestureEventsHandler != null) { // LC-Note: QuickSwitch compatibility!
createOverviewGestureHandler()); mQuickstepKeyGestureEventsHandler.registerOverviewKeyGestureEvent(
createOverviewGestureHandler());
}
} }
public OverviewCommandHelper getOverviewCommandHelper() { public OverviewCommandHelper getOverviewCommandHelper() {
@@ -971,7 +976,9 @@ public class TouchInteractionService extends Service {
+ " instance=" + System.identityHashCode(this)); + " instance=" + System.identityHashCode(this));
if (LockedUserState.get(this).isUserUnlocked()) { if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer(); mInputConsumer.unregisterInputConsumer();
mQuickstepKeyGestureEventsHandler.onDestroy(); if (mQuickstepKeyGestureEventsHandler != null) { // LC-Note: QuickSwitch compatibility!
mQuickstepKeyGestureEventsHandler.onDestroy();
}
mOverviewComponentObserver.setHomeDisabled(false); mOverviewComponentObserver.setHomeDisabled(false);
mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener); mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener);
} }
@@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Trace import android.os.Trace
import android.view.Display.DEFAULT_DISPLAY import android.view.Display.DEFAULT_DISPLAY
import com.android.launcher3.Utilities
import com.android.launcher3.provider.RestoreDbTask import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.util.Executors import com.android.launcher3.util.Executors
import com.android.launcher3.util.LockedUserState import com.android.launcher3.util.LockedUserState
@@ -41,6 +42,8 @@ object ActivityPreloadUtil {
} }
private fun preloadOverview(ctx: Context, fromInit: Boolean, forSUWAllSet: Boolean) { private fun preloadOverview(ctx: Context, fromInit: Boolean, forSUWAllSet: Boolean) {
if (!Utilities.ATLEAST_BAKLAVA) return // LC-Note: QuickSwitch compatibility!
Trace.beginSection("preloadOverview(fromInit=$fromInit, forSUWAllSet=$forSUWAllSet)") Trace.beginSection("preloadOverview(fromInit=$fromInit, forSUWAllSet=$forSUWAllSet)")
try { try {
@@ -24,6 +24,7 @@ import android.util.Log
import android.view.Display.DEFAULT_DISPLAY import android.view.Display.DEFAULT_DISPLAY
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import com.android.internal.app.AssistUtils import com.android.internal.app.AssistUtils
import com.android.launcher3.Utilities
import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.logging.StatsLogManager import com.android.launcher3.logging.StatsLogManager
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR
@@ -62,7 +63,11 @@ internal constructor(
SystemUiProxy.INSTANCE[context], SystemUiProxy.INSTANCE[context],
StatsLogManager.newInstance(context), StatsLogManager.newInstance(context),
ContextualSearchHapticManager.INSTANCE[context], ContextualSearchHapticManager.INSTANCE[context],
context.getSystemService(ContextualSearchManager::class.java), if (Utilities.ATLEAST_V) {
context.getSystemService(ContextualSearchManager::class.java)
} else { // LC-Note: QuickSwitch compatibility!
null
},
) )
@Inject @Inject
@@ -80,13 +85,20 @@ internal constructor(
systemUiProxy, systemUiProxy,
logManagerFactory.create(context), logManagerFactory.create(context),
hapticManager, hapticManager,
context.getSystemService(ContextualSearchManager::class.java), if (Utilities.ATLEAST_V) { // LC-Note: QuickSwitch compatibility!
context.getSystemService(ContextualSearchManager::class.java)
} else {
null
},
) )
/** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */ /** @return Array of AssistUtils.INVOCATION_TYPE_* that we want to handle instead of SysUI. */
fun getSysUiAssistOverrideInvocationTypes(): IntArray { fun getSysUiAssistOverrideInvocationTypes(): IntArray {
val overrideInvocationTypes = com.android.launcher3.util.IntArray() val overrideInvocationTypes = com.android.launcher3.util.IntArray()
if (context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) { // LC-Note: QuickSwitch compatibility!
if (Utilities.ATLEAST_V &&
context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)
) {
overrideInvocationTypes.add(AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS) overrideInvocationTypes.add(AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS)
} }
return overrideInvocationTypes.toArray() return overrideInvocationTypes.toArray()
@@ -98,7 +110,11 @@ internal constructor(
*/ */
fun tryStartAssistOverride(invocationType: Int): Boolean { fun tryStartAssistOverride(invocationType: Int): Boolean {
if (invocationType == AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS) { if (invocationType == AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS) {
if (!context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) { // LC-Note: QuickSwitch compatibility!
if (
!Utilities.ATLEAST_V ||
!context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)
) {
// When Contextual Search is disabled, fall back to Assistant. // When Contextual Search is disabled, fall back to Assistant.
return false return false
} }
@@ -142,7 +158,8 @@ internal constructor(
*/ */
fun runContextualSearchInvocationChecksAndLogFailures(): Boolean { fun runContextualSearchInvocationChecksAndLogFailures(): Boolean {
if ( if (
contextualSearchManager == null || !Utilities.ATLEAST_V || // LC-Note: QuickSwitch compatibility!
contextualSearchManager == null ||
!context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH) !context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)
) { ) {
Log.i(TAG, "Contextual Search invocation failed: no ContextualSearchManager") Log.i(TAG, "Contextual Search invocation failed: no ContextualSearchManager")
@@ -99,12 +99,23 @@ public class ContextualSearchStateManager {
mContextualSearchPackageReceiver = mContextualSearchPackageReceiver =
new SimpleBroadcastReceiver(context, UI_HELPER_EXECUTOR, new SimpleBroadcastReceiver(context, UI_HELPER_EXECUTOR,
(unused) -> requestUpdateProperties()); (unused) -> requestUpdateProperties());
mContextualSearchPackage = mContext.getResources().getString(
com.android.internal.R.string.config_defaultContextualSearchPackageName); // LC-Note: QuickSwitch compatibility!
String resolvedCSPkgName;
try {
resolvedCSPkgName = mContext.getResources().getString(
com.android.internal.R.string.config_defaultContextualSearchPackageName);
} catch (Exception e) {
Log.e(TAG, "Failed to get config_defaultContextualSearchPackageName", e);
resolvedCSPkgName = "";
}
mContextualSearchPackage = resolvedCSPkgName;
mSystemUiProxy = systemUiProxy; mSystemUiProxy = systemUiProxy;
mTopTaskTracker = topTaskTracker; mTopTaskTracker = topTaskTracker;
if (areAllContextualSearchFlagsDisabled() if (!Utilities.ATLEAST_V // LC-Note: QuickSwitch compatibility!
|| areAllContextualSearchFlagsDisabled()
|| !context.getPackageManager().hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) { || !context.getPackageManager().hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) {
// If we had previously registered a SystemAction which is no longer valid, we need to // If we had previously registered a SystemAction which is no longer valid, we need to
// unregister it here. // unregister it here.
@@ -90,7 +90,8 @@ public final class FeatureFlags {
DISABLED, "Sends a notification whenever launcher encounters an uncaught exception."); DISABLED, "Sends a notification whenever launcher encounters an uncaught exception.");
public static final boolean ENABLE_TASKBAR_NAVBAR_UNIFICATION = public static final boolean ENABLE_TASKBAR_NAVBAR_UNIFICATION =
enableTaskbarNavbarUnification() && (!isPhone() || enableTaskbarOnPhones()); Utilities.ATLEAST_V && enableTaskbarNavbarUnification()
&& (!isPhone() || enableTaskbarOnPhones()); // LC-Note: QuickSwitch, Note, when this is enabled, it can crash with type 2019 on Android 14 and under
private static boolean isPhone() { private static boolean isPhone() {
final boolean isPhone; final boolean isPhone;
@@ -39,6 +39,8 @@ import androidx.annotation.Nullable;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import app.lawnchair.compat.LawnchairQuickstepCompat;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.Objects; import java.util.Objects;
@@ -111,8 +113,10 @@ public class Task {
this.displayId = t.displayId; this.displayId = t.displayId;
this.baseActivity = t.baseActivity; this.baseActivity = t.baseActivity;
this.numActivities = t.numActivities; this.numActivities = t.numActivities;
this.isTopActivityNoDisplay = t.isTopActivityNoDisplay; if (LawnchairQuickstepCompat.ATLEAST_BAKLAVA) { // LC-Note: QuickSwitch compatibility!
this.isActivityStackTransparent = t.isActivityStackTransparent; this.isTopActivityNoDisplay = t.isTopActivityNoDisplay;
this.isActivityStackTransparent = t.isActivityStackTransparent;
}
updateHashCode(); updateHashCode();
} }
+1
View File
@@ -32,6 +32,7 @@ dependencies {
compileOnly projects.msdllib compileOnly projects.msdllib
compileOnly projects.flags compileOnly projects.flags
compileOnly projects.utils compileOnly projects.utils
compileOnly libs.chickenhook.restrictionbypass
} }
ksp { ksp {
@@ -44,6 +44,7 @@ import android.annotation.SuppressLint;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.WindowConfiguration; import android.app.WindowConfiguration;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.view.RemoteAnimationTarget; import android.view.RemoteAnimationTarget;
@@ -51,6 +52,8 @@ import android.view.SurfaceControl;
import android.view.WindowManager; import android.view.WindowManager;
import android.window.TransitionInfo; import android.window.TransitionInfo;
import org.chickenhook.restrictionbypass.RestrictionBypass;
import java.util.function.Predicate; import java.util.function.Predicate;
/** Various utility functions for transitions. */ /** Various utility functions for transitions. */
@@ -408,7 +411,7 @@ public class TransitionUtil {
null, null,
new Rect(change.getStartAbsBounds()), new Rect(change.getStartAbsBounds()),
taskInfo, taskInfo,
change.isAllowEnterPip(), getAllowEnterPip(change), // LC-Note: QuickSwitch compatibility!
INVALID_WINDOW_TYPE INVALID_WINDOW_TYPE
); );
target.setWillShowImeOnTarget( target.setWillShowImeOnTarget(
@@ -418,6 +421,28 @@ public class TransitionUtil {
return target; return target;
} }
/** Can you enter picture-in-picture mode?
* <p>
* SDK 35 or above: isAllowEnterPip()
* <p>
* SDK 34 or below: getAllowEnterPip()
* <p>
* LC-Note: QuickSwitch compatibility!
*
* @return true if the change allows entering picture-in-picture mode, false otherwise
* */
private static boolean getAllowEnterPip(TransitionInfo.Change change) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
return change.isAllowEnterPip();
}
try {
return (Boolean) RestrictionBypass.getMethod(
TransitionInfo.Change.class, "getAllowEnterPip").invoke(change);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Unable to read allowEnterPip from transition change", e);
}
}
/** /**
* Creates a new RemoteAnimationTarget from the provided change and leash * Creates a new RemoteAnimationTarget from the provided change and leash
*/ */
@@ -18,11 +18,30 @@ package com.android.wm.shell.shared.pip
import android.app.AppGlobals import android.app.AppGlobals
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.window.DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP import android.window.DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP
import com.android.wm.shell.Flags import com.android.wm.shell.Flags
class PipFlags { class PipFlags {
companion object { companion object {
/** LC-Note: Can you enable desktop windowing picture-in-picture?
*
* SDK 36.1 or above: ENABLE_DESKTOP_WINDOWING_PIP.isTrue
*
* SDK Anything else: false
*
* Note when an exception occurred it will default to false
*/
private val enableDesktopWindowingPipCompat = Build.VERSION.SDK_INT >= 36 && Build.VERSION.SDK_INT_FULL >= 3600001 && try { // Literal int because https://github.com/LawnchairLauncher/lawnchair/issues/6817
ENABLE_DESKTOP_WINDOWING_PIP.isTrue
} catch (e: Exception) {
Log.d("LC-PipFlags", "Failed to get ENABLE_DESKTOP_WINDOWING_PIP flag, defaulting to false", e)
false
}
/** /**
* Returns true if PiP2 implementation should be used. Special note: if PiP on Desktop * Returns true if PiP2 implementation should be used. Special note: if PiP on Desktop
* Windowing is enabled, override the PiP2 gantry flag to be ON. * Windowing is enabled, override the PiP2 gantry flag to be ON.
@@ -31,7 +50,7 @@ class PipFlags {
val isPip2ExperimentEnabled: Boolean by lazy { val isPip2ExperimentEnabled: Boolean by lazy {
val isTv = AppGlobals.getPackageManager().hasSystemFeature( val isTv = AppGlobals.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK, 0) PackageManager.FEATURE_LEANBACK, 0)
(Flags.enablePip2() || ENABLE_DESKTOP_WINDOWING_PIP.isTrue) && !isTv (Flags.enablePip2() || enableDesktopWindowingPipCompat) && !isTv
} }
@JvmStatic @JvmStatic