Merge "Provide single threaded executor to UnfoldUnfoldTransitionFactory" into tm-qpr-dev
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LauncherBinder.java
similarity index 82%
rename from go/src/com/android/launcher3/model/LoaderResults.java
rename to go/src/com/android/launcher3/model/LauncherBinder.java
index 5f71061..437d8ca 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LauncherBinder.java
@@ -22,11 +22,11 @@
import com.android.launcher3.model.BgDataModel.Callbacks;
/**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
-public class LoaderResults extends BaseLoaderResults {
+public class LauncherBinder extends BaseLauncherBinder {
- public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+ public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 731eea7..4e795d9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -598,9 +598,6 @@
}
public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
- if (!isUserSetupComplete()) {
- return;
- }
mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
.updateValue(darkIntensity);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 7a75661..4ad3858 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -160,7 +160,6 @@
mIconAlignment.finishAnimation();
- Log.d("b/260135164", "onDestroy - updateIconAlphaForHome(1)");
mLauncher.getHotseat().setIconsAlpha(1f);
mLauncher.getStateManager().removeStateListener(mStateListener);
@@ -405,8 +404,6 @@
public void onAnimationEnd(Animator animation) {
if (isInStashedState && committed) {
// Reset hotseat alpha to default
- Log.d("b/260135164",
- "playStateTransitionAnim#onAnimationEnd - setIconsAlpha(1)");
mLauncher.getHotseat().setIconsAlpha(1);
}
}
@@ -455,9 +452,6 @@
* Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
* should not be visible at the same time.
*/
- Log.d("b/260135164",
- "updateIconAlphaForHome - setIconsAlpha(" + (hotseatVisible ? 1 : 0)
- + "), isTaskbarPresent: " + mLauncher.getDeviceProfile().isTaskbarPresent);
mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0);
mLauncher.getHotseat().setQsbAlpha(
mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index c269648..6031b49 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -364,7 +364,12 @@
* Returns the height that taskbar will be touchable.
*/
public int getTouchableHeight() {
- return mIsStashed ? mStashedHeight : mUnstashedHeight;
+ int bottomMargin = 0;
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ bottomMargin = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.transient_taskbar_margin);
+ }
+ return mIsStashed ? mStashedHeight : (mUnstashedHeight + bottomMargin);
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 0bc3c11..a7651b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -67,9 +67,13 @@
import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.WindowManagerGlobal;
+import android.window.BackEvent;
+import android.window.OnBackAnimationCallback;
+import android.window.OnBackInvokedDispatcher;
import android.window.SplashScreen;
import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.app.viewcapture.ViewCapture;
@@ -103,6 +107,8 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.LauncherTaskbarUIController;
import com.android.launcher3.taskbar.TaskbarManager;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.uioverrides.QuickstepWidgetHolder.QuickstepHolderFactory;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
@@ -621,6 +627,29 @@
}
}
+ @Override
+ protected void registerBackDispatcher() {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ new OnBackAnimationCallback() {
+ @Override
+ public void onBackInvoked() {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ }
+
+ @Override
+ public void onBackProgressed(@NonNull BackEvent backEvent) {
+ QuickstepLauncher.this.onBackProgressed(backEvent.getProgress());
+ }
+
+ @Override
+ public void onBackCancelled() {
+ QuickstepLauncher.this.onBackCancelled();
+ }
+ });
+ }
+
private void onTaskbarInAppDisplayProgressUpdate(float progress, int flag) {
if (mTaskbarManager == null
|| mTaskbarManager.getCurrentActivityContext() == null
@@ -999,6 +1028,14 @@
mPendingSplitSelectInfo = null;
}
+ @Override
+ public boolean areFreeformTasksVisible() {
+ if (mDesktopVisibilityController != null) {
+ return mDesktopVisibilityController.areFreeformTasksVisible();
+ }
+ return false;
+ }
+
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 733c6a8..95eb128 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -28,6 +28,7 @@
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
/**
@@ -91,6 +92,12 @@
@Override
protected float getDepthUnchecked(Context context) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
+ // Don't blur the background while freeform tasks are visible
+ return 0;
+ }
+ }
return 1;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 969abc2..7392469 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -17,10 +17,13 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import android.graphics.Color;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
+import com.android.quickstep.views.DesktopTaskView;
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
@@ -43,6 +46,12 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (launcher.areFreeformTasksVisible()) {
+ // No scrim while freeform tasks are visible
+ return Color.TRANSPARENT;
+ }
+ }
DeviceProfile dp = launcher.getDeviceProfile();
if (dp.isTaskbarPresentInApps) {
return launcher.getColor(R.color.taskbar_background);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 9e25555..f1c0f3e 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,7 +17,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -50,10 +49,8 @@
import android.graphics.Region;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
-import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserManager;
import android.provider.Settings;
import android.view.MotionEvent;
@@ -63,9 +60,9 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -109,15 +106,6 @@
private final boolean mIsOneHandedModeSupported;
private boolean mPipIsActive;
- private boolean mIsUserUnlocked;
- private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
- private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
- if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
- mIsUserUnlocked = true;
- notifyUserUnlocked();
- }
- });
-
private int mGestureBlockingTaskId = -1;
private @NonNull Region mExclusionRegion = new Region();
private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -143,14 +131,6 @@
runOnDestroy(mRotationTouchHelper::destroy);
}
- // Register for user unlocked if necessary
- mIsUserUnlocked = context.getSystemService(UserManager.class)
- .isUserUnlocked(Process.myUserHandle());
- if (!mIsUserUnlocked) {
- mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
- }
- runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
-
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@Override
@@ -310,39 +290,12 @@
}
/**
- * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
- * will be called back immediately.
- */
- public void runOnUserUnlocked(Runnable action) {
- if (mIsUserUnlocked) {
- action.run();
- } else {
- mUserUnlockedActions.add(action);
- }
- }
-
- /**
- * @return whether the user is unlocked.
- */
- public boolean isUserUnlocked() {
- return mIsUserUnlocked;
- }
-
- /**
* @return whether the user has completed setup wizard
*/
public boolean isUserSetupComplete() {
return mIsUserSetupComplete;
}
- private void notifyUserUnlocked() {
- for (Runnable action : mUserUnlockedActions) {
- action.run();
- }
- mUserUnlockedActions.clear();
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
- }
-
/**
* Sets the task id where gestures should be blocked
*/
@@ -585,7 +538,7 @@
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
- pw.println(" isUserUnlocked=" + mIsUserUnlocked);
+ pw.println(" isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked());
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 08a0ab3..d19f124 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -87,6 +87,7 @@
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -406,8 +407,8 @@
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
- mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
- mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+ LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
+ LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
ProtoTracer.INSTANCE.get(this).add(this);
@@ -477,7 +478,7 @@
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+ if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
// mode doesn't have gestures
return;
@@ -520,7 +521,7 @@
@UiThread
private void onSystemUiFlagsChanged(int lastSysUIFlags) {
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
@@ -565,7 +566,7 @@
@UiThread
private void onAssistantVisibilityChanged() {
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
mDeviceState.getAssistantVisibility());
}
@@ -575,7 +576,7 @@
public void onDestroy() {
Log.d(TAG, "Touch service destroyed: user=" + getUserId());
sIsInitialized = false;
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
@@ -609,7 +610,7 @@
TestLogging.recordMotionEvent(
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
@@ -631,7 +632,8 @@
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
mUncheckedConsumer = mConsumer;
- } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
+ } else if (LockedUserState.get(this).isUserUnlocked()
+ && mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
mGestureState = createGestureState(mGestureState);
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
@@ -751,7 +753,7 @@
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
CompoundString reasonString = newCompoundString("device locked");
InputConsumer consumer;
if (canStartSystemGesture) {
@@ -1098,7 +1100,7 @@
}
private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
@@ -1130,7 +1132,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (!mDeviceState.isUserUnlocked()) {
+ if (!LockedUserState.get(this).isUserUnlocked()) {
return;
}
final BaseActivityInterface activityInterface =
@@ -1171,7 +1173,7 @@
} else {
// Dump everything
FeatureFlags.dump(pw);
- if (mDeviceState.isUserUnlocked()) {
+ if (LockedUserState.get(this).isUserUnlocked()) {
PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
}
mDeviceState.dump(pw);
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 877e28a..cecf58d 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -108,7 +108,10 @@
float depth = mDepth;
IBinder windowToken = mLauncher.getRootView().getWindowToken();
if (windowToken != null) {
- mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
+ // The API's full zoom-out is three times larger than the zoom-out we apply to the
+ // icons. To keep the two consistent throughout the animation while keeping Launcher's
+ // concept of full depth unchanged, we divide the depth by 3 here.
+ mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
}
if (!BlurUtils.supportsBlursOnWindows()) {
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index c878278..858f6ab 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -81,6 +81,8 @@
private final ArrayList<CancellableTask<?>> mPendingThumbnailRequests = new ArrayList<>();
+ private ShapeDrawable mBackground;
+
public DesktopTaskView(Context context) {
this(context, null);
}
@@ -99,10 +101,11 @@
float[] outerRadii = new float[8];
Arrays.fill(outerRadii, getTaskCornerRadius());
RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
- ShapeDrawable background = new ShapeDrawable(shape);
- background.setTint(getResources().getColor(android.R.color.system_neutral2_300));
+ mBackground = new ShapeDrawable(shape);
+ mBackground.setTint(getResources().getColor(android.R.color.system_neutral2_300,
+ getContext().getTheme()));
// TODO(b/244348395): this should be wallpaper
- setBackground(background);
+ setBackground(mBackground);
mSnapshotViews.add(mSnapshotView);
}
@@ -427,6 +430,12 @@
// TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
progress = Utilities.boundToRange(progress, 0, 1);
mFullscreenProgress = progress;
+ if (mFullscreenProgress > 0) {
+ // Don't show background while we are transitioning to/from fullscreen
+ setBackground(null);
+ } else {
+ setBackground(mBackground);
+ }
for (int i = 0; i < mSnapshotViewMap.size(); i++) {
TaskThumbnailView thumbnailView = mSnapshotViewMap.valueAt(i);
thumbnailView.getTaskOverlay().setFullscreenProgress(progress);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9cf0601..c11f7ed 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -3040,7 +3040,7 @@
false /* fadeWithThumbnail */, true /* isStagedTask */);
}
- // TODO (b/257513449): Launch animation not fully complete. OK to remove flag once it is.
+ // Allow user to click staged app to launch into fullscreen
if (ENABLE_LAUNCH_FROM_STAGED_APP.get()) {
mFirstFloatingTaskView.setOnClickListener(this::animateToFullscreen);
}
@@ -3111,7 +3111,9 @@
false /* fadeWithThumbnail */,
true /* isStagedTask */);
- pendingAnimation.addEndListener(success -> launchStagedTask());
+ pendingAnimation.addEndListener(animationSuccess ->
+ mSplitSelectStateController.launchSplitTasks(launchSuccess ->
+ resetFromSplitSelectionState()));
pendingAnimation.buildAnim().start();
}
@@ -4818,16 +4820,6 @@
return mPendingAnimation;
}
- protected void launchStagedTask() {
- if (mSplitHiddenTaskView != null) {
- // Split staging was started from an existing running task (in Overview)
- mSplitHiddenTaskView.launchTask(success -> resetFromSplitSelectionState());
- } else {
- // Split staging was started from a new intent (from app menu in Home/AllApps)
- mActivity.startActivity(mSplitSelectSource.intent);
- }
- }
-
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 5c2e14f..1129a33 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -46,7 +46,8 @@
runWithShellPermission(() ->
usageStatsManager.registerAppUsageLimitObserver(observerId, packages,
Duration.ofSeconds(600), Duration.ofSeconds(300),
- PendingIntent.getActivity(mTargetContext, -1, new Intent(),
+ PendingIntent.getActivity(mTargetContext, -1, new Intent()
+ .setPackage(mTargetContext.getPackageName()),
PendingIntent.FLAG_MUTABLE)));
mLauncher.goHome();
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 61707df..e71391f 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -176,14 +176,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (Utilities.ATLEAST_T) {
- getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- () -> {
- onBackPressed();
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
- });
- }
+ registerBackDispatcher();
}
@Override
@@ -246,6 +239,17 @@
}
+ protected void registerBackDispatcher() {
+ if (Utilities.ATLEAST_T) {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ () -> {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ });
+ }
+ }
+
public boolean isStarted() {
return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 25520e1..f124940 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -910,12 +910,24 @@
cellHeightPx = cellContentHeight;
cellLayoutBorderSpacePx.y -= extraHeightRequired / numBorders;
} else {
- // If it still doesn't fit, set borderSpace to 0 and distribute the space for
- // cellHeight, and reduce iconSize.
+ // If it still doesn't fit, set borderSpace to 0 to recover space.
cellHeightPx = (cellHeightPx * inv.numRows
+ cellLayoutBorderSpacePx.y * numBorders) / inv.numRows;
- iconSizePx = Math.min(iconSizePx, cellHeightPx - cellTextAndPaddingHeight);
cellLayoutBorderSpacePx.y = 0;
+ // Reduce iconDrawablePaddingPx to make cellContentHeight smaller.
+ int cellContentWithoutPadding = cellContentHeight - iconDrawablePaddingPx;
+ if (cellContentWithoutPadding <= cellHeightPx) {
+ iconDrawablePaddingPx = cellContentHeight - cellHeightPx;
+ } else {
+ // If it still doesn't fit, set iconDrawablePaddingPx to 0 to recover space,
+ // then proportional reduce iconSizePx and iconTextSizePx to fit.
+ iconDrawablePaddingPx = 0;
+ float ratio = cellHeightPx / (float) cellContentWithoutPadding;
+ iconSizePx = (int) (iconSizePx * ratio);
+ iconTextSizePx = (int) (iconTextSizePx * ratio);
+ }
+ cellTextAndPaddingHeight =
+ iconDrawablePaddingPx + Utilities.calculateTextHeight(iconTextSizePx);
}
cellContentHeight = iconSizePx + cellTextAndPaddingHeight;
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 43772e4..e9723a5 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -120,6 +120,7 @@
import android.widget.Toast;
import androidx.annotation.CallSuper;
+import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -2065,6 +2066,14 @@
mStateManager.getState().onBackPressed(this);
}
+ protected void onBackProgressed(@FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ mStateManager.getState().onBackProgressed(this, backProgress);
+ }
+
+ protected void onBackCancelled() {
+ mStateManager.getState().onBackCancelled(this);
+ }
+
protected void onScreenOff() {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
@@ -3317,4 +3326,12 @@
return false; // Return false to continue iterating through all the items.
});
}
+
+ /**
+ * Returns {@code true} if there are visible tasks with windowing mode set to
+ * {@link android.app.WindowConfiguration#WINDOWING_MODE_FREEFORM}
+ */
+ public boolean areFreeformTasksVisible() {
+ return false; // Base launcher does not track freeform tasks
+ }
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 20df897..2c6458b 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -47,7 +47,7 @@
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.CacheDataUpdatedTask;
import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.LauncherBinder;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDelegate;
import com.android.launcher3.model.ModelWriter;
@@ -405,22 +405,22 @@
MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
- LoaderResults loaderResults = new LoaderResults(
+ LauncherBinder launcherBinder = new LauncherBinder(
mApp, mBgDataModel, mBgAllAppsList, callbacksList);
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
- loaderResults.bindWorkspace(bindAllCallbacks);
+ launcherBinder.bindWorkspace(bindAllCallbacks);
// For now, continue posting the binding of AllApps as there are other
// issues that arise from that.
- loaderResults.bindAllApps();
- loaderResults.bindDeepShortcuts();
- loaderResults.bindWidgets();
+ launcherBinder.bindAllApps();
+ launcherBinder.bindDeepShortcuts();
+ launcherBinder.bindWidgets();
return true;
} else {
stopLoader();
mLoaderTask = new LoaderTask(
- mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);
+ mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder);
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 5dddc6f..b9e4c17 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -34,6 +34,8 @@
import android.graphics.Color;
import android.view.animation.Interpolator;
+import androidx.annotation.FloatRange;
+
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.HintState;
@@ -342,6 +344,27 @@
}
}
+ /**
+ * Find {@link StateManager} and target {@link LauncherState} to handle back progress in
+ * predictive back gesture.
+ */
+ public void onBackProgressed(
+ Launcher launcher, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ StateManager<LauncherState> lsm = launcher.getStateManager();
+ LauncherState toState = lsm.getLastState();
+ lsm.onBackProgressed(toState, backProgress);
+ }
+
+ /**
+ * Find {@link StateManager} and target {@link LauncherState} to handle backProgress in
+ * predictive back gesture.
+ */
+ public void onBackCancelled(Launcher launcher) {
+ StateManager<LauncherState> lsm = launcher.getStateManager();
+ LauncherState toState = lsm.getLastState();
+ lsm.onBackCancelled(toState);
+ }
+
public static abstract class PageAlphaProvider {
public final Interpolator interpolator;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 9930abe..24bfedb 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
@@ -33,11 +34,15 @@
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.FloatRange;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -61,6 +66,8 @@
implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
// This constant should match the second derivative of the animator interpolator.
public static final float INTERP_COEFF = 1.7f;
+ private static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
+ private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -139,6 +146,7 @@
private ActivityAllAppsContainerView<Launcher> mAppsView;
private final Launcher mLauncher;
+ private final AnimatedFloat mAllAppScale = new AnimatedFloat(this::onScaleProgressChanged);
private boolean mIsVerticalLayout;
// Whether this class should take care of closing the keyboard.
@@ -232,6 +240,52 @@
onProgressAnimationEnd();
}
+ @Override
+ public void onBackProgressed(
+ LauncherState toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
+ return;
+ }
+
+ float deceleratedProgress =
+ Interpolators.PREDICTIVE_BACK_DECELERATED_EASE.getInterpolation(backProgress);
+ float scaleProgress = SWIPE_ALL_APPS_TO_HOME_MIN_SCALE
+ + (1 - SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) * (1 - deceleratedProgress);
+
+ mAllAppScale.updateValue(scaleProgress);
+ }
+
+ @Override
+ public void onBackCancelled(LauncherState toState) {
+ if (!mLauncher.isInState(ALL_APPS) || !NORMAL.equals(toState)) {
+ return;
+ }
+
+ // TODO: once ag/20649618 is picked into tm-qpr, we don't need to animate back on cancel
+ // swipe because framework will do that for us in {@link #onBackProgressed}.
+ animateAllAppsToNoScale();
+ }
+
+ private void onScaleProgressChanged() {
+ final float scaleProgress = mAllAppScale.value;
+ SCALE_PROPERTY.set(mLauncher.getAppsView(), scaleProgress);
+ mLauncher.getScrimView().setScrimHeaderScale(scaleProgress);
+
+ AllAppsRecyclerView rv = mLauncher.getAppsView().getActiveRecyclerView();
+ if (rv != null && rv.getScrollbar() != null) {
+ rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE);
+ }
+
+ // TODO(b/264906511): We need to disable view clipping on all apps' parent views so
+ // that the extra roll of app icons are displayed.
+ }
+
+ private void animateAllAppsToNoScale() {
+ mAllAppScale.animateToValue(1f)
+ .setDuration(REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS)
+ .start();
+ }
+
/**
* Creates an animation which updates the vertical transition progress and updates all the
* dependent UI using various animation events
@@ -258,6 +312,8 @@
if (config.userControlled && success && mShouldControlKeyboard) {
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
}
+
+ mAllAppScale.updateValue(1f);
});
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 00e89ba..4878077 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -808,7 +808,7 @@
}
@Override
- public void drawOnScrim(Canvas canvas) {
+ public void drawOnScrimWithScale(Canvas canvas, float scale) {
boolean isTablet = mActivityContext.getDeviceProfile().isTablet;
// Draw full background panel for tablets.
@@ -833,7 +833,9 @@
if (mHeaderPaint.getColor() == mScrimColor || mHeaderPaint.getColor() == 0) {
return;
}
- int bottom = getHeaderBottom() + getVisibleContainerView().getPaddingTop();
+ final float offset = (getVisibleContainerView().getHeight() * (1 - scale) / 2);
+ final float bottom =
+ scale * (getHeaderBottom() + getVisibleContainerView().getPaddingTop()) + offset;
FloatingHeaderView headerView = getFloatingHeaderView();
if (isTablet) {
// Start adding header protection if search bar or tabs will attach to the top.
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index b55a1e4..e886543 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -56,6 +56,8 @@
public static final Interpolator DECELERATED_EASE = new PathInterpolator(0, 0, .2f, 1f);
public static final Interpolator ACCELERATED_EASE = new PathInterpolator(0.4f, 0, 1f, 1f);
+ public static final Interpolator PREDICTIVE_BACK_DECELERATED_EASE =
+ new PathInterpolator(0, 0, 0, 1f);
/**
* The default emphasized interpolator. Used for hero / emphasized movement of content.
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 082f6a1..daf83d4 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -273,6 +273,10 @@
"ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
"Enable option to replace decorator-based search result backgrounds with drawables");
+ public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = new DeviceFlag(
+ "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
+ "Enable option to launch search results using the new standardized transitions");
+
public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = new DeviceFlag(
"TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
"Use 2 rows of app predictions in All Apps search zero-state");
@@ -364,7 +368,7 @@
"ENABLE_DEVICE_PROFILE_LOGGING", false, "Allows DeviceProfile logging");
public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag(
- "ENABLE_LAUNCH_FROM_STAGED_APP", false,
+ "ENABLE_LAUNCH_FROM_STAGED_APP", true,
"Enable the ability to tap a staged app during split select to launch it in full screen"
);
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
similarity index 95%
rename from src/com/android/launcher3/model/BaseLoaderResults.java
rename to src/com/android/launcher3/model/BaseLauncherBinder.java
index 8c6428b..9f8db51 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -47,12 +47,11 @@
import java.util.concurrent.Executor;
/**
- * Base Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
-public abstract class BaseLoaderResults {
+public abstract class BaseLauncherBinder {
- protected static final String TAG = "LoaderResults";
- protected static final int INVALID_SCREEN_ID = -1;
+ protected static final String TAG = "LauncherBinder";
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
protected final LooperExecutor mUiExecutor;
@@ -65,7 +64,7 @@
private int mMyBindingId;
- public BaseLoaderResults(LauncherAppState app, BgDataModel dataModel,
+ public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacksList, LooperExecutor uiExecutor) {
mUiExecutor = uiExecutor;
mApp = app;
@@ -101,8 +100,14 @@
}
}
+ /**
+ * BindDeepShortcuts is abstract because it is a no-op for the go launcher.
+ */
public abstract void bindDeepShortcuts();
+ /**
+ * Binds the all apps results from LoaderTask to the callbacks UX.
+ */
public void bindAllApps() {
// shallow copy
AppInfo[] apps = mBgAllAppsList.copyData();
@@ -110,6 +115,9 @@
executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
}
+ /**
+ * bindWidgets is abstract because it is a no-op for the go launcher.
+ */
public abstract void bindWidgets();
/**
@@ -160,6 +168,9 @@
});
}
+ /**
+ * Only used in LoaderTask.
+ */
public LooperIdleLock newIdleLock(Object lock) {
LooperIdleLock idleLock = new LooperIdleLock(lock, mUiExecutor.getLooper());
// If we are not binding or if the main looper is already idle, there is no reason to wait
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 1d6971e..46a6a66 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -125,7 +125,7 @@
private FirstScreenBroadcast mFirstScreenBroadcast;
- private final LoaderResults mResults;
+ private final LauncherBinder mLauncherBinder;
private final LauncherApps mLauncherApps;
private final UserManager mUserManager;
@@ -145,12 +145,12 @@
private String mDbName;
public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
- ModelDelegate modelDelegate, LoaderResults results) {
+ ModelDelegate modelDelegate, LauncherBinder launcherBinder) {
mApp = app;
mBgAllAppsList = bgAllAppsList;
mBgDataModel = dataModel;
mModelDelegate = modelDelegate;
- mResults = results;
+ mLauncherBinder = launcherBinder;
mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
mUserManager = mApp.getContext().getSystemService(UserManager.class);
@@ -163,7 +163,7 @@
// Wait until the either we're stopped or the other threads are done.
// This way we don't start loading all apps until the workspace has settled
// down.
- LooperIdleLock idleLock = mResults.newIdleLock(this);
+ LooperIdleLock idleLock = mLauncherBinder.newIdleLock(this);
// Just in case mFlushingWorkerThread changes but we aren't woken up,
// wait no longer than 1sec at a time
while (!mStopped && idleLock.awaitLocked(1000));
@@ -221,7 +221,7 @@
}
verifyNotStopped();
- mResults.bindWorkspace(true /* incrementBindId */);
+ mLauncherBinder.bindWorkspace(true /* incrementBindId */);
logASplit(logger, "bindWorkspace");
mModelDelegate.workspaceLoadComplete();
@@ -245,7 +245,7 @@
logASplit(logger, "loadAllApps");
verifyNotStopped();
- mResults.bindAllApps();
+ mLauncherBinder.bindAllApps();
logASplit(logger, "bindAllApps");
verifyNotStopped();
@@ -271,7 +271,7 @@
logASplit(logger, "loadDeepShortcuts");
verifyNotStopped();
- mResults.bindDeepShortcuts();
+ mLauncherBinder.bindDeepShortcuts();
logASplit(logger, "bindDeepShortcuts");
verifyNotStopped();
@@ -290,7 +290,7 @@
logASplit(logger, "load widgets");
verifyNotStopped();
- mResults.bindWidgets();
+ mLauncherBinder.bindWidgets();
logASplit(logger, "bindWidgets");
verifyNotStopped();
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index ad1e7f0..34ac8c2 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -28,6 +28,8 @@
import android.os.Handler;
import android.os.Looper;
+import androidx.annotation.FloatRange;
+
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -195,6 +197,21 @@
}
}
+ /** Handles backProgress in predictive back gesture by passing it to state handlers. */
+ public void onBackProgressed(
+ STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {
+ for (StateHandler handler : getStateHandlers()) {
+ handler.onBackProgressed(toState, backProgress);
+ }
+ }
+
+ /** Handles back cancelled event in predictive back gesture by passing it to state handlers. */
+ public void onBackCancelled(STATE_TYPE toState) {
+ for (StateHandler handler : getStateHandlers()) {
+ handler.onBackCancelled(toState);
+ }
+ }
+
private void goToState(
STATE_TYPE state, boolean animated, long delay, AnimatorListener listener) {
animated &= areAnimatorsEnabled();
@@ -586,6 +603,13 @@
*/
void setStateWithAnimation(
STATE_TYPE toState, StateAnimationConfig config, PendingAnimation animation);
+
+ /** Handles backProgress in predictive back gesture for target state. */
+ default void onBackProgressed(
+ STATE_TYPE toState, @FloatRange(from = 0.0, to = 1.0) float backProgress) {};
+
+ /** Handles back cancelled event in predictive back gesture for target state. */
+ default void onBackCancelled(STATE_TYPE toState) {};
}
public interface StateListener<STATE_TYPE> {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 8f7a4ec..c499e35 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -32,7 +32,6 @@
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
-import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
@@ -292,11 +291,18 @@
? mToState : mFromState;
// snap to top or bottom using the release velocity
} else {
- float successTransitionProgress =
- mLauncher.getDeviceProfile().isTablet
- && (mToState == ALL_APPS || mFromState == ALL_APPS)
- ? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS
- : SUCCESS_TRANSITION_PROGRESS;
+ float successTransitionProgress = SUCCESS_TRANSITION_PROGRESS;
+ if (mLauncher.getDeviceProfile().isTablet
+ && (mToState == ALL_APPS || mFromState == ALL_APPS)) {
+ successTransitionProgress = TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS;
+ } else if (!mLauncher.getDeviceProfile().isTablet
+ && mToState == ALL_APPS && mFromState == NORMAL) {
+ successTransitionProgress = AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL;
+ } else if (!mLauncher.getDeviceProfile().isTablet
+ && mToState == NORMAL && mFromState == ALL_APPS) {
+ successTransitionProgress =
+ 1 - AllAppsSwipeController.ALL_APPS_STATE_TRANSITION_MANUAL;
+ }
targetState =
(interpolatedProgress > successTransitionProgress) ? mToState : mFromState;
}
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 5279dec..bfd0e1b 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
@@ -64,11 +63,14 @@
// ---- Custom interpolators for NORMAL -> ALL_APPS on phones only. ----
- private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
- private static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.305f;
- private static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
- private static final float ALL_APPS_FADE_END_ATOMIC = 0.4717f;
+ public static final float ALL_APPS_STATE_TRANSITION_ATOMIC = 0.3333f;
+ public static final float ALL_APPS_STATE_TRANSITION_MANUAL = 0.4f;
+ private static final float ALL_APPS_FADE_END_ATOMIC = 0.8333f;
+ private static final float ALL_APPS_FADE_END_MANUAL = 0.8f;
private static final float ALL_APPS_FULL_DEPTH_PROGRESS = 0.5f;
+ private static final float SCRIM_FADE_START_ATOMIC = 0.2642f;
+ private static final float SCRIM_FADE_START_MANUAL = 0.117f;
+ private static final float WORKSPACE_MOTION_START_ATOMIC = 0.1667f;
private static final Interpolator LINEAR_EARLY_MANUAL =
Interpolators.clampToProgress(LINEAR, 0f, ALL_APPS_STATE_TRANSITION_MANUAL);
@@ -98,27 +100,30 @@
public static final Interpolator HOTSEAT_FADE_ATOMIC = STEP_TRANSITION_ATOMIC;
public static final Interpolator HOTSEAT_FADE_MANUAL = STEP_TRANSITION_MANUAL;
- public static final Interpolator HOTSEAT_SCALE_ATOMIC = STEP_TRANSITION_ATOMIC;
- public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
-
- public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC =
+ public static final Interpolator HOTSEAT_SCALE_ATOMIC =
Interpolators.clampToProgress(
EMPHASIZED_ACCELERATE, WORKSPACE_MOTION_START_ATOMIC,
ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator HOTSEAT_SCALE_MANUAL = LINEAR_EARLY_MANUAL;
+
+ public static final Interpolator HOTSEAT_TRANSLATE_ATOMIC = STEP_TRANSITION_ATOMIC;
public static final Interpolator HOTSEAT_TRANSLATE_MANUAL = STEP_TRANSITION_MANUAL;
public static final Interpolator SCRIM_FADE_ATOMIC =
Interpolators.clampToProgress(
Interpolators.mapToProgress(LINEAR, 0f, 0.8f),
- WORKSPACE_MOTION_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
- public static final Interpolator SCRIM_FADE_MANUAL = LINEAR_EARLY_MANUAL;
+ SCRIM_FADE_START_ATOMIC, ALL_APPS_STATE_TRANSITION_ATOMIC);
+ public static final Interpolator SCRIM_FADE_MANUAL =
+ Interpolators.clampToProgress(
+ LINEAR, SCRIM_FADE_START_MANUAL, ALL_APPS_STATE_TRANSITION_MANUAL);
public static final Interpolator ALL_APPS_FADE_ATOMIC =
Interpolators.clampToProgress(
- Interpolators.mapToProgress(DECELERATED_EASE, 0.2f, 1f),
+ Interpolators.mapToProgress(EMPHASIZED_DECELERATE, 0.2f, 1f),
ALL_APPS_STATE_TRANSITION_ATOMIC, ALL_APPS_FADE_END_ATOMIC);
public static final Interpolator ALL_APPS_FADE_MANUAL =
- Interpolators.clampToProgress(LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, 1f);
+ Interpolators.clampToProgress(
+ LINEAR, ALL_APPS_STATE_TRANSITION_MANUAL, ALL_APPS_FADE_END_MANUAL);
public static final Interpolator ALL_APPS_VERTICAL_PROGRESS_ATOMIC =
Interpolators.clampToProgress(
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index b7e0105..64951ca 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -23,14 +23,14 @@
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
-import android.os.Process;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -66,6 +66,8 @@
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
/**
* Class for handling clicks on workspace and all-apps items
@@ -156,32 +158,18 @@
private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
boolean downloadStarted) {
- if (downloadStarted) {
- // If the download has started, simply direct to the market app.
- startMarketIntentForPackage(v, launcher, packageName);
- return;
- }
- UserHandle user = v.getTag() instanceof ItemInfo
- ? ((ItemInfo) v.getTag()).user : Process.myUserHandle();
- new AlertDialog.Builder(launcher)
- .setTitle(R.string.abandoned_promises_title)
- .setMessage(R.string.abandoned_promise_explanation)
- .setPositiveButton(R.string.abandoned_search,
- (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
- .setNeutralButton(R.string.abandoned_clean_this,
- (d, i) -> launcher.getWorkspace()
- .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
- Collections.singleton(packageName), user),
- "user explicitly removes the promise app icon"))
- .create().show();
- }
-
- private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
ItemInfo item = (ItemInfo) v.getTag();
+ CompletableFuture<SessionInfo> siFuture;
if (Utilities.ATLEAST_Q) {
- SessionInfo sessionInfo = InstallSessionHelper.INSTANCE.get(launcher)
- .getActiveSessionInfo(item.user, packageName);
- if (sessionInfo != null) {
+ siFuture = CompletableFuture.supplyAsync(() ->
+ InstallSessionHelper.INSTANCE.get(launcher)
+ .getActiveSessionInfo(item.user, packageName),
+ UI_HELPER_EXECUTOR);
+ } else {
+ siFuture = CompletableFuture.completedFuture(null);
+ }
+ Consumer<SessionInfo> marketLaunchAction = sessionInfo -> {
+ if (sessionInfo != null && Utilities.ATLEAST_Q) {
LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
try {
launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
@@ -191,11 +179,27 @@
Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
}
}
- }
+ // Fallback to using custom market intent.
+ Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+ launcher.startActivitySafely(v, intent, item);
+ };
- // Fallback to using custom market intent.
- Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
- launcher.startActivitySafely(v, intent, item);
+ if (downloadStarted) {
+ // If the download has started, simply direct to the market app.
+ siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR);
+ return;
+ }
+ new AlertDialog.Builder(launcher)
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search,
+ (d, i) -> siFuture.thenAcceptAsync(marketLaunchAction, MAIN_EXECUTOR))
+ .setNeutralButton(R.string.abandoned_clean_this,
+ (d, i) -> launcher.getWorkspace()
+ .persistRemoveItemsByMatcher(ItemInfoMatcher.ofPackages(
+ Collections.singleton(packageName), item.user),
+ "user explicitly removes the promise app icon"))
+ .create().show();
}
/**
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
new file mode 100644
index 0000000..7b49583
--- /dev/null
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -0,0 +1,57 @@
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.annotation.VisibleForTesting
+
+class LockedUserState(private val mContext: Context) : SafeCloseable {
+ var isUserUnlocked: Boolean
+ private set
+ private val mUserUnlockedActions: RunnableList = RunnableList()
+
+ @VisibleForTesting
+ val mUserUnlockedReceiver = SimpleBroadcastReceiver {
+ if (Intent.ACTION_USER_UNLOCKED == it.action) {
+ isUserUnlocked = true
+ notifyUserUnlocked()
+ }
+ }
+
+ init {
+ isUserUnlocked =
+ mContext
+ .getSystemService(UserManager::class.java)!!
+ .isUserUnlocked(Process.myUserHandle())
+ if (isUserUnlocked) {
+ notifyUserUnlocked()
+ } else {
+ mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
+ }
+ }
+
+ private fun notifyUserUnlocked() {
+ mUserUnlockedActions.executeAllAndDestroy()
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ }
+
+ /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
+ override fun close() {
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ }
+
+ /**
+ * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
+ * this runnable will run immediately because RunnableList will already have been destroyed.
+ */
+ fun runOnUserUnlocked(action: Runnable) {
+ mUserUnlockedActions.add(action)
+ }
+
+ companion object {
+ @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+
+ @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
+ }
+}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 4c0bfde..870ff12 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -46,6 +46,7 @@
private int mBackgroundColor;
private boolean mIsVisible = true;
private boolean mLastDispatchedOpaqueness;
+ private float mHeaderScale = 1f;
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -91,7 +92,16 @@
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mDrawingController != null) {
- mDrawingController.drawOnScrim(canvas);
+ mDrawingController.drawOnScrimWithScale(canvas, mHeaderScale);
+ }
+ }
+
+ /** Set scrim header's scale and bottom offset. */
+ public void setScrimHeaderScale(float scale) {
+ boolean hasChanged = mHeaderScale != scale;
+ mHeaderScale = scale;
+ if (hasChanged) {
+ invalidate();
}
}
@@ -176,6 +186,6 @@
/**
* Called inside ScrimView#OnDraw
*/
- void drawOnScrim(Canvas canvas);
+ void drawOnScrimWithScale(Canvas canvas, float scale);
}
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
similarity index 88%
rename from src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
rename to src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
index abce2a2..e1a5f24 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LauncherBinder.java
@@ -27,11 +27,11 @@
import java.util.List;
/**
- * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects.
*/
-public class LoaderResults extends BaseLoaderResults {
+public class LauncherBinder extends BaseLauncherBinder {
- public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+ public LauncherBinder(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0db719e..9669010 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -147,7 +147,8 @@
// Set callback
PendingIntent callback = PendingIntent.getBroadcast(mTargetContext, 0,
- new Intent(mCallbackAction), FLAG_ONE_SHOT | FLAG_MUTABLE);
+ new Intent(mCallbackAction).setPackage(mTargetContext.getPackageName()),
+ FLAG_ONE_SHOT | FLAG_MUTABLE);
mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, "setCallback").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", callback));
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
new file mode 100644
index 0000000..84156e7
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/** Unit tests for {@link LockedUserUtil} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockedUserStateTest {
+
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var context: Context
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+ }
+
+ @Test
+ fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ val action: Runnable = mock()
+
+ LockedUserState.get(context).runOnUserUnlocked(action)
+ verify(action).run()
+ }
+
+ @Test
+ fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ val action: Runnable = mock()
+
+ LockedUserState.get(context).runOnUserUnlocked(action)
+ verifyZeroInteractions(action)
+
+ LockedUserState.get(context)
+ .mUserUnlockedReceiver
+ .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
+
+ verify(action).run()
+ }
+
+ @Test
+ fun isUserUnlocked_returns_true_when_user_is_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
+ }
+
+ @Test
+ fun isUserUnlocked_returns_false_when_user_is_locked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
+ }
+}