diff --git a/lawnchair/src/app/lawnchair/util/Compatibility.kt b/lawnchair/src/app/lawnchair/util/Compatibility.kt index 385ba5902a..2316ead73c 100644 --- a/lawnchair/src/app/lawnchair/util/Compatibility.kt +++ b/lawnchair/src/app/lawnchair/util/Compatibility.kt @@ -11,6 +11,10 @@ val isOnePlusStock = checkOnePlusStock() val isGestureNavContractCompatible = checkGestureNavContract() +val isNothingOs = checkNothingStock() + +val isAndroidBaklavaInitial = checkGenericBaklavaInitial() + private fun checkOnePlusStock(): Boolean = when { getSystemProperty("ro.rom.version", "") .contains(Regex("Oxygen OS|Hydrogen OS|O2_BETA|H2_BETA")) -> true @@ -52,6 +56,18 @@ private fun checkMeizuStock(): Boolean = when { 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 { !Utilities.ATLEAST_Q -> false checkOnePlusStock() -> false diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 1a106a41e6..751be27a72 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1258,7 +1258,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; 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].mMustBeIndependent = true; diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt index 1434168687..26fc15382a 100644 --- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt +++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransitionManager.kt @@ -25,6 +25,7 @@ import android.window.RemoteTransition import android.window.TransitionFilter import android.window.TransitionFilter.CONTAINER_ORDER_TOP import com.android.internal.jank.Cuj +import com.android.launcher3.Utilities import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType import com.android.launcher3.util.Executors.MAIN_EXECUTOR import com.android.quickstep.SystemUiProxy @@ -74,7 +75,8 @@ class DesktopAppLaunchTransitionManager( } 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 companion object { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt index c07ae1d5b3..0d025c77a9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar +import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Insets @@ -22,6 +23,7 @@ import android.graphics.Paint import android.graphics.Rect import android.graphics.Region import android.os.Binder +import android.os.Handler import android.os.IBinder import android.view.DisplayInfo import android.view.Gravity @@ -50,6 +52,7 @@ import androidx.core.graphics.toRegion import com.android.internal.policy.GestureNavigationSettingsObserver import com.android.launcher3.DeviceProfile import com.android.launcher3.R +import com.android.launcher3.Utilities import com.android.launcher3.anim.AlphaUpdateListener import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate @@ -89,12 +92,29 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas onTaskbarOrBubblebarWindowHeightOrInsetsChanged() } private val gestureNavSettingsObserver = - GestureNavigationSettingsObserver( - context.mainThreadHandler, - Executors.UI_HELPER_EXECUTOR.handler, - context, - this::onTaskbarOrBubblebarWindowHeightOrInsetsChanged, - ) + if (Utilities.ATLEAST_V) { // LC-Note: This is only available for V + GestureNavigationSettingsObserver( + context.mainThreadHandler, + Executors.UI_HELPER_EXECUTOR.handler, + 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() // Initialized in init. diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 3a33644fa8..bc9d4ab56a 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -81,6 +81,7 @@ import android.graphics.Matrix; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; +import android.os.Build; import android.os.IBinder; import android.os.SystemClock; import android.util.Log; @@ -1687,8 +1688,19 @@ public abstract class AbsSwipeUpHandler< boolean hasValidLeash = runningTaskTarget != null && runningTaskTarget.leash != null && 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 = - DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() + enableDesktopWindowingPipCompat && runningTaskTarget != null && runningTaskTarget.taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; diff --git a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt index 51a68d16da..6236fce2db 100644 --- a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt +++ b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt @@ -42,7 +42,7 @@ private val USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(USER_SETUP_COMPL class AllAppsActionManager( private val context: Context, 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, ) { @@ -104,12 +104,12 @@ class AllAppsActionManager( ), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, ) - quickstepKeyGestureEventsManager.registerAllAppsKeyGestureEvent( + quickstepKeyGestureEventsManager?.registerAllAppsKeyGestureEvent( // LC-Note: QuickSwitch compatibility! allAppsPendingIntent ) } else { accessibilityManager.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS) - quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent() + quickstepKeyGestureEventsManager?.unregisterAllAppsKeyGestureEvent() // LC-Note: QuickSwitch compatibility! } } } @@ -119,7 +119,7 @@ class AllAppsActionManager( context .getSystemService(AccessibilityManager::class.java) ?.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS) - quickstepKeyGestureEventsManager.unregisterAllAppsKeyGestureEvent() + quickstepKeyGestureEventsManager?.unregisterAllAppsKeyGestureEvent() // LC-Note: QuickSwitch compatibility! SettingsCache.INSTANCE[context].unregister( USER_SETUP_COMPLETE_URI, onSettingsChangeListener, diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt index c3313e4fc6..fa6df1e4ab 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt @@ -26,10 +26,12 @@ import android.content.Intent import android.content.pm.ShortcutInfo import android.graphics.Point import android.graphics.Rect +import android.os.Build import android.os.Bundle import android.os.Handler import android.os.IBinder import android.os.Message +import android.os.Parcel import android.os.RemoteException import android.os.Trace import android.os.Trace.traceBegin @@ -52,12 +54,14 @@ import android.window.WindowContainerTransaction import androidx.annotation.MainThread import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread +import app.lawnchair.util.isAndroidBaklavaInitial +import app.lawnchair.util.isNothingOs //import app.lawnchair.gestures.type.GestureType import com.android.internal.logging.InstanceId import com.android.internal.util.ScreenshotRequest import com.android.internal.view.AppearanceRegion 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.LauncherAppComponent 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?) = executeWithErrorLog({ "Failed call onBackPressed" }) { systemUiProxy?.onBackEvent(backEvent) @@ -1108,7 +1142,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context: private fun shouldEnableRunningTasksForDesktopMode(): Boolean = DesktopModeStatus.canEnterDesktopMode(context) && - if (ATLEAST_BAKLAVA) { + if (Utilities.ATLEAST_BAKLAVA) { ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS.isTrue } else { false @@ -1271,6 +1305,24 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context: displayId: Int, ): Boolean { 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( getRecentsPendingIntent(displayId), intent, @@ -1284,8 +1336,7 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context: RecentsAnimationListenerStub(listener), ) ?: run { - // LC-Ignored - //ActiveGestureProtoLogProxy.logRecentTasksMissing() + if (Utilities.ATLEAST_S) ActiveGestureProtoLogProxy.logRecentTasksMissing() return false } return true @@ -1295,6 +1346,45 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context: private class RecentsAnimationListenerStub(val listener: RecentsAnimationListener) : 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( controller: IRecentsAnimationController, apps: Array?, @@ -1397,6 +1487,31 @@ class SystemUiProxy @Inject constructor(@ApplicationContext private val context: companion object { 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) private const val MSG_SET_SHELF_HEIGHT = 1 diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index d676fe7f92..89631ccb58 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -337,7 +337,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn final ActivityOptions options = ActivityOptions.makeBasic(); 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.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); options.setLaunchDisplayId(mDisplayId); diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java index 3bb429d7df..a4e826ee02 100644 --- a/quickstep/src/com/android/quickstep/TopTaskTracker.java +++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java @@ -47,6 +47,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; +import com.android.launcher3.Utilities; import com.android.launcher3.dagger.ApplicationContext; import com.android.launcher3.dagger.LauncherAppSingleton; 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 if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME || (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 for (int i = 1; i < mOrderedTaskList.size(); i++) { final TaskInfo info = mOrderedTaskList.get(i); diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 861d78dfc3..647836c522 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -731,6 +731,7 @@ public class TouchInteractionService extends Service { private DisplayRepository mDisplayRepository; + @Nullable // LC-Note: QuickSwitch compatibility! private QuickstepKeyGestureEventsManager mQuickstepKeyGestureEventsHandler; private DisplaysWithDecorationsRepositoryCompat mDisplaysWithDecorationsRepositoryCompat; private CoroutineDispatcher mCoroutineDispatcher; @@ -749,7 +750,9 @@ public class TouchInteractionService extends Service { mRotationTouchHelperRepository = RotationTouchHelper.REPOSITORY_INSTANCE.get(this); mRecentsWindowManagerRepository = RecentsWindowManager.REPOSITORY_INSTANCE.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(); mDisplaysWithDecorationsRepositoryCompat = LauncherDisplaysWithDecorationsRepositoryCompat.getINSTANCE().get(this); @@ -884,8 +887,10 @@ public class TouchInteractionService extends Service { onOverviewTargetChanged(mOverviewComponentObserver.isHomeAndOverviewSame()); mTaskbarManager.onUserUnlocked(); - mQuickstepKeyGestureEventsHandler.registerOverviewKeyGestureEvent( - createOverviewGestureHandler()); + if (mQuickstepKeyGestureEventsHandler != null) { // LC-Note: QuickSwitch compatibility! + mQuickstepKeyGestureEventsHandler.registerOverviewKeyGestureEvent( + createOverviewGestureHandler()); + } } public OverviewCommandHelper getOverviewCommandHelper() { @@ -971,7 +976,9 @@ public class TouchInteractionService extends Service { + " instance=" + System.identityHashCode(this)); if (LockedUserState.get(this).isUserUnlocked()) { mInputConsumer.unregisterInputConsumer(); - mQuickstepKeyGestureEventsHandler.onDestroy(); + if (mQuickstepKeyGestureEventsHandler != null) { // LC-Note: QuickSwitch compatibility! + mQuickstepKeyGestureEventsHandler.onDestroy(); + } mOverviewComponentObserver.setHomeDisabled(false); mOverviewComponentObserver.removeOverviewChangeListener(mOverviewChangeListener); } diff --git a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt index 239230c123..0a94b0c4fb 100644 --- a/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt +++ b/quickstep/src/com/android/quickstep/util/ActivityPreloadUtil.kt @@ -20,6 +20,7 @@ import android.content.Context import android.content.Intent import android.os.Trace import android.view.Display.DEFAULT_DISPLAY +import com.android.launcher3.Utilities import com.android.launcher3.provider.RestoreDbTask import com.android.launcher3.util.Executors import com.android.launcher3.util.LockedUserState @@ -41,6 +42,8 @@ object ActivityPreloadUtil { } 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)") try { diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt index aabea65917..21f8926458 100644 --- a/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt +++ b/quickstep/src/com/android/quickstep/util/ContextualSearchInvoker.kt @@ -24,6 +24,7 @@ import android.util.Log import android.view.Display.DEFAULT_DISPLAY import androidx.annotation.VisibleForTesting import com.android.internal.app.AssistUtils +import com.android.launcher3.Utilities import com.android.launcher3.dagger.ApplicationContext import com.android.launcher3.logging.StatsLogManager import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LAUNCH_ASSISTANT_FAILED_SERVICE_ERROR @@ -62,7 +63,11 @@ internal constructor( SystemUiProxy.INSTANCE[context], StatsLogManager.newInstance(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 @@ -80,13 +85,20 @@ internal constructor( systemUiProxy, logManagerFactory.create(context), 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. */ fun getSysUiAssistOverrideInvocationTypes(): 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) } return overrideInvocationTypes.toArray() @@ -98,7 +110,11 @@ internal constructor( */ fun tryStartAssistOverride(invocationType: Int): Boolean { 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. return false } @@ -142,7 +158,8 @@ internal constructor( */ fun runContextualSearchInvocationChecksAndLogFailures(): Boolean { if ( - contextualSearchManager == null || + !Utilities.ATLEAST_V || // LC-Note: QuickSwitch compatibility! + contextualSearchManager == null || !context.packageManager.hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH) ) { Log.i(TAG, "Contextual Search invocation failed: no ContextualSearchManager") diff --git a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java index 81bb281600..ca25d8c8b0 100644 --- a/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java +++ b/quickstep/src/com/android/quickstep/util/ContextualSearchStateManager.java @@ -99,12 +99,23 @@ public class ContextualSearchStateManager { mContextualSearchPackageReceiver = new SimpleBroadcastReceiver(context, UI_HELPER_EXECUTOR, (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; mTopTaskTracker = topTaskTracker; - if (areAllContextualSearchFlagsDisabled() + if (!Utilities.ATLEAST_V // LC-Note: QuickSwitch compatibility! + || areAllContextualSearchFlagsDisabled() || !context.getPackageManager().hasSystemFeature(FEATURE_CONTEXTUAL_SEARCH)) { // If we had previously registered a SystemAction which is no longer valid, we need to // unregister it here. diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index e9974042bd..1bed9d052e 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -90,7 +90,8 @@ public final class FeatureFlags { DISABLED, "Sends a notification whenever launcher encounters an uncaught exception."); 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() { final boolean isPhone; diff --git a/systemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/systemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 96307c7c30..f318b12b46 100644 --- a/systemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/systemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -39,6 +39,8 @@ import androidx.annotation.Nullable; import com.android.internal.util.ArrayUtils; +import app.lawnchair.compat.LawnchairQuickstepCompat; + import java.io.PrintWriter; import java.util.Objects; @@ -111,8 +113,10 @@ public class Task { this.displayId = t.displayId; this.baseActivity = t.baseActivity; this.numActivities = t.numActivities; - this.isTopActivityNoDisplay = t.isTopActivityNoDisplay; - this.isActivityStackTransparent = t.isActivityStackTransparent; + if (LawnchairQuickstepCompat.ATLEAST_BAKLAVA) { // LC-Note: QuickSwitch compatibility! + this.isTopActivityNoDisplay = t.isTopActivityNoDisplay; + this.isActivityStackTransparent = t.isActivityStackTransparent; + } updateHashCode(); } diff --git a/wmshell/build.gradle b/wmshell/build.gradle index 0b208db2c4..b73c9e40e0 100644 --- a/wmshell/build.gradle +++ b/wmshell/build.gradle @@ -32,6 +32,7 @@ dependencies { compileOnly projects.msdllib compileOnly projects.flags compileOnly projects.utils + compileOnly libs.chickenhook.restrictionbypass } ksp { diff --git a/wmshell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/wmshell/shared/src/com/android/wm/shell/shared/TransitionUtil.java index efb2183df5..0005cb24df 100644 --- a/wmshell/shared/src/com/android/wm/shell/shared/TransitionUtil.java +++ b/wmshell/shared/src/com/android/wm/shell/shared/TransitionUtil.java @@ -44,6 +44,7 @@ import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.WindowConfiguration; import android.graphics.Rect; +import android.os.Build; import android.util.ArrayMap; import android.util.SparseBooleanArray; import android.view.RemoteAnimationTarget; @@ -51,6 +52,8 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; +import org.chickenhook.restrictionbypass.RestrictionBypass; + import java.util.function.Predicate; /** Various utility functions for transitions. */ @@ -408,7 +411,7 @@ public class TransitionUtil { null, new Rect(change.getStartAbsBounds()), taskInfo, - change.isAllowEnterPip(), + getAllowEnterPip(change), // LC-Note: QuickSwitch compatibility! INVALID_WINDOW_TYPE ); target.setWillShowImeOnTarget( @@ -418,6 +421,28 @@ public class TransitionUtil { return target; } + /** Can you enter picture-in-picture mode? + *

+ * SDK 35 or above: isAllowEnterPip() + *

+ * SDK 34 or below: getAllowEnterPip() + *

+ * 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 */ diff --git a/wmshell/shared/src/com/android/wm/shell/shared/pip/PipFlags.kt b/wmshell/shared/src/com/android/wm/shell/shared/pip/PipFlags.kt index 4a5e4c950e..9e413f6858 100644 --- a/wmshell/shared/src/com/android/wm/shell/shared/pip/PipFlags.kt +++ b/wmshell/shared/src/com/android/wm/shell/shared/pip/PipFlags.kt @@ -18,11 +18,30 @@ package com.android.wm.shell.shared.pip import android.app.AppGlobals import android.content.pm.PackageManager +import android.os.Build +import android.util.Log import android.window.DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP import com.android.wm.shell.Flags class PipFlags { 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 * Windowing is enabled, override the PiP2 gantry flag to be ON. @@ -31,7 +50,7 @@ class PipFlags { val isPip2ExperimentEnabled: Boolean by lazy { val isTv = AppGlobals.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK, 0) - (Flags.enablePip2() || ENABLE_DESKTOP_WINDOWING_PIP.isTrue) && !isTv + (Flags.enablePip2() || enableDesktopWindowingPipCompat) && !isTv } @JvmStatic