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 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
@@ -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;
@@ -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 {
@@ -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.
@@ -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;
@@ -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,
@@ -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<RemoteAnimationTarget>?,
@@ -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
@@ -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);
@@ -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);
@@ -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);
}
@@ -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 {
@@ -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")
@@ -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.
@@ -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;
@@ -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();
}
+1
View File
@@ -32,6 +32,7 @@ dependencies {
compileOnly projects.msdllib
compileOnly projects.flags
compileOnly projects.utils
compileOnly libs.chickenhook.restrictionbypass
}
ksp {
@@ -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?
* <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
*/
@@ -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