summaryrefslogtreecommitdiff
path: root/quickstep/src/com/android/launcher3
diff options
context:
space:
mode:
Diffstat (limited to 'quickstep/src/com/android/launcher3')
-rw-r--r--quickstep/src/com/android/launcher3/HomeTransitionController.java57
-rw-r--r--quickstep/src/com/android/launcher3/QuickstepTransitionManager.java343
-rw-r--r--quickstep/src/com/android/launcher3/WidgetPickerActivity.java93
-rw-r--r--quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java7
-rw-r--r--quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java5
-rw-r--r--quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt109
-rw-r--r--quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java8
-rw-r--r--quickstep/src/com/android/launcher3/model/PredictionHelper.java3
-rw-r--r--quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java10
-rw-r--r--quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java17
-rw-r--r--quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java16
-rw-r--r--quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java78
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java9
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java16
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java7
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java19
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java106
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java37
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java72
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java56
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java2
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java349
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt199
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java6
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt73
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt113
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java16
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java24
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java21
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt35
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java7
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java31
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt45
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java58
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java159
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java4
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt134
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java15
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java4
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java63
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java42
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java10
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java199
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java31
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarView.java149
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java289
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java39
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java4
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java30
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt6
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java34
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java28
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt1
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java6
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java19
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java1
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt35
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt46
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt88
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt17
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt77
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt45
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt41
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt47
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt54
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java31
-rw-r--r--quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java34
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java60
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java3
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java4
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java7
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java172
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java9
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java17
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java (renamed from quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java)324
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java5
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java48
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java23
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/flags/IntDeviceFlag.java34
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java4
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java4
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java4
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java4
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java18
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java5
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java7
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java4
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java9
-rw-r--r--quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java5
89 files changed, 3181 insertions, 1418 deletions
diff --git a/quickstep/src/com/android/launcher3/HomeTransitionController.java b/quickstep/src/com/android/launcher3/HomeTransitionController.java
new file mode 100644
index 0000000000..b264115bf3
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/HomeTransitionController.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.SystemUiProxy;
+import com.android.wm.shell.transition.IHomeTransitionListener;
+
+/**
+ * Controls launcher response to home activity visibility changing.
+ */
+public class HomeTransitionController {
+
+ private final QuickstepLauncher mLauncher;
+ @Nullable private IHomeTransitionListener mHomeTransitionListener;
+
+ public HomeTransitionController(QuickstepLauncher launcher) {
+ mLauncher = launcher;
+ }
+
+ public void registerHomeTransitionListener() {
+ mHomeTransitionListener = new IHomeTransitionListener.Stub() {
+ @Override
+ public void onHomeVisibilityChanged(boolean isVisible) {
+ MAIN_EXECUTOR.execute(() -> {
+ if (mLauncher.getTaskbarUIController() != null) {
+ mLauncher.getTaskbarUIController().onLauncherVisibilityChanged(isVisible);
+ }
+ });
+ }
+ };
+
+ SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(mHomeTransitionListener);
+ }
+
+ public void unregisterHomeTransitionListener() {
+ SystemUiProxy.INSTANCE.get(mLauncher).setHomeTransitionListener(null);
+ mHomeTransitionListener = null;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 741249d53e..4898761736 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -22,6 +22,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -53,7 +55,9 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAU
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
+import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
+import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
@@ -74,8 +78,8 @@ import android.app.ActivityOptions;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -90,6 +94,7 @@ import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.util.Pair;
import android.util.Size;
import android.view.CrossWindowBlurListeners;
@@ -116,6 +121,7 @@ import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.model.data.ItemInfo;
@@ -160,6 +166,7 @@ import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.wm.shell.startingsurface.IStartingWindowListener;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
@@ -181,9 +188,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
*/
public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
- private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
- "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
-
public static final long APP_LAUNCH_DURATION = 500;
private static final long APP_LAUNCH_ALPHA_DURATION = 50;
@@ -226,7 +230,18 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private final float mClosingWindowTransY;
private final float mMaxShadowRadius;
- private final StartingWindowListener mStartingWindowListener = new StartingWindowListener();
+ private final StartingWindowListener mStartingWindowListener =
+ new StartingWindowListener(this);
+ private ContentObserver mAnimationRemovalObserver = new ContentObserver(
+ ORDERED_BG_EXECUTOR.getHandler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mAreAnimationsEnabled = Global.getFloat(mLauncher.getContentResolver(),
+ Global.ANIMATOR_DURATION_SCALE, 1f) > 0
+ || Global.getFloat(mLauncher.getContentResolver(),
+ Global.TRANSITION_ANIMATION_SCALE, 1f) > 0;
+ }
+ };
private DeviceProfile mDeviceProfile;
@@ -238,6 +253,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
private RemoteTransition mLauncherOpenTransition;
+ private final RemoteAnimationCoordinateTransfer mCoordinateTransfer;
+
private LauncherBackAnimationController mBackAnimationController;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
@@ -254,6 +271,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
// Pairs of window starting type and starting window background color for starting tasks
// Will never be larger than MAX_NUM_TASKS
private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
+ private boolean mAreAnimationsEnabled = true;
private final Interpolator mOpeningXInterpolator;
private final Interpolator mOpeningInterpolator;
@@ -264,6 +282,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mHandler = new Handler(Looper.getMainLooper());
mDeviceProfile = mLauncher.getDeviceProfile();
mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
+ checkAndMonitorIfAnimationsAreEnabled();
Resources res = mLauncher.getResources();
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
@@ -271,15 +290,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mLauncher.addOnDeviceProfileChangeListener(this);
- if (supportsSSplashScreen()) {
- mTaskStartParams = new LinkedHashMap<Integer, Pair<Integer, Integer>>(MAX_NUM_TASKS) {
+ if (ENABLE_SHELL_STARTING_SURFACE) {
+ mTaskStartParams = new LinkedHashMap<>(MAX_NUM_TASKS) {
@Override
protected boolean removeEldestEntry(Entry<Integer, Pair<Integer, Integer>> entry) {
return size() > MAX_NUM_TASKS;
}
};
- mStartingWindowListener.setTransitionManager(this);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
mStartingWindowListener);
}
@@ -287,6 +305,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x);
mOpeningInterpolator = AnimationUtils.loadInterpolator(context,
R.interpolator.emphasized_interpolator);
+ mCoordinateTransfer = new RemoteAnimationCoordinateTransfer(mLauncher);
}
@Override
@@ -311,8 +330,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
ItemInfo tag = (ItemInfo) v.getTag();
if (tag != null && tag.shouldUseBackgroundAnimation()) {
- ContainerAnimationRunner containerAnimationRunner =
- ContainerAnimationRunner.from(v, mStartingWindowListener, onEndCallback);
+ ContainerAnimationRunner containerAnimationRunner = ContainerAnimationRunner.from(
+ v, mLauncher, mStartingWindowListener, onEndCallback);
if (containerAnimationRunner != null) {
mAppLaunchRunner = containerAnimationRunner;
}
@@ -474,9 +493,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
/**
* Content is everything on screen except the background and the floating view (if any).
*
- * @param isAppOpening True when this is called when an app is opening.
- * False when this is called when an app is closing.
- * @param startDelay Start delay duration.
+ * @param isAppOpening True when this is called when an app is opening.
+ * False when this is called when an app is closing.
+ * @param startDelay Start delay duration.
* @param skipAllAppsScale True if we want to avoid scaling All Apps
*/
private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
@@ -660,7 +679,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
mDragLayer.getLocationOnScreen(dragLayerBounds);
final boolean hasSplashScreen;
- if (supportsSSplashScreen()) {
+ if (ENABLE_SHELL_STARTING_SURFACE) {
int taskId = openingTargets.getFirstAppTargetTaskId();
Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);
Pair<Integer, Integer> taskParams =
@@ -905,7 +924,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
RemoteAnimationTarget openingTarget = openingTargets.getFirstAppTarget();
int fallbackBackgroundColor = 0;
- if (openingTarget != null && supportsSSplashScreen()) {
+ if (openingTarget != null && ENABLE_SHELL_STARTING_SURFACE) {
fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
? mTaskStartParams.get(openingTarget.taskId).second : 0;
mTaskStartParams.remove(openingTarget.taskId);
@@ -1043,7 +1062,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
LaunchDepthController depthController = new LaunchDepthController(mLauncher);
ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth,
MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher))
- .setDuration(APP_LAUNCH_DURATION);
+ .setDuration(APP_LAUNCH_DURATION);
if (allowBlurringLauncher) {
// Create a temporary effect layer, that lives on top of launcher, so we can apply
@@ -1081,11 +1100,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
- if (hasControlRemoteAppTransitionPermission()) {
- RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
- addRemoteAnimations(definition);
- mLauncher.registerRemoteAnimations(definition);
- }
+ RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ addRemoteAnimations(definition);
+ mLauncher.registerRemoteAnimations(definition);
}
/**
@@ -1123,28 +1140,27 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
- if (hasControlRemoteAppTransitionPermission()) {
- mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
- mLauncherOpenTransition = new RemoteTransition(
- new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
- false /* startAtFrontOfQueue */).toRemoteTransition(),
- mLauncher.getIApplicationThread(), "QuickstepLaunchHome");
-
- TransitionFilter homeCheck = new TransitionFilter();
- // No need to handle the transition that also dismisses keyguard.
- homeCheck.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
- homeCheck.mRequirements =
- new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
- new TransitionFilter.Requirement()};
- homeCheck.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
- homeCheck.mRequirements[0].mTopActivity = mLauncher.getComponentName();
- homeCheck.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
- homeCheck.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
- homeCheck.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
- homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
- SystemUiProxy.INSTANCE.get(mLauncher)
- .registerRemoteTransition(mLauncherOpenTransition, homeCheck);
- }
+
+ mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
+ mLauncherOpenTransition = new RemoteTransition(
+ new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
+ false /* startAtFrontOfQueue */).toRemoteTransition(),
+ mLauncher.getIApplicationThread(), "QuickstepLaunchHome");
+
+ TransitionFilter homeCheck = new TransitionFilter();
+ // No need to handle the transition that also dismisses keyguard.
+ homeCheck.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
+ homeCheck.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
+ new TransitionFilter.Requirement()};
+ homeCheck.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
+ homeCheck.mRequirements[0].mTopActivity = mLauncher.getComponentName();
+ homeCheck.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+ homeCheck.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
+ homeCheck.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
+ homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
+ SystemUiProxy.INSTANCE.get(mLauncher)
+ .registerRemoteTransition(mLauncherOpenTransition, homeCheck);
if (mBackAnimationController != null) {
mBackAnimationController.registerBackCallbacks(mHandler);
}
@@ -1153,23 +1169,22 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
public void onActivityDestroyed() {
unregisterRemoteAnimations();
unregisterRemoteTransitions();
- mStartingWindowListener.setTransitionManager(null);
SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
+ ORDERED_BG_EXECUTOR.execute(() -> mLauncher.getContentResolver()
+ .unregisterContentObserver(mAnimationRemovalObserver));
}
private void unregisterRemoteAnimations() {
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
- if (hasControlRemoteAppTransitionPermission()) {
- mLauncher.unregisterRemoteAnimations();
-
- // Also clear strong references to the runners registered with the remote animation
- // definition so we don't have to wait for the system gc
- mWallpaperOpenRunner = null;
- mAppLaunchRunner = null;
- mKeyguardGoingAwayRunner = null;
- }
+ mLauncher.unregisterRemoteAnimations();
+
+ // Also clear strong references to the runners registered with the remote animation
+ // definition so we don't have to wait for the system gc
+ mWallpaperOpenRunner = null;
+ mAppLaunchRunner = null;
+ mKeyguardGoingAwayRunner = null;
}
protected void unregisterRemoteTransitions() {
@@ -1179,19 +1194,28 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
if (SEPARATE_RECENTS_ACTIVITY.get()) {
return;
}
- if (hasControlRemoteAppTransitionPermission()) {
- if (mLauncherOpenTransition == null) return;
- SystemUiProxy.INSTANCE.get(mLauncher).unregisterRemoteTransition(
- mLauncherOpenTransition);
- mLauncherOpenTransition = null;
- mWallpaperOpenTransitionRunner = null;
- }
+ if (mLauncherOpenTransition == null) return;
+ SystemUiProxy.INSTANCE.get(mLauncher).unregisterRemoteTransition(
+ mLauncherOpenTransition);
+ mLauncherOpenTransition = null;
+ mWallpaperOpenTransitionRunner = null;
if (mBackAnimationController != null) {
mBackAnimationController.unregisterBackCallbacks();
mBackAnimationController = null;
}
}
+ private void checkAndMonitorIfAnimationsAreEnabled() {
+ ORDERED_BG_EXECUTOR.execute(() -> {
+ mAnimationRemovalObserver.onChange(true);
+ mLauncher.getContentResolver().registerContentObserver(Global.getUriFor(
+ Global.ANIMATOR_DURATION_SCALE), false, mAnimationRemovalObserver);
+ mLauncher.getContentResolver().registerContentObserver(Global.getUriFor(
+ Global.TRANSITION_ANIMATION_SCALE), false, mAnimationRemovalObserver);
+
+ });
+ }
+
private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
for (RemoteAnimationTarget target : targets) {
if (target.mode == mode && target.taskInfo != null
@@ -1287,7 +1311,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
return null;
}
- final ComponentName[] taskInfoActivities = new ComponentName[] {
+ final ComponentName[] taskInfoActivities = new ComponentName[]{
runningTaskTarget.taskInfo.baseActivity,
runningTaskTarget.taskInfo.origActivity,
runningTaskTarget.taskInfo.realActivity,
@@ -1333,7 +1357,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
.getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
float secondaryDimension = orientationHandler
.getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
- final float targetX = primaryDimension / 2f;
+ final float targetX = primaryDimension / 2f;
final float targetY = secondaryDimension - dp.hotseatBarSizePx;
return new RectF(targetX - halfIconSize, targetY - halfIconSize,
targetX + halfIconSize, targetY + halfIconSize);
@@ -1344,7 +1368,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
*/
protected RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS,
- RectF closingWindowStartRect, float startWindowCornerRadius) {
+ RectF closingWindowStartRectF, float startWindowCornerRadius) {
FloatingIconView floatingIconView = null;
FloatingWidgetView floatingWidget = null;
RectF targetRect = new RectF();
@@ -1370,7 +1394,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
(LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
isTransluscent, fallbackBackgroundColor);
- } else if (launcherView != null) {
+ } else if (launcherView != null && mAreAnimationsEnabled) {
floatingIconView = getFloatingIconView(mLauncher, launcherView, null,
mLauncher.getTaskbarUIController() == null
? null
@@ -1384,13 +1408,16 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
boolean useTaskbarHotseatParams = mDeviceProfile.isTaskbarPresent && isInHotseat;
RectFSpringAnim anim = new RectFSpringAnim(useTaskbarHotseatParams
- ? new TaskbarHotseatSpringConfig(mLauncher, closingWindowStartRect, targetRect)
- : new DefaultSpringConfig(mLauncher, mDeviceProfile, closingWindowStartRect,
+ ? new TaskbarHotseatSpringConfig(mLauncher, closingWindowStartRectF, targetRect)
+ : new DefaultSpringConfig(mLauncher, mDeviceProfile, closingWindowStartRectF,
targetRect));
// Hook up floating views to the closing window animators.
- final int rotationChange = getRotationChange(targets);
- Rect windowTargetBounds = getWindowTargetBounds(targets, rotationChange);
+ // note the coordinate of closingWindowStartRect is based on launcher
+ Rect closingWindowStartRect = new Rect();
+ closingWindowStartRectF.round(closingWindowStartRect);
+ Rect closingWindowOriginalRect =
+ new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
if (floatingIconView != null) {
anim.addAnimatorListener(floatingIconView);
floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
@@ -1402,7 +1429,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
- windowTargetBounds, startWindowCornerRadius) {
+ closingWindowStartRect, closingWindowOriginalRect, startWindowCornerRadius) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
finalFloatingIconView.update(1f, currentRectF, progress, windowAlphaThreshold,
@@ -1420,7 +1447,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
FloatingWidgetView finalFloatingWidget = floatingWidget;
RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
- windowTargetBounds, startWindowCornerRadius) {
+ closingWindowStartRect, closingWindowOriginalRect, startWindowCornerRadius) {
@Override
public void onUpdate(RectF currentRectF, float progress) {
final float fallbackBackgroundAlpha =
@@ -1438,7 +1465,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
// If no floating icon or widget is present, animate the to the default window
// target rect.
anim.addOnUpdateListener(new SpringAnimRunner(
- targets, targetRect, windowTargetBounds, startWindowCornerRadius));
+ targets, targetRect, closingWindowStartRect, closingWindowOriginalRect,
+ startWindowCornerRadius));
}
// Use a fixed velocity to start the animation.
@@ -1519,20 +1547,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
return closingAnimator;
}
- private boolean supportsSSplashScreen() {
- return hasControlRemoteAppTransitionPermission()
- && Utilities.ATLEAST_S
- && ENABLE_SHELL_STARTING_SURFACE;
- }
-
- /**
- * Returns true if we have permission to control remote app transisions
- */
- public boolean hasControlRemoteAppTransitionPermission() {
- return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
- == PackageManager.PERMISSION_GRANTED;
- }
-
private void addCujInstrumentation(Animator anim, int cuj) {
anim.addListener(new AnimationSuccessListener() {
@Override
@@ -1638,7 +1652,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
// is initialized.
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(anim, playFallBackAnimation
- ? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME);
+ ? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ AccessibilityManagerCompat.sendTestProtocolEventToTest(
+ mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
+ }
+ });
+
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
@@ -1694,8 +1718,18 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
RectF windowTargetBounds =
new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets)));
+
+ final RectF resolveRectF = new RectF(windowTargetBounds);
+ for (RemoteAnimationTarget t : appTargets) {
+ if (t.mode == MODE_CLOSING) {
+ transferRectToTargetCoordinate(
+ t, windowTargetBounds, true, resolveRectF);
+ break;
+ }
+ }
+
Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
- appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds,
+ appTargets, wallpaperTargets, mFromUnlock, resolveRectF,
QuickStepContract.getWindowCornerRadius(mLauncher),
false /* fromPredictiveBack */);
@@ -1776,11 +1810,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
@Nullable
- private static ContainerAnimationRunner from(
- View v, StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
+ private static ContainerAnimationRunner from(View v, Launcher launcher,
+ StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
View viewToUse = findLaunchableViewWithBackground(v);
if (viewToUse == null) {
- viewToUse = v;
+ return null;
}
// The CUJ is logged by the click handler, so we don't log it inside the animation
@@ -1802,8 +1836,13 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
};
- ActivityLaunchAnimator.Callback callback = task -> ColorUtils.setAlphaComponent(
- startingWindowListener.getBackgroundColor(), 255);
+ ActivityLaunchAnimator.Callback callback = task -> {
+ final int backgroundColor =
+ startingWindowListener.mBackgroundColor == Color.TRANSPARENT
+ ? launcher.getScrimView().getBackgroundColor()
+ : startingWindowListener.mBackgroundColor;
+ return ColorUtils.setAlphaComponent(backgroundColor, 255);
+ };
ActivityLaunchAnimator.Listener listener = new ActivityLaunchAnimator.Listener() {
@Override
@@ -1917,24 +1956,68 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
}
- private class StartingWindowListener extends IStartingWindowListener.Stub {
- private QuickstepTransitionManager mTransitionManager;
+ private static class StartingWindowListener extends IStartingWindowListener.Stub {
+ private final WeakReference<QuickstepTransitionManager> mTransitionManagerRef;
private int mBackgroundColor;
- public void setTransitionManager(QuickstepTransitionManager transitionManager) {
- mTransitionManager = transitionManager;
+ private StartingWindowListener(QuickstepTransitionManager transitionManager) {
+ mTransitionManagerRef = new WeakReference<>(transitionManager);
}
@Override
public void onTaskLaunching(int taskId, int supportedType, int color) {
- mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
+ QuickstepTransitionManager transitionManager = mTransitionManagerRef.get();
+ if (transitionManager != null) {
+ transitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
+ }
mBackgroundColor = color;
}
+ }
+
+ /**
+ * Transfer the rectangle to another coordinate if needed.
+ *
+ * @param toLauncher which one is the anchor of this transfer, if true then transfer from
+ * animation target to launcher, false transfer from launcher to animation
+ * target.
+ */
+ public void transferRectToTargetCoordinate(RemoteAnimationTarget target, RectF currentRect,
+ boolean toLauncher, RectF resultRect) {
+ mCoordinateTransfer.transferRectToTargetCoordinate(
+ target, currentRect, toLauncher, resultRect);
+ }
+
+ private static class RemoteAnimationCoordinateTransfer {
+ private final QuickstepLauncher mLauncher;
+ private final Rect mDisplayRect = new Rect();
+ private final Rect mTmpResult = new Rect();
+
+ RemoteAnimationCoordinateTransfer(QuickstepLauncher launcher) {
+ mLauncher = launcher;
+ }
- public int getBackgroundColor() {
- return mBackgroundColor == Color.TRANSPARENT
- ? mLauncher.getScrimView().getBackgroundColor()
- : mBackgroundColor;
+ void transferRectToTargetCoordinate(RemoteAnimationTarget target, RectF currentRect,
+ boolean toLauncher, RectF resultRect) {
+ final int taskRotation = target.windowConfiguration.getRotation();
+ final DeviceProfile profile = mLauncher.getDeviceProfile();
+
+ final int rotationDelta = toLauncher
+ ? android.util.RotationUtils.deltaRotation(taskRotation, profile.rotationHint)
+ : android.util.RotationUtils.deltaRotation(profile.rotationHint, taskRotation);
+ if (rotationDelta != ROTATION_0) {
+ // Get original display size when task is on top but with different rotation
+ if (rotationDelta % 2 != 0 && toLauncher && (profile.rotationHint == ROTATION_0
+ || profile.rotationHint == ROTATION_180)) {
+ mDisplayRect.set(0, 0, profile.heightPx, profile.widthPx);
+ } else {
+ mDisplayRect.set(0, 0, profile.widthPx, profile.heightPx);
+ }
+ currentRect.round(mTmpResult);
+ android.util.RotationUtils.rotateBounds(mTmpResult, mDisplayRect, rotationDelta);
+ resultRect.set(mTmpResult);
+ } else {
+ resultRect.set(currentRect);
+ }
}
}
@@ -1949,17 +2032,49 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
private final float mStartRadius;
private final float mEndRadius;
private final SurfaceTransactionApplier mSurfaceApplier;
- private final Rect mWindowTargetBounds = new Rect();
+ private final Rect mWindowStartBounds = new Rect();
+ private final Rect mWindowOriginalBounds = new Rect();
private final Rect mTmpRect = new Rect();
+ /**
+ * Constructor for SpringAnimRunner
+ *
+ * @param appTargets the list of opening/closing apps
+ * @param targetRect target rectangle
+ * @param closingWindowStartRect start position of the window when the spring animation
+ * is started. In the predictive back to home case this
+ * will be smaller than closingWindowOriginalRect because
+ * the window is already scaled by the user gesture
+ * @param closingWindowOriginalRect Original unscaled window rect
+ * @param startWindowCornerRadius corner radius of window at the start position
+ */
SpringAnimRunner(RemoteAnimationTarget[] appTargets, RectF targetRect,
- Rect windowTargetBounds, float startWindowCornerRadius) {
+ Rect closingWindowStartRect, Rect closingWindowOriginalRect,
+ float startWindowCornerRadius) {
mAppTargets = appTargets;
mStartRadius = startWindowCornerRadius;
mEndRadius = Math.max(1, targetRect.width()) / 2f;
mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
- mWindowTargetBounds.set(windowTargetBounds);
+ mWindowStartBounds.set(closingWindowStartRect);
+ mWindowOriginalBounds.set(closingWindowOriginalRect);
+
+ // transfer the coordinate based on animation target.
+ if (mAppTargets != null) {
+ for (RemoteAnimationTarget t : mAppTargets) {
+ if (t.mode == MODE_CLOSING) {
+ final RectF transferRect = new RectF(mWindowStartBounds);
+ final RectF result = new RectF();
+ transferRectToTargetCoordinate(t, transferRect, false, result);
+ result.round(mWindowStartBounds);
+
+ transferRect.set(closingWindowOriginalRect);
+ transferRectToTargetCoordinate(t, transferRect, false, result);
+ result.round(mWindowOriginalBounds);
+ break;
+ }
+ }
+ }
}
public float getCornerRadius(float progress) {
@@ -1980,26 +2095,28 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
if (target.mode == MODE_CLOSING) {
+ transferRectToTargetCoordinate(target, currentRectF, false, currentRectF);
currentRectF.round(mCurrentRect);
// Scale the target window to match the currentRectF.
final float scale;
// We need to infer the crop (we crop the window to match the currentRectF).
- if (mWindowTargetBounds.height() > mWindowTargetBounds.width()) {
- scale = Math.min(1f, currentRectF.width() / mWindowTargetBounds.width());
+ if (mWindowStartBounds.height() > mWindowStartBounds.width()) {
+ scale = Math.min(1f, currentRectF.width() / mWindowOriginalBounds.width());
int unscaledHeight = (int) (mCurrentRect.height() * (1f / scale));
- int croppedHeight = mWindowTargetBounds.height() - unscaledHeight;
- mTmpRect.set(0, 0, mWindowTargetBounds.width(),
- mWindowTargetBounds.height() - croppedHeight);
+ int croppedHeight = mWindowStartBounds.height() - unscaledHeight;
+ mTmpRect.set(0, 0, mWindowOriginalBounds.width(),
+ mWindowStartBounds.height() - croppedHeight);
} else {
- scale = Math.min(1f, currentRectF.height() / mWindowTargetBounds.height());
+ scale = Math.min(1f, currentRectF.height()
+ / mWindowOriginalBounds.height());
int unscaledWidth = (int) (mCurrentRect.width() * (1f / scale));
- int croppedWidth = mWindowTargetBounds.width() - unscaledWidth;
- mTmpRect.set(0, 0, mWindowTargetBounds.width() - croppedWidth,
- mWindowTargetBounds.height());
+ int croppedWidth = mWindowStartBounds.width() - unscaledWidth;
+ mTmpRect.set(0, 0, mWindowStartBounds.width() - croppedWidth,
+ mWindowOriginalBounds.height());
}
// Match size and position of currentRect.
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
new file mode 100644
index 0000000000..43716abd97
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.os.Bundle;
+import android.view.WindowInsetsController;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.dragndrop.SimpleDragLayer;
+import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.widget.BaseWidgetSheet;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
+
+import java.util.ArrayList;
+
+/** An Activity that can host Launcher's widget picker. */
+public class WidgetPickerActivity extends BaseActivity {
+ private SimpleDragLayer<WidgetPickerActivity> mDragLayer;
+ private WidgetsModel mModel;
+ private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {});
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
+
+ LauncherAppState app = LauncherAppState.getInstance(this);
+ InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
+
+ mDeviceProfile = idp.getDeviceProfile(this);
+ mModel = new WidgetsModel();
+
+ setContentView(R.layout.widget_picker_activity);
+ mDragLayer = findViewById(R.id.drag_layer);
+ mDragLayer.recreateControllers();
+
+ WindowInsetsController wc = mDragLayer.getWindowInsetsController();
+ wc.hide(navigationBars() + statusBars());
+
+ BaseWidgetSheet widgetSheet = WidgetsFullSheet.show(this, true);
+ widgetSheet.disableNavBarScrim(true);
+ widgetSheet.addOnCloseListener(this::finish);
+
+ refreshAndBindWidgets();
+ }
+
+ @NonNull
+ @Override
+ public PopupDataProvider getPopupDataProvider() {
+ return mPopupDataProvider;
+ }
+
+ @Override
+ public SimpleDragLayer<WidgetPickerActivity> getDragLayer() {
+ return mDragLayer;
+ }
+
+ private void refreshAndBindWidgets() {
+ MODEL_EXECUTOR.execute(() -> {
+ LauncherAppState app = LauncherAppState.getInstance(this);
+ mModel.update(app, null);
+ final ArrayList<WidgetsListBaseEntry> widgets =
+ mModel.getWidgetsListForPicker(app.getContext());
+ MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
+ });
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index e8374b813c..037f7a8fea 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -35,9 +35,7 @@ import androidx.core.content.ContextCompat;
import com.android.launcher3.R;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.ActivityContext;
/**
* A view which shows a horizontal divider
@@ -93,10 +91,7 @@ public class AppsDividerView extends View implements FloatingHeaderRow {
? R.color.all_apps_label_text_dark
: R.color.all_apps_label_text);
- OnboardingPrefs<?> onboardingPrefs = ActivityContext.lookupContext(
- getContext()).getOnboardingPrefs();
- mShowAllAppsLabel = onboardingPrefs == null || !onboardingPrefs.hasReachedMaxCount(
- ALL_APPS_VISITED_COUNT);
+ mShowAllAppsLabel = !ALL_APPS_VISITED_COUNT.hasReachedMax(context);
}
public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index f82474a8e8..1440498e15 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -36,6 +36,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
import com.android.launcher3.model.data.ItemInfo;
@@ -126,6 +127,10 @@ public class PredictionRowView<T extends Context & ActivityContext>
int verticalPadding = getResources().getDimensionPixelSize(
R.dimen.all_apps_predicted_icon_vertical_padding);
int totalHeight = iconHeight + iconPadding + textHeight + verticalPadding * 2;
+ if (FeatureFlags.enableTwolineAllapps()) {
+ // Add extra textHeight to the existing total height.
+ totalHeight += textHeight;
+ }
return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
new file mode 100644
index 0000000000..10733fbbea
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -0,0 +1,109 @@
+/*
+ * 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.desktop
+
+import android.app.IApplicationThread
+import android.os.IBinder
+import android.os.RemoteException
+import android.util.Log
+import android.view.SurfaceControl
+import android.window.IRemoteTransition
+import android.window.IRemoteTransitionFinishedCallback
+import android.window.RemoteTransition
+import android.window.TransitionInfo
+import com.android.launcher3.statehandlers.DepthController
+import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.views.DesktopTaskView
+import java.util.function.Consumer
+
+/** Manage recents related operations with desktop tasks */
+class DesktopRecentsTransitionController(
+ private val stateManager: StateManager<*>,
+ private val systemUiProxy: SystemUiProxy,
+ private val appThread: IApplicationThread,
+ private val depthController: DepthController?
+) {
+
+ /** Launch desktop tasks from recents view */
+ fun launchDesktopFromRecents(
+ desktopTaskView: DesktopTaskView,
+ callback: Consumer<Boolean>? = null
+ ) {
+ val animRunner =
+ RemoteDesktopLaunchTransitionRunner(
+ desktopTaskView,
+ stateManager,
+ depthController,
+ callback
+ )
+ val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
+ systemUiProxy.showDesktopApps(desktopTaskView.display.displayId, transition)
+ }
+
+ private class RemoteDesktopLaunchTransitionRunner(
+ private val desktopTaskView: DesktopTaskView,
+ private val stateManager: StateManager<*>,
+ private val depthController: DepthController?,
+ private val successCallback: Consumer<Boolean>?
+ ) : IRemoteTransition.Stub() {
+
+ override fun startAnimation(
+ token: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ finishCallback: IRemoteTransitionFinishedCallback
+ ) {
+ val errorHandlingFinishCallback = Runnable {
+ try {
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Failed to call finish callback for desktop recents animation", e)
+ }
+ }
+
+ MAIN_EXECUTOR.execute {
+ TaskViewUtils.composeRecentsDesktopLaunchAnimator(
+ desktopTaskView,
+ stateManager,
+ depthController,
+ info,
+ t
+ ) {
+ errorHandlingFinishCallback.run()
+ successCallback?.accept(true)
+ }
+ }
+ }
+
+ override fun mergeAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ mergeTarget: IBinder,
+ finishCallback: IRemoteTransitionFinishedCallback
+ ) {}
+
+ override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) {
+ }
+ }
+
+ companion object {
+ const val TAG = "DesktopRecentsTransitionController"
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index a63f9e8b29..baea418160 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -23,6 +23,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
+import static com.android.launcher3.util.OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -41,6 +42,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -59,7 +61,6 @@ import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.views.Snackbar;
import java.io.PrintWriter;
@@ -104,12 +105,11 @@ public class HotseatPredictionController implements DragController.DragListener,
if (mLauncher.getWorkspace().isSwitchingState()) return false;
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onWorkspaceItemLongClick");
- if (mEnableHotseatLongPressTipForTesting && !mLauncher.getOnboardingPrefs().getBoolean(
- OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
+ if (mEnableHotseatLongPressTipForTesting && !HOTSEAT_LONGPRESS_TIP_SEEN.get(mLauncher)) {
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(getSettingsIntent()));
- mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
+ LauncherPrefs.get(mLauncher).put(HOTSEAT_LONGPRESS_TIP_SEEN, true);
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
return true;
}
diff --git a/quickstep/src/com/android/launcher3/model/PredictionHelper.java b/quickstep/src/com/android/launcher3/model/PredictionHelper.java
index 738dd83cbc..dbd99e1eab 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionHelper.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionHelper.java
@@ -67,6 +67,9 @@ public final class PredictionHelper {
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
context.getPackageName(), info.user).build();
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR) {
+ return new AppTarget.Builder(new AppTargetId("app_pair:" + info.id),
+ context.getPackageName(), info.user).build();
}
return null;
}
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index e504141024..2fcbe4e0fe 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -15,8 +15,9 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.LauncherPrefs.nonRestorableItem;
+import static com.android.launcher3.EncryptionType.ENCRYPTED;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
import android.app.prediction.AppTarget;
@@ -29,6 +30,7 @@ import android.os.UserHandle;
import androidx.annotation.NonNull;
+import com.android.launcher3.ConstantItem;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -47,6 +49,9 @@ import java.util.stream.Collectors;
*/
public class PredictionUpdateTask extends BaseModelUpdateTask {
+ public static final ConstantItem<Boolean> LAST_PREDICTION_ENABLED =
+ nonRestorableItem("last_prediction_enabled_state", true, ENCRYPTED);
+
private final List<AppTarget> mTargets;
private final PredictorState mPredictorState;
@@ -61,8 +66,7 @@ public class PredictionUpdateTask extends BaseModelUpdateTask {
Context context = app.getContext();
// TODO: remove this
- LauncherPrefs.getDevicePrefs(context).edit()
- .putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
+ LauncherPrefs.get(context).put(LAST_PREDICTION_ENABLED, !mTargets.isEmpty());
Set<UserHandle> usersForChangedShortcuts =
dataModel.extraItems.get(mPredictorState.containerId).items.stream()
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 32361a862a..667f78498e 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -18,7 +18,8 @@ package com.android.launcher3.model;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.formatElapsedTime;
-import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
+import static com.android.launcher3.LauncherPrefs.nonRestorableItem;
+import static com.android.launcher3.EncryptionType.ENCRYPTED;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
@@ -39,7 +40,6 @@ import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
@@ -55,8 +55,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.ConstantItem;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.InstanceId;
@@ -86,14 +88,15 @@ import java.util.stream.IntStream;
*/
public class QuickstepModelDelegate extends ModelDelegate {
- public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
- private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
private static final boolean IS_DEBUG = false;
private static final String TAG = "QuickstepModelDelegate";
+ private static final ConstantItem<Long> LAST_SNAPSHOT_TIME_MILLIS =
+ nonRestorableItem("LAST_SNAPSHOT_TIME_MILLIS", 0L, ENCRYPTED);
+
@VisibleForTesting
final PredictorState mAllAppsState =
new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
@@ -211,8 +214,8 @@ public class QuickstepModelDelegate extends ModelDelegate {
super.modelLoadComplete();
// Log snapshot of the model
- SharedPreferences prefs = getDevicePrefs(mApp.getContext());
- long lastSnapshotTimeMillis = prefs.getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
+ LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
+ long lastSnapshotTimeMillis = prefs.get(LAST_SNAPSHOT_TIME_MILLIS);
// Log snapshot only if previous snapshot was older than a day
long now = System.currentTimeMillis();
if (now - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
@@ -233,7 +236,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
}
additionalSnapshotEvents(instanceId);
- prefs.edit().putLong(LAST_SNAPSHOT_TIME_MILLIS, now).apply();
+ prefs.put(LAST_SNAPSHOT_TIME_MILLIS, now);
}
// Only register for launcher snapshot logging if this is the primary ModelDelegate
diff --git a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
index b982688ce7..8b71f01396 100644
--- a/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
+++ b/quickstep/src/com/android/launcher3/secondarydisplay/SecondaryDisplayPredictionsImpl.java
@@ -22,7 +22,6 @@ import android.content.Context;
import com.android.launcher3.appprediction.AppsDividerView;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.views.ActivityContext;
/**
@@ -30,22 +29,21 @@ import com.android.launcher3.views.ActivityContext;
*/
@SuppressWarnings("unused")
public final class SecondaryDisplayPredictionsImpl extends SecondaryDisplayPredictions {
+
private final ActivityContext mActivityContext;
+ private final Context mContext;
public SecondaryDisplayPredictionsImpl(Context context) {
+ mContext = context;
mActivityContext = ActivityContext.lookupContext(context);
}
@Override
void updateAppDivider() {
- OnboardingPrefs<?> onboardingPrefs = mActivityContext.getOnboardingPrefs();
- if (onboardingPrefs != null) {
- mActivityContext.getAppsView().getFloatingHeaderView()
- .findFixedRowByType(AppsDividerView.class)
- .setShowAllAppsLabel(
- !onboardingPrefs.hasReachedMaxCount(ALL_APPS_VISITED_COUNT));
- onboardingPrefs.incrementEventCount(ALL_APPS_VISITED_COUNT);
- }
+ mActivityContext.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(AppsDividerView.class)
+ .setShowAllAppsLabel(!ALL_APPS_VISITED_COUNT.hasReachedMax(mContext));
+ ALL_APPS_VISITED_COUNT.increment(mContext);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index ecf483caa4..a7e8118851 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -15,8 +15,11 @@
*/
package com.android.launcher3.statehandlers;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import android.os.Debug;
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
@@ -46,6 +49,7 @@ public class DesktopVisibilityController {
private boolean mFreeformTasksVisible;
private boolean mInOverviewState;
+ private boolean mBackgroundStateEnabled;
private boolean mGestureInProgress;
@Nullable
@@ -103,18 +107,14 @@ public class DesktopVisibilityController {
}
/**
- * Whether desktop mode is supported.
- */
- private boolean isDesktopModeSupported() {
- return SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false)
- || SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
- }
-
- /**
* Whether freeform windows are visible in desktop mode.
*/
public boolean areFreeformTasksVisible() {
- return mFreeformTasksVisible;
+ if (DEBUG) {
+ Log.d(TAG, "areFreeformTasksVisible: freeformVisible=" + mFreeformTasksVisible
+ + " overview=" + mInOverviewState);
+ }
+ return mFreeformTasksVisible && !mInOverviewState;
}
/**
@@ -122,7 +122,8 @@ public class DesktopVisibilityController {
*/
public void setFreeformTasksVisible(boolean freeformTasksVisible) {
if (DEBUG) {
- Log.d(TAG, "setFreeformTasksVisible: visible=" + freeformTasksVisible);
+ Log.d(TAG, "setFreeformTasksVisible: visible=" + freeformTasksVisible
+ + " currentValue=" + mFreeformTasksVisible);
}
if (!isDesktopModeSupported()) {
return;
@@ -147,11 +148,21 @@ public class DesktopVisibilityController {
}
/**
- * Sets whether the overview is visible and updates launcher visibility based on that.
+ * Process launcher state change and update launcher view visibility based on desktop state
*/
- public void setOverviewStateEnabled(boolean overviewStateEnabled) {
+ public void onLauncherStateChanged(LauncherState state) {
if (DEBUG) {
- Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled);
+ Log.d(TAG, "onLauncherStateChanged: newState=" + state);
+ }
+ setBackgroundStateEnabled(state == BACKGROUND_APP);
+ // Desktop visibility tracks overview and background state separately
+ setOverviewStateEnabled(state != BACKGROUND_APP && state.overviewUi);
+ }
+
+ private void setOverviewStateEnabled(boolean overviewStateEnabled) {
+ if (DEBUG) {
+ Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled
+ + " currentValue=" + mInOverviewState);
}
if (!isDesktopModeSupported()) {
return;
@@ -161,7 +172,30 @@ public class DesktopVisibilityController {
if (mInOverviewState) {
setLauncherViewsVisibility(View.VISIBLE);
markLauncherResumed();
- } else if (mFreeformTasksVisible) {
+ } else if (areFreeformTasksVisible() && !mGestureInProgress) {
+ // Switching out of overview state and gesture finished.
+ // If freeform tasks are still visible, hide launcher again.
+ setLauncherViewsVisibility(View.INVISIBLE);
+ markLauncherPaused();
+ }
+ }
+ }
+
+ private void setBackgroundStateEnabled(boolean backgroundStateEnabled) {
+ if (DEBUG) {
+ Log.d(TAG, "setBackgroundStateEnabled: enabled=" + backgroundStateEnabled
+ + " currentValue=" + mBackgroundStateEnabled);
+ }
+ if (!isDesktopModeSupported()) {
+ return;
+ }
+ if (backgroundStateEnabled != mBackgroundStateEnabled) {
+ mBackgroundStateEnabled = backgroundStateEnabled;
+ if (mBackgroundStateEnabled) {
+ setLauncherViewsVisibility(View.VISIBLE);
+ markLauncherResumed();
+ } else if (areFreeformTasksVisible() && !mGestureInProgress) {
+ // Switching out of background state. If freeform tasks are visible, pause launcher.
setLauncherViewsVisibility(View.INVISIBLE);
markLauncherPaused();
}
@@ -182,6 +216,9 @@ public class DesktopVisibilityController {
if (!isDesktopModeSupported()) {
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "setRecentsGestureStart");
+ }
setRecentsGestureInProgress(true);
}
@@ -193,6 +230,9 @@ public class DesktopVisibilityController {
if (!isDesktopModeSupported()) {
return;
}
+ if (DEBUG) {
+ Log.d(TAG, "setRecentsGestureEnd: endTarget=" + endTarget);
+ }
setRecentsGestureInProgress(false);
if (endTarget == null) {
@@ -202,9 +242,6 @@ public class DesktopVisibilityController {
}
private void setRecentsGestureInProgress(boolean gestureInProgress) {
- if (DEBUG) {
- Log.d(TAG, "setGestureInProgress: inProgress=" + gestureInProgress);
- }
if (gestureInProgress != mGestureInProgress) {
mGestureInProgress = gestureInProgress;
}
@@ -221,7 +258,8 @@ public class DesktopVisibilityController {
private void setLauncherViewsVisibility(int visibility) {
if (DEBUG) {
- Log.d(TAG, "setLauncherViewsVisibility: visibility=" + visibility);
+ Log.d(TAG, "setLauncherViewsVisibility: visibility=" + visibility + " "
+ + Debug.getCaller());
}
View workspaceView = mLauncher.getWorkspace();
if (workspaceView != null) {
@@ -235,7 +273,7 @@ public class DesktopVisibilityController {
private void markLauncherPaused() {
if (DEBUG) {
- Log.d(TAG, "markLauncherPaused");
+ Log.d(TAG, "markLauncherPaused " + Debug.getCaller());
}
StatefulActivity<LauncherState> activity =
QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
@@ -246,7 +284,7 @@ public class DesktopVisibilityController {
private void markLauncherResumed() {
if (DEBUG) {
- Log.d(TAG, "markLauncherResumed");
+ Log.d(TAG, "markLauncherResumed " + Debug.getCaller());
}
StatefulActivity<LauncherState> activity =
QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index 331184a081..c201236586 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -20,8 +20,6 @@ import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
@@ -34,12 +32,10 @@ public abstract class BaseTaskbarContext extends ContextThemeWrapper implements
protected final LayoutInflater mLayoutInflater;
private final List<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
- private final OnboardingPrefs<BaseTaskbarContext> mOnboardingPrefs;
public BaseTaskbarContext(Context windowContext) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
- mOnboardingPrefs = new OnboardingPrefs<>(this, LauncherPrefs.getPrefs(this));
}
@Override
@@ -52,11 +48,6 @@ public abstract class BaseTaskbarContext extends ContextThemeWrapper implements
return mDPChangeListeners;
}
- @Override
- public OnboardingPrefs<BaseTaskbarContext> getOnboardingPrefs() {
- return mOnboardingPrefs;
- }
-
/** Callback invoked when a drag is initiated within this context. */
public abstract void onDragStart();
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
index 885afffcbb..0a9dfff568 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopNavbarButtonsViewController.java
@@ -18,11 +18,15 @@ package com.android.launcher3.taskbar;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_NOTIFICATIONS;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_QUICK_SETTINGS;
+import android.content.Context;
+import android.content.pm.ActivityInfo.Config;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.R;
/**
@@ -39,8 +43,8 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl
private TaskbarControllers mControllers;
public DesktopNavbarButtonsViewController(TaskbarActivityContext context,
- FrameLayout navButtonsView) {
- super(context, navButtonsView);
+ @Nullable Context navigationBarPanelContext, FrameLayout navButtonsView) {
+ super(context, navigationBarPanelContext, navButtonsView);
mContext = context;
mNavButtonsView = navButtonsView;
mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
@@ -56,6 +60,11 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl
@Override
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ super.init(controllers);
+ }
+
+ @Override
+ protected void setupController() {
mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarHeight;
// Quick settings and notifications buttons
@@ -72,4 +81,7 @@ public class DesktopNavbarButtonsViewController extends NavbarButtonsViewControl
/** Cleans up on destroy */
@Override
public void onDestroy() { }
+
+ @Override
+ public void onConfigurationChanged(@Config int configChanges) { }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 072fc3026e..f58fd4547d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@@ -28,7 +30,6 @@ import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.GroupTask;
-import com.android.quickstep.views.DesktopTaskView;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -109,7 +110,7 @@ public final class KeyboardQuickSwitchController implements
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED
+ isDesktopModeSupported()
&& desktopController != null
&& desktopController.areFreeformTasksVisible();
@@ -136,7 +137,7 @@ public final class KeyboardQuickSwitchController implements
// Hide all desktop tasks and show them on the hidden tile
int hiddenDesktopTasks = 0;
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (isDesktopModeSupported()) {
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
hiddenDesktopTasks = desktopTask.tasks.size();
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 3e1a6ae9cb..a9d50b9332 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -40,6 +40,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import java.util.function.Consumer;
+import kotlin.Unit;
+
/**
* A view that displays a recent task during a keyboard quick switch.
*/
@@ -96,17 +98,18 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
Resources resources = mContext.getResources();
Preconditions.assertNotNull(mContent);
- mBorderAnimator = new BorderAnimator(
+ mBorderAnimator = BorderAnimator.createScalingBorderAnimator(
/* borderRadiusPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_task_view_radius),
- /* borderColor= */ mBorderColor,
- /* borderAnimationParams= */ new BorderAnimator.ScalingParams(
- /* borderWidthPx= */ resources.getDimensionPixelSize(
+ /* borderWidthPx= */ resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width),
- /* boundsBuilder= */ bounds -> bounds.set(
- 0, 0, getWidth(), getHeight()),
- /* targetView= */ this,
- /* contentView= */ mContent));
+ /* boundsBuilder= */ bounds -> {
+ bounds.set(0, 0, getWidth(), getHeight());
+ return Unit.INSTANCE;
+ },
+ /* targetView= */ this,
+ /* contentView= */ mContent,
+ /* borderColor= */ mBorderColor);
}
@Nullable
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 4e9e3019a9..42c423ce83 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -46,6 +46,8 @@ import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.quickstep.util.GroupTask;
import java.util.HashMap;
@@ -360,11 +362,8 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
OPEN_OUTLINE_INTERPOLATOR));
}
});
- if (currentFocusIndexOverride == -1) {
- initializeScroll(/* index= */ 0, /* shouldTruncateTarget= */ false);
- } else {
- animateFocusMove(-1, currentFocusIndexOverride);
- }
+ animateFocusMove(-1, currentFocusIndexOverride == -1
+ ? Math.min(mContent.getChildCount(), 1) : currentFocusIndexOverride);
displayedContent.setVisibility(VISIBLE);
setVisibility(VISIBLE);
requestFocus();
@@ -413,7 +412,8 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
// there are more tasks
initializeScroll(
firstVisibleTaskIndex,
- /* shouldTruncateTarget= */ firstVisibleTaskIndex != toIndex);
+ /* shouldTruncateTarget= */ firstVisibleTaskIndex != 0
+ && firstVisibleTaskIndex != toIndex);
} else if (toIndex > fromIndex || toIndex == 0) {
// Scrolling to next task view
if (mIsRtl) {
@@ -439,6 +439,13 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
}
@Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ TestLogging.recordKeyEvent(
+ TestProtocol.SEQUENCE_MAIN, "KeyboardQuickSwitchView key event", event);
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return (mViewCallbacks != null
&& mViewCallbacks.onKeyUp(keyCode, event, mIsRtl, mDisplayingRecentTasks))
@@ -454,56 +461,80 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
return;
}
if (mIsRtl) {
- scrollRightTo(
- task, shouldTruncateTarget, /* smoothScroll= */ false);
- } else {
scrollLeftTo(
- task, shouldTruncateTarget, /* smoothScroll= */ false);
+ task,
+ shouldTruncateTarget,
+ /* smoothScroll= */ false,
+ /* waitForLayout= */ true);
+ } else {
+ scrollRightTo(
+ task,
+ shouldTruncateTarget,
+ /* smoothScroll= */ false,
+ /* waitForLayout= */ true);
}
}
private void scrollRightTo(@NonNull View targetTask) {
- scrollRightTo(targetTask, /* shouldTruncateTarget= */ false, /* smoothScroll= */ true);
+ scrollRightTo(
+ targetTask,
+ /* shouldTruncateTarget= */ false,
+ /* smoothScroll= */ true,
+ /* waitForLayout= */ false);
}
private void scrollRightTo(
- @NonNull View targetTask, boolean shouldTruncateTarget, boolean smoothScroll) {
+ @NonNull View targetTask,
+ boolean shouldTruncateTarget,
+ boolean smoothScroll,
+ boolean waitForLayout) {
if (!mDisplayingRecentTasks) {
return;
}
if (smoothScroll && !shouldScroll(targetTask, shouldTruncateTarget)) {
return;
}
- int scrollTo = targetTask.getLeft() - mSpacing
- + (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
- // Scroll so that the focused task is to the left of the list
- if (smoothScroll) {
- mScrollView.smoothScrollTo(scrollTo, 0);
- } else {
- mScrollView.scrollTo(scrollTo, 0);
- }
+ runScrollCommand(waitForLayout, () -> {
+ int scrollTo = targetTask.getLeft() - mSpacing
+ + (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
+ // Scroll so that the focused task is to the left of the list
+ if (smoothScroll) {
+ mScrollView.smoothScrollTo(scrollTo, 0);
+ } else {
+ mScrollView.scrollTo(scrollTo, 0);
+ }
+ });
}
private void scrollLeftTo(@NonNull View targetTask) {
- scrollLeftTo(targetTask, /* shouldTruncateTarget= */ false, /* smoothScroll= */ true);
+ scrollLeftTo(
+ targetTask,
+ /* shouldTruncateTarget= */ false,
+ /* smoothScroll= */ true,
+ /* waitForLayout= */ false);
}
private void scrollLeftTo(
- @NonNull View targetTask, boolean shouldTruncateTarget, boolean smoothScroll) {
+ @NonNull View targetTask,
+ boolean shouldTruncateTarget,
+ boolean smoothScroll,
+ boolean waitForLayout) {
if (!mDisplayingRecentTasks) {
return;
}
if (smoothScroll && !shouldScroll(targetTask, shouldTruncateTarget)) {
return;
}
- int scrollTo = targetTask.getRight() + mSpacing - mScrollView.getWidth()
- - (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
- // Scroll so that the focused task is to the right of the list
- if (smoothScroll) {
- mScrollView.smoothScrollTo(scrollTo, 0);
- } else {
- mScrollView.scrollTo(scrollTo, 0);
- }
+ runScrollCommand(waitForLayout, () -> {
+ int scrollTo = targetTask.getRight() + mSpacing - mScrollView.getWidth()
+ - (shouldTruncateTarget ? targetTask.getWidth() / 2 : 0);
+ // Scroll so that the focused task is to the right of the list
+ if (smoothScroll) {
+ mScrollView.smoothScrollTo(scrollTo, 0);
+ } else {
+ mScrollView.scrollTo(scrollTo, 0);
+ }
+ });
}
private boolean shouldScroll(@NonNull View targetTask, boolean shouldTruncateTarget) {
@@ -514,6 +545,21 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
return isTargetTruncated && !shouldTruncateTarget;
}
+ private void runScrollCommand(boolean waitForLayout, @NonNull Runnable scrollCommand) {
+ if (!waitForLayout) {
+ scrollCommand.run();
+ return;
+ }
+ mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ scrollCommand.run();
+ mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ });
+ }
+
@Nullable
protected KeyboardQuickSwitchTaskView getTaskAt(int index) {
return !mDisplayingRecentTasks || index < 0 || index >= mContent.getChildCount()
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index a293f748b9..b29ce6be14 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -24,7 +24,7 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
import com.android.quickstep.SystemUiProxy;
@@ -92,27 +92,19 @@ public class KeyboardQuickSwitchViewController {
protected void closeQuickSwitchView(boolean animate) {
if (mCloseAnimation != null) {
- if (animate) {
- // Let currently-running animation finish.
- return;
- } else {
- mCloseAnimation.cancel();
+ // Let currently-running animation finish.
+ if (!animate) {
+ mCloseAnimation.end();
}
+ return;
}
if (!animate) {
- mCloseAnimation = null;
onCloseComplete();
return;
}
mCloseAnimation = mKeyboardQuickSwitchView.getCloseAnimation();
- mCloseAnimation.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mCloseAnimation = null;
- onCloseComplete();
- }
- });
+ mCloseAnimation.addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
mCloseAnimation.start();
}
@@ -141,11 +133,20 @@ public class KeyboardQuickSwitchViewController {
GroupTask task = mControllerCallbacks.getTaskAt(index);
if (task == null) {
return Math.max(0, index);
- } else if (mOnDesktop) {
+ }
+ Task task2 = task.task2;
+ int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId;
+ if (runningTaskId == task.task1.key.id
+ || (task2 != null && runningTaskId == task2.key.id)) {
+ // Ignore attempts to run the selected task if it is already running.
+ return -1;
+ }
+
+ if (mOnDesktop) {
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
.showDesktopApp(task.task1.key.id));
- } else if (task.task2 == null) {
+ } else if (task2 == null) {
UI_HELPER_EXECUTOR.execute(() ->
ActivityManagerWrapper.getInstance().startActivityFromRecents(
task.task1.key,
@@ -153,13 +154,13 @@ public class KeyboardQuickSwitchViewController {
taskView == null ? mKeyboardQuickSwitchView : taskView, null)
.options));
} else {
- mControllers.uiController.launchSplitTasks(
- taskView == null ? mKeyboardQuickSwitchView : taskView, task);
+ mControllers.uiController.launchSplitTasks(task);
}
return -1;
}
private void onCloseComplete() {
+ mCloseAnimation = null;
mOverlayContext.getDragLayer().removeView(mKeyboardQuickSwitchView);
mControllerCallbacks.onCloseComplete();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 4e834ec3ef..159a6eff07 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -18,7 +18,7 @@ package com.android.launcher3.taskbar;
import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
-import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED;
+import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import android.animation.Animator;
@@ -26,7 +26,6 @@ import android.animation.AnimatorSet;
import android.os.RemoteException;
import android.util.Log;
import android.view.TaskTransitionSpec;
-import android.view.View;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
@@ -41,6 +40,7 @@ import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.taskbar.bubbles.BubbleBarController;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
@@ -99,7 +99,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mLauncher.setTaskbarUIController(this);
- onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */);
+ onLauncherVisibilityChanged(mLauncher.hasBeenResumed(), true /* fromInit */);
onStashedInAppChanged(mLauncher.getDeviceProfile());
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
@@ -118,7 +118,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
@Override
protected void onDestroy() {
super.onDestroy();
- onLauncherResumedOrPaused(false);
+ onLauncherVisibilityChanged(false);
mTaskbarLauncherStateController.onDestroy();
mLauncher.setTaskbarUIController(null);
@@ -159,8 +159,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
* sub-animations are properly coordinated. This duration should not
* actually be used since this animation tracks a swipe progress.
*/
- protected void addLauncherResumeAnimation(AnimatorSet animation, int placeholderDuration) {
- animation.play(onLauncherResumedOrPaused(
+ protected void addLauncherVisibilityChangedAnimation(AnimatorSet animation,
+ int placeholderDuration) {
+ animation.play(onLauncherVisibilityChanged(
/* isResumed= */ true,
/* fromInit= */ false,
/* startAnimation= */ false,
@@ -170,40 +171,53 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
/**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
- public void onLauncherResumedOrPaused(boolean isResumed) {
- onLauncherResumedOrPaused(isResumed, false /* fromInit */);
+ public void onLauncherVisibilityChanged(boolean isVisible) {
+ onLauncherVisibilityChanged(isVisible, false /* fromInit */);
}
- private void onLauncherResumedOrPaused(boolean isResumed, boolean fromInit) {
- onLauncherResumedOrPaused(
- isResumed,
+ private void onLauncherVisibilityChanged(boolean isVisible, boolean fromInit) {
+ onLauncherVisibilityChanged(
+ isVisible,
fromInit,
/* startAnimation= */ true,
DisplayController.isTransientTaskbar(mLauncher)
? TRANSIENT_TASKBAR_TRANSITION_DURATION
- : (!isResumed
+ : (!isVisible
? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
: QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION));
}
@Nullable
- private Animator onLauncherResumedOrPaused(
- boolean isResumed, boolean fromInit, boolean startAnimation, int duration) {
+ private Animator onLauncherVisibilityChanged(
+ boolean isVisible, boolean fromInit, boolean startAnimation, int duration) {
// Launcher is resumed during the swipe-to-overview gesture under shell-transitions, so
// avoid updating taskbar state in that situation (when it's non-interactive -- or
// "background") to avoid premature animations.
- if (ENABLE_SHELL_TRANSITIONS && isResumed
+ if (ENABLE_SHELL_TRANSITIONS && isVisible
&& mLauncher.getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)
&& !mLauncher.getStateManager().getState().isTaskbarAlignedWithHotseat(mLauncher)) {
return null;
}
- mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed);
+ mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, isVisible);
return mTaskbarLauncherStateController.applyState(fromInit ? 0 : duration, startAnimation);
}
+ @Override
+ public void onStateTransitionCompletedAfterSwipeToHome(LauncherState state) {
+ mTaskbarLauncherStateController.onStateTransitionCompletedAfterSwipeToHome(state);
+ }
+
+ @Override
public void refreshResumedState() {
- onLauncherResumedOrPaused(mLauncher.hasBeenResumed());
+ onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
+ }
+
+ @Override
+ public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {
+ if (mLauncher.getHotseat() != null) {
+ mLauncher.getHotseat().adjustForBubbleBar(isBubbleBarVisible);
+ }
}
/**
@@ -279,8 +293,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
// Persistent features EDU tooltip.
if (!DisplayController.isTransientTaskbar(mLauncher)) {
- return !mLauncher.getOnboardingPrefs().hasReachedMaxCount(
- OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP);
+ return !OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.hasReachedMax(mLauncher);
}
// Transient swipe EDU tooltip.
@@ -327,10 +340,25 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
return mTaskbarInAppDisplayProgress.value > 0;
}
+ public boolean isBubbleBarEnabled() {
+ return BubbleBarController.isBubbleBarEnabled();
+ }
+
+ /** Whether the bubble bar has any bubbles. */
+ public boolean hasBubbles() {
+ if (mControllers == null) {
+ return false;
+ }
+ if (mControllers.bubbleControllers.isEmpty()) {
+ return false;
+ }
+ return mControllers.bubbleControllers.get().bubbleBarViewController.hasBubbles();
+ }
+
@Override
public void onExpandPip() {
super.onExpandPip();
- mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, false);
+ mTaskbarLauncherStateController.updateStateForFlag(FLAG_VISIBLE, false);
mTaskbarLauncherStateController.applyState();
}
@@ -362,8 +390,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
@Override
- public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
- mLauncher.launchSplitTasks(taskView, groupTask);
+ public void launchSplitTasks(@NonNull GroupTask groupTask) {
+ mLauncher.launchSplitTasks(groupTask);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index fcd8c806e4..bed4c376ae 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -23,8 +23,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static com.android.launcher3.LauncherAnimUtils.ROTATION_DRAWABLE_PERCENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.SYSUI_SURFACE_PROGRESS_INDEX;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
+import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
@@ -51,6 +53,7 @@ import android.animation.ObjectAnimator;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.LayoutRes;
+import android.content.Context;
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -78,6 +81,8 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
@@ -88,10 +93,10 @@ import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory;
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter;
import com.android.launcher3.util.DimensionUtils;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.views.BaseDragLayer;
import com.android.systemui.shared.rotation.FloatingRotationButton;
import com.android.systemui.shared.rotation.RotationButton;
@@ -144,6 +149,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
private int mState;
private final TaskbarActivityContext mContext;
+ private final @Nullable Context mNavigationBarPanelContext;
+ private final WindowManagerProxy mWindowManagerProxy;
private final FrameLayout mNavButtonsView;
private final LinearLayout mNavButtonContainer;
// Used for IME+A11Y buttons
@@ -190,6 +197,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
private MultiValueAlpha mBackButtonAlpha;
private MultiValueAlpha mHomeButtonAlpha;
private FloatingRotationButton mFloatingRotationButton;
+ private ImageView mImeSwitcherButton;
// Variables for moving nav buttons to a separate window above IME
private boolean mAreNavButtonsInSeparateWindow = false;
@@ -198,10 +206,12 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
this::onComputeInsetsForSeparateWindow;
private final RecentsHitboxExtender mHitboxExtender = new RecentsHitboxExtender();
private ImageView mRecentsButton;
- private DisplayController mDisplayController;
- public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
+ public NavbarButtonsViewController(TaskbarActivityContext context,
+ @Nullable Context navigationBarPanelContext, FrameLayout navButtonsView) {
mContext = context;
+ mNavigationBarPanelContext = navigationBarPanelContext;
+ mWindowManagerProxy = WindowManagerProxy.INSTANCE.get(mContext);
mNavButtonsView = navButtonsView;
mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
@@ -219,25 +229,27 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
*/
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ setupController();
+ }
+
+ protected void setupController() {
boolean isThreeButtonNav = mContext.isThreeButtonNav();
DeviceProfile deviceProfile = mContext.getDeviceProfile();
Resources resources = mContext.getResources();
Point p = !mContext.isUserSetupComplete()
- ? new Point(0, controllers.taskbarActivityContext.getSetupWindowHeight())
+ ? new Point(0, mControllers.taskbarActivityContext.getSetupWindowHeight())
: DimensionUtils.getTaskbarPhoneDimensions(deviceProfile, resources,
TaskbarManager.isPhoneMode(deviceProfile));
mNavButtonsView.getLayoutParams().height = p.y;
- mDisplayController = DisplayController.INSTANCE.get(mContext);
-
mIsImeRenderingNavButtons =
InputMethodService.canImeRenderGesturalNavButtons() && mContext.imeDrawsImeNavBar();
if (!mIsImeRenderingNavButtons) {
// IME switcher
- View imeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH,
+ mImeSwitcherButton = addButton(R.drawable.ic_ime_switcher, BUTTON_IME_SWITCH,
isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer,
mControllers.navButtonController, R.id.ime_switcher);
- mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
+ mPropertyHolders.add(new StatePropertyHolder(mImeSwitcherButton,
flags -> ((flags & FLAG_SWITCHER_SHOWING) != 0)
&& ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
}
@@ -306,7 +318,8 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
rotationButton.hide();
mControllers.rotationButtonController.setRotationButton(rotationButton, null);
} else {
- mFloatingRotationButton = new FloatingRotationButton(mContext,
+ mFloatingRotationButton = new FloatingRotationButton(
+ ENABLE_TASKBAR_NAVBAR_UNIFICATION ? mNavigationBarPanelContext : mContext,
R.string.accessibility_rotate_button,
R.layout.rotate_suggestion,
R.id.rotate_suggestion,
@@ -375,10 +388,12 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
int navButtonSize = mContext.getResources().getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_size);
boolean isRtl = Utilities.isRtl(mContext.getResources());
- mPropertyHolders.add(new StatePropertyHolder(
- mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0
- || (flags & FLAG_KEYGUARD_VISIBLE) != 0,
- VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0));
+ if (!isPhoneMode(mContext.getDeviceProfile())) {
+ mPropertyHolders.add(new StatePropertyHolder(
+ mBackButton, flags -> (flags & FLAG_ONLY_BACK_FOR_BOUNCER_VISIBLE) != 0
+ || (flags & FLAG_KEYGUARD_VISIBLE) != 0,
+ VIEW_TRANSLATE_X, navButtonSize * (isRtl ? -2 : 2), 0));
+ }
// home button
mHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
@@ -467,7 +482,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
/**
* @return {@code true} if A11y is showing in 3 button nav taskbar
*/
- private boolean isContextualButtonShowing() {
+ private boolean isA11yButtonPersistent() {
return mContext.isThreeButtonNav() && (mState & FLAG_A11Y_VISIBLE) != 0;
}
@@ -730,12 +745,15 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
// TODO(b/244231596) we're getting the incorrect kidsMode value in small-screen
boolean isInKidsMode = mContext.isNavBarKidsModeActive();
- if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
NavButtonLayoutter navButtonLayoutter =
NavButtonLayoutFactory.Companion.getUiLayoutter(
- dp, mNavButtonsView, res, isInKidsMode, isInSetup, isThreeButtonNav,
- TaskbarManager.isPhoneMode(dp), mDisplayController.getInfo().rotation);
- navButtonLayoutter.layoutButtons(dp, isContextualButtonShowing());
+ dp, mNavButtonsView, mImeSwitcherButton,
+ mControllers.rotationButtonController.getRotationButton(),
+ mA11yButton, res, isInKidsMode, isInSetup, isThreeButtonNav,
+ TaskbarManager.isPhoneMode(dp),
+ mWindowManagerProxy.getRotation(mContext));
+ navButtonLayoutter.layoutButtons(dp, isA11yButtonPersistent());
updateNavButtonColor();
return;
}
@@ -831,7 +849,7 @@ public class NavbarButtonsViewController implements TaskbarControllers.LoggableT
int contextualWidth = mEndContextualContainer.getWidth();
// If contextual buttons are showing, we check if the end margin is enough for the
// contextual button to be showing - if not, move the nav buttons over a smidge
- if (isContextualButtonShowing() && navMarginEnd < contextualWidth) {
+ if (isA11yButtonPersistent() && navMarginEnd < contextualWidth) {
// Additional spacing, eat up half of space between last icon and nav button
navMarginEnd += res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index c4255bf70f..da1f766420 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -108,7 +108,7 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
Resources resources = mActivity.getResources();
if (isPhoneGestureNavMode(mActivity.getDeviceProfile())) {
- mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_size);
+ mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_phone_size);
mStashedHandleWidth =
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
} else {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index b15dda2de1..4290948ad2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -20,17 +20,22 @@ import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_UNDEFINED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
+import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.Utilities.calculateTextHeight;
import static com.android.launcher3.Utilities.isRunningInTestHarness;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING;
import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_FULLSCREEN;
-import static com.android.launcher3.taskbar.TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW;
import static com.android.launcher3.testing.shared.ResourceUtils.getBoolByName;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
@@ -69,9 +74,11 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.folder.Folder;
@@ -101,6 +108,7 @@ import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.NavigationMode;
@@ -109,6 +117,7 @@ import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.views.RecentsView;
@@ -121,7 +130,9 @@ import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
/**
* The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
@@ -136,6 +147,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
private static final String WINDOW_TITLE = "Taskbar";
+ private final @Nullable Context mNavigationBarPanelContext;
+
private final TaskbarDragLayer mDragLayer;
private final TaskbarControllers mControllers;
@@ -167,13 +180,20 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
- public TaskbarActivityContext(Context windowContext, DeviceProfile launcherDp,
+ private DeviceProfile mTransientTaskbarDeviceProfile;
+
+ private DeviceProfile mPersistentTaskbarDeviceProfile;
+
+ private final LauncherPrefs mLauncherPrefs;
+
+ public TaskbarActivityContext(Context windowContext,
+ @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp,
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
unfoldTransitionProgressProvider) {
super(windowContext);
+ mNavigationBarPanelContext = navigationBarPanelContext;
applyDeviceProfile(launcherDp);
-
final Resources resources = getResources();
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false);
@@ -193,11 +213,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
Display display = windowContext.getDisplay();
Context c = getApplicationContext();
mWindowManager = c.getSystemService(WindowManager.class);
- mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
- mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
- // Inflate views.
boolean phoneMode = TaskbarManager.isPhoneMode(mDeviceProfile);
+ mLeftCorner = phoneMode
+ ? null
+ : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
+ mRightCorner = phoneMode
+ ? null
+ : display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
+
+ // Inflate views.
int taskbarLayout = DisplayController.isTransientTaskbar(this) && !phoneMode
? R.layout.transient_taskbar
: R.layout.taskbar;
@@ -215,7 +240,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
// If Bubble bar is present, TaskbarControllers depends on it so build it first.
Optional<BubbleControllers> bubbleControllersOptional = Optional.empty();
- if (BubbleBarController.BUBBLE_BAR_ENABLED && bubbleBarView != null) {
+ BubbleBarController.onTaskbarRecreated();
+ if (BubbleBarController.isBubbleBarEnabled() && bubbleBarView != null) {
bubbleControllersOptional = Optional.of(new BubbleControllers(
new BubbleBarController(this, bubbleBarView),
new BubbleBarViewController(this, bubbleBarView),
@@ -240,16 +266,18 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
new TaskbarDragController(this),
buttonController,
isDesktopMode
- ? new DesktopNavbarButtonsViewController(this, navButtonsView)
- : new NavbarButtonsViewController(this, navButtonsView),
+ ? new DesktopNavbarButtonsViewController(this, mNavigationBarPanelContext,
+ navButtonsView)
+ : new NavbarButtonsViewController(this, mNavigationBarPanelContext,
+ navButtonsView),
rotationButtonController,
new TaskbarDragLayerController(this, mDragLayer),
new TaskbarViewController(this, taskbarView),
new TaskbarScrimViewController(this, taskbarScrimView),
new TaskbarUnfoldAnimationController(this, unfoldTransitionProgressProvider,
- mWindowManager,
- new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
- getMainThreadHandler())),
+ mWindowManager,
+ new RotationChangeProvider(c.getSystemService(DisplayManager.class), this,
+ getMainThreadHandler())),
new TaskbarKeyguardController(this),
new StashedHandleViewController(this, stashedHandleView),
new TaskbarStashController(this),
@@ -267,8 +295,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
: TaskbarRecentAppsController.DEFAULT,
new TaskbarEduTooltipController(this),
new KeyboardQuickSwitchController(),
- new TaskbarDividerPopupController(this),
+ new TaskbarPinningController(this),
bubbleControllersOptional);
+
+ mLauncherPrefs = LauncherPrefs.get(this);
}
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
@@ -288,20 +318,42 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
* the icon size
*/
private void applyDeviceProfile(DeviceProfile originDeviceProfile) {
+ Consumer<DeviceProfile> overrideProvider = deviceProfile -> {
+ // Taskbar should match the number of icons of hotseat
+ deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
+ // Same QSB width to have a smooth animation
+ deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
+
+ // Update icon size
+ deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
+ deviceProfile.updateIconSize(1f, getResources());
+ };
mDeviceProfile = originDeviceProfile.toBuilder(this)
- .withDimensionsOverride(deviceProfile -> {
- // Taskbar should match the number of icons of hotseat
- deviceProfile.numShownHotseatIcons = originDeviceProfile.numShownHotseatIcons;
- // Same QSB width to have a smooth animation
- deviceProfile.hotseatQsbWidth = originDeviceProfile.hotseatQsbWidth;
-
- // Update icon size
- deviceProfile.iconSizePx = deviceProfile.taskbarIconSize;
- deviceProfile.updateIconSize(1f, getResources());
- }).build();
+ .withDimensionsOverride(overrideProvider).build();
+
+ if (DisplayController.isTransientTaskbar(this)) {
+ mTransientTaskbarDeviceProfile = mDeviceProfile;
+ mPersistentTaskbarDeviceProfile = mDeviceProfile
+ .toBuilder(this)
+ .withDimensionsOverride(overrideProvider)
+ .setIsTransientTaskbar(false)
+ .build();
+ } else {
+ mPersistentTaskbarDeviceProfile = mDeviceProfile;
+ mTransientTaskbarDeviceProfile = mDeviceProfile
+ .toBuilder(this)
+ .withDimensionsOverride(overrideProvider)
+ .setIsTransientTaskbar(true)
+ .build();
+ }
mNavMode = DisplayController.getNavigationMode(this);
}
+ /** Called when the visibility of the bubble bar changed. */
+ public void bubbleBarVisibilityChanged(boolean isVisible) {
+ mControllers.uiController.adjustHotseatForBubbleBar(isVisible);
+ mControllers.taskbarViewController.resetIconAlignmentController();
+ }
public void init(@NonNull TaskbarSharedState sharedState) {
mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, getResources(), false);
@@ -320,18 +372,18 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
sharedState.systemBarAttrsBehavior);
onNavButtonsDarkIntensityChanged(sharedState.navButtonsDarkIntensity);
- if (FLAG_HIDE_NAVBAR_WINDOW) {
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
// W/ the flag not set this entire class gets re-created, which resets the value of
// mIsDestroyed. We re-use the class for small-screen, so we explicitly have to mark
// this class as non-destroyed
mIsDestroyed = false;
}
- if (!mAddedWindow) {
+ if (!enableTaskbarNoRecreate() && !mAddedWindow) {
mWindowManager.addView(mDragLayer, mWindowLayoutParams);
mAddedWindow = true;
} else {
- mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ notifyUpdateLayoutParams();
}
}
@@ -347,6 +399,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mControllers.taskbarAllAppsController.toggle();
}
+ /** Toggles Taskbar All Apps overlay with keyboard ready for search. */
+ public void toggleAllAppsSearch() {
+ mControllers.taskbarAllAppsController.toggleSearch();
+ }
+
@Override
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
@@ -359,6 +416,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
getDeviceProfile().toSmallString());
}
+ @NonNull
+ public LauncherPrefs getLauncherPrefs() {
+ return mLauncherPrefs;
+ }
+
/**
* Returns the View bounds of transient taskbar.
*/
@@ -374,7 +436,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
/**
* Creates LayoutParams for adding a view directly to WindowManager as a new window.
- * @param type The window type to pass to the created WindowManager.LayoutParams.
+ *
+ * @param type The window type to pass to the created WindowManager.LayoutParams.
* @param title The window title to pass to the created WindowManager.LayoutParams.
*/
public WindowManager.LayoutParams createDefaultWindowLayoutParams(int type, String title) {
@@ -413,9 +476,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
* for taskbar showing as navigation bar
*/
private WindowManager.LayoutParams createAllWindowParams() {
+ final int windowType =
+ ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
WindowManager.LayoutParams windowLayoutParams =
- createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL,
- TaskbarActivityContext.WINDOW_TITLE);
+ createDefaultWindowLayoutParams(windowType, TaskbarActivityContext.WINDOW_TITLE);
boolean isPhoneNavMode = TaskbarManager.isPhoneButtonNavMode(this);
if (!isPhoneNavMode) {
return windowLayoutParams;
@@ -428,7 +492,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4];
for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
WindowManager.LayoutParams lp =
- createDefaultWindowLayoutParams(TYPE_NAVIGATION_BAR_PANEL,
+ createDefaultWindowLayoutParams(windowType,
TaskbarActivityContext.WINDOW_TITLE);
switch (rot) {
case Surface.ROTATION_0, Surface.ROTATION_180 -> {
@@ -678,7 +742,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mIsDestroyed = true;
setUIController(TaskbarUIController.DEFAULT);
mControllers.onDestroy();
- if (!FLAG_HIDE_NAVBAR_WINDOW) {
+ if (!enableTaskbarNoRecreate() && !ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
mWindowManager.removeViewImmediate(mDragLayer);
mAddedWindow = false;
}
@@ -777,7 +841,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
// Overlay AFVs are in a separate window and do not require Taskbar to be fullscreen.
if (!isDragInProgress
&& !AbstractFloatingView.hasOpenView(
- this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
+ this, TYPE_ALL & ~TYPE_TASKBAR_OVERLAY_PROXY)) {
// Reverts Taskbar window to its original size
setTaskbarWindowFullscreen(false);
}
@@ -810,7 +874,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
}
mWindowLayoutParams.height = height;
mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
- mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ notifyUpdateLayoutParams();
}
/**
@@ -819,9 +883,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
public int getDefaultTaskbarWindowHeight() {
Resources resources = getResources();
- if (FLAG_HIDE_NAVBAR_WINDOW && mDeviceProfile.isPhone) {
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION && mDeviceProfile.isPhone) {
return isThreeButtonNav() ?
- resources.getDimensionPixelSize(R.dimen.taskbar_size) :
+ resources.getDimensionPixelSize(R.dimen.taskbar_phone_size) :
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
}
@@ -829,20 +893,46 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
return getSetupWindowHeight();
}
- if (DisplayController.isTransientTaskbar(this)) {
- return mDeviceProfile.taskbarHeight
- + (2 * mDeviceProfile.taskbarBottomMargin)
- + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur);
+ boolean shouldTreatAsTransient = DisplayController.isTransientTaskbar(this)
+ || (enableTaskbarPinning() && !isThreeButtonNav());
+
+ int extraHeightForTaskbarTooltips = enableCursorHoverStates()
+ ? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
+ + (resources.getDimensionPixelSize(R.dimen.taskbar_tooltip_vertical_padding) * 2)
+ + calculateTextHeight(
+ resources.getDimensionPixelSize(R.dimen.arrow_toast_text_size))
+ : 0;
+
+ // Return transient taskbar window height when pinning feature is enabled, so taskbar view
+ // does not get cut off during pinning animation.
+ if (shouldTreatAsTransient) {
+ DeviceProfile transientTaskbarDp = mDeviceProfile.toBuilder(this)
+ .setIsTransientTaskbar(true).build();
+
+ return transientTaskbarDp.taskbarHeight
+ + (2 * transientTaskbarDp.taskbarBottomMargin)
+ + Math.max(extraHeightForTaskbarTooltips, resources.getDimensionPixelSize(
+ R.dimen.transient_taskbar_shadow_blur));
}
+
return mDeviceProfile.taskbarHeight
- + Math.max(getLeftCornerRadius(), getRightCornerRadius());
+ + Math.max(getLeftCornerRadius(), getRightCornerRadius())
+ + extraHeightForTaskbarTooltips;
}
public int getSetupWindowHeight() {
return getResources().getDimensionPixelSize(R.dimen.taskbar_suw_frame);
}
+ public DeviceProfile getTransientTaskbarDeviceProfile() {
+ return mTransientTaskbarDeviceProfile;
+ }
+
+ public DeviceProfile getPersistentTaskbarDeviceProfile() {
+ return mPersistentTaskbarDeviceProfile;
+ }
+
/**
* Either adds or removes {@link WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} on the taskbar
* window.
@@ -853,7 +943,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
} else {
mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
}
- mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ notifyUpdateLayoutParams();
}
/**
@@ -904,6 +994,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
}
protected void onTaskbarIconClicked(View view) {
+ TaskbarUIController taskbarUIController = mControllers.uiController;
+ RecentsView recents = taskbarUIController.getRecentsView();
boolean shouldCloseAllOpenViews = true;
Object tag = view.getTag();
if (tag instanceof Task) {
@@ -911,41 +1003,26 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
ActivityOptions.makeBasic());
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
- } else if (tag instanceof FolderInfo) {
+ } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_FOLDER) {
+ // Tapping an expandable folder icon on Taskbar
shouldCloseAllOpenViews = false;
- FolderIcon folderIcon = (FolderIcon) view;
- Folder folder = folderIcon.getFolder();
-
- folder.setOnFolderStateChangedListener(newState -> {
- if (newState == Folder.STATE_OPEN) {
- setTaskbarWindowFocusableForIme(true);
- } else if (newState == Folder.STATE_CLOSED) {
- // Defer by a frame to ensure we're no longer fullscreen and thus won't jump.
- getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false));
- folder.setOnFolderStateChangedListener(null);
- }
- });
-
- setTaskbarWindowFullscreen(true);
-
- getDragLayer().post(() -> {
- folder.animateOpen();
- getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
-
- folder.iterateOverItems((itemInfo, itemView) -> {
- mControllers.taskbarViewController
- .setClickAndLongClickListenersForIcon(itemView);
- // To play haptic when dragging, like other Taskbar items do.
- itemView.setHapticFeedbackEnabled(true);
- return false;
- });
- });
+ expandFolder((FolderIcon) view);
+ } else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_APP_PAIR) {
+ // Tapping an app pair icon on Taskbar
+ if (recents != null && recents.isSplitSelectionActive()) {
+ // TODO (b/274835596): Implement "can't split with this" bounce animation
+ Toast.makeText(this, "Unable to split with an app pair. Select another app.",
+ Toast.LENGTH_SHORT).show();
+ } else {
+ // Else launch the selected app pair
+ launchFromTaskbarPreservingSplitIfVisible(recents, view, fi.contents);
+ mControllers.uiController.onTaskbarIconLaunched(fi);
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+ }
} else if (tag instanceof WorkspaceItemInfo) {
// Tapping a launchable icon on Taskbar
WorkspaceItemInfo info = (WorkspaceItemInfo) tag;
if (!info.isDisabled() || !ItemClickHandler.handleDisabledItemClicked(info, this)) {
- TaskbarUIController taskbarUIController = mControllers.uiController;
- RecentsView recents = taskbarUIController.getRecentsView();
if (recents != null && recents.isSplitSelectionActive()) {
// If we are selecting a second app for split, launch the split tasks
taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
@@ -973,7 +1050,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
getSystemService(LauncherApps.class)
.startShortcut(packageName, id, null, null, info.user);
} else {
- launchFromTaskbarPreservingSplitIfVisible(recents, info);
+ launchFromTaskbarPreservingSplitIfVisible(
+ recents, view, Collections.singletonList(info));
}
} catch (NullPointerException
@@ -984,7 +1062,21 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
Log.e(TAG, "Unable to launch. tag=" + info + " intent=" + intent, e);
return;
}
+ }
+ // If the app was launched from a folder, stash the taskbar after it closes
+ Folder f = Folder.getOpen(this);
+ if (f != null && f.getInfo().id == info.container) {
+ f.addOnFolderStateChangedListener(new Folder.OnFolderStateChangedListener() {
+ @Override
+ public void onFolderStateChanged(int newState) {
+ if (newState == Folder.STATE_CLOSED) {
+ f.removeOnFolderStateChangedListener(this);
+ mControllers.taskbarStashController
+ .updateAndAnimateTransientTaskbar(true);
+ }
+ }
+ });
}
mControllers.uiController.onTaskbarIconLaunched(info);
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
@@ -992,14 +1084,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
} else if (tag instanceof AppInfo) {
// Tapping an item in AllApps
AppInfo info = (AppInfo) tag;
- TaskbarUIController taskbarUIController = mControllers.uiController;
- RecentsView recents = taskbarUIController.getRecentsView();
if (recents != null
&& taskbarUIController.getRecentsView().isSplitSelectionActive()) {
// If we are selecting a second app for split, launch the split tasks
taskbarUIController.triggerSecondAppForSplit(info, info.intent, view);
} else {
- launchFromTaskbarPreservingSplitIfVisible(recents, info);
+ launchFromTaskbarPreservingSplitIfVisible(
+ recents, view, Collections.singletonList(info));
}
mControllers.uiController.onTaskbarIconLaunched(info);
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
@@ -1021,17 +1112,22 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
* (potentially breaking a split pair).
*/
private void launchFromTaskbarPreservingSplitIfVisible(@Nullable RecentsView recents,
- ItemInfo info) {
+ @Nullable View launchingIconView, List<? extends ItemInfo> itemInfos) {
if (recents == null) {
return;
}
+
+ boolean findExactPairMatch = itemInfos.size() == 2;
+ // Convert the list of ItemInfo instances to a list of ComponentKeys
+ List<ComponentKey> componentKeys =
+ itemInfos.stream().map(ItemInfo::getComponentKey).toList();
recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
- Collections.singletonList(info.getComponentKey()),
+ componentKeys,
+ findExactPairMatch,
foundTasks -> {
@Nullable Task foundTask = foundTasks.get(0);
if (foundTask != null) {
- TaskView foundTaskView =
- recents.getTaskViewByTaskId(foundTask.key.id);
+ TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
if (foundTaskView != null
&& foundTaskView.isVisibleToUser()) {
TestLogging.recordEvent(
@@ -1040,8 +1136,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
return;
}
}
- startItemInfoActivity(info);
- });
+
+ if (findExactPairMatch) {
+ // We did not find the app pair we were looking for, so launch one.
+ recents.getSplitSelectController().getAppPairsController().launchAppPair(
+ (AppPairIcon) launchingIconView);
+ } else {
+ startItemInfoActivity(itemInfos.get(0));
+ }
+ }
+ );
}
private void startItemInfoActivity(ItemInfo info) {
@@ -1063,6 +1167,41 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
}
}
+ /** Expands a folder icon when it is clicked */
+ private void expandFolder(FolderIcon folderIcon) {
+ Folder folder = folderIcon.getFolder();
+
+ folder.setPriorityOnFolderStateChangedListener(
+ new Folder.OnFolderStateChangedListener() {
+ @Override
+ public void onFolderStateChanged(int newState) {
+ if (newState == Folder.STATE_OPEN) {
+ setTaskbarWindowFocusableForIme(true);
+ } else if (newState == Folder.STATE_CLOSED) {
+ // Defer by a frame to ensure we're no longer fullscreen and thus
+ // won't jump.
+ getDragLayer().post(() -> setTaskbarWindowFocusableForIme(false));
+ folder.setPriorityOnFolderStateChangedListener(null);
+ }
+ }
+ });
+
+ setTaskbarWindowFullscreen(true);
+
+ getDragLayer().post(() -> {
+ folder.animateOpen();
+ getStatsLogManager().logger().withItemInfo(folder.mInfo).log(LAUNCHER_FOLDER_OPEN);
+
+ folder.iterateOverItems((itemInfo, itemView) -> {
+ mControllers.taskbarViewController
+ .setClickAndLongClickListenersForIcon(itemView);
+ // To play haptic when dragging, like other Taskbar items do.
+ itemView.setHapticFeedbackEnabled(true);
+ return false;
+ });
+ });
+ }
+
/**
* Returns whether the taskbar is currently visually stashed.
*/
@@ -1071,18 +1210,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
}
/**
- * Called when we detect a long press in the nav region before passing the gesture slop.
- *
- * @return Whether taskbar handled the long press, and thus should cancel the gesture.
- */
- public boolean onLongPressToUnstashTaskbar() {
- return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
- }
-
- /**
* Called when we want to unstash taskbar when user performs swipes up gesture.
*/
public void onSwipeToUnstashTaskbar() {
+ VibratorWrapper.INSTANCE.get(this).vibrateForTaskbarUnstash();
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
mControllers.taskbarEduTooltipController.hide();
}
@@ -1133,28 +1264,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
* @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
*/
public void startTaskbarUnstashHint(boolean animateForward) {
- // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code.
- startTaskbarUnstashHint(animateForward, /* forceUnstash = */ false);
- }
-
- /**
- * Called when we detect a motion down or up/cancel in the nav region while stashed.
- *
- * @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
- * @param forceUnstash Whether we force the unstash hint.
- */
- public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) {
- // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code.
- mControllers.taskbarStashController.startUnstashHint(animateForward, forceUnstash);
- }
-
- /**
- * Enables manual taskbar stashing. This method should only be used for tests that need to
- * stash/unstash the taskbar.
- */
- @VisibleForTesting
- public void enableManualStashingDuringTests(boolean enableManualStashing) {
- mControllers.taskbarStashController.enableManualStashingDuringTests(enableManualStashing);
+ mControllers.taskbarStashController.startUnstashHint(animateForward);
}
/**
@@ -1167,15 +1277,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
}
/**
- * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
- * taskbar at the end of a test.
+ * Unstashes the Taskbar if it is stashed.
*/
@VisibleForTesting
public void unstashTaskbarIfStashed() {
if (DisplayController.isTransientTaskbar(this)) {
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
- } else {
- mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
}
}
@@ -1212,7 +1319,7 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
TaskbarUIController uiController = mControllers.uiController;
if (uiController instanceof LauncherTaskbarUIController) {
- ((LauncherTaskbarUIController) uiController).addLauncherResumeAnimation(
+ ((LauncherTaskbarUIController) uiController).addLauncherVisibilityChangedAnimation(
fullAnimation, duration);
}
mControllers.taskbarStashController.addUnstashToHotseatAnimation(fullAnimation, duration);
@@ -1249,12 +1356,16 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
mWindowLayoutParams.privateFlags &=
~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
}
- mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ notifyUpdateLayoutParams();
}
void notifyUpdateLayoutParams() {
if (mDragLayer.isAttachedToWindow()) {
- mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ if (enableTaskbarNoRecreate()) {
+ mWindowManager.updateViewLayout(mDragLayer.getRootView(), mWindowLayoutParams);
+ } else {
+ mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index d237c1f997..d6016f1304 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -29,26 +29,39 @@ import com.android.launcher3.Utilities
import com.android.launcher3.Utilities.mapRange
import com.android.launcher3.Utilities.mapToRange
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
+import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT
+import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT
import com.android.launcher3.util.DisplayController
+import kotlin.math.min
/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
-class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
+class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) {
private val isInSetup: Boolean = !context.isUserSetupComplete
private val DARK_THEME_SHADOW_ALPHA = 51f
private val LIGHT_THEME_SHADOW_ALPHA = 25f
+ private val maxTransientTaskbarHeight =
+ context.transientTaskbarDeviceProfile.taskbarHeight.toFloat()
+ private val maxPersistentTaskbarHeight =
+ context.persistentTaskbarDeviceProfile.taskbarHeight.toFloat()
+ var backgroundProgress =
+ if (DisplayController.isTransientTaskbar(context)) {
+ PINNING_TRANSIENT
+ } else {
+ PINNING_PERSISTENT
+ }
+
+ var isAnimatingPinning = false
+
val paint = Paint()
val lastDrawnTransientRect = RectF()
var backgroundHeight = context.deviceProfile.taskbarHeight.toFloat()
var translationYForSwipe = 0f
var translationYForStash = 0f
- private var maxBackgroundHeight = context.deviceProfile.taskbarHeight.toFloat()
private val transientBackgroundBounds = context.transientTaskbarBounds
- private val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
-
private val shadowAlpha: Float
private var shadowBlur = 0f
private var keyShadowDistance = 0f
@@ -75,13 +88,6 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
paint.flags = Paint.ANTI_ALIAS_FLAG
paint.style = Paint.Style.FILL
- if (isTransientTaskbar) {
- val res = context.resources
- bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)
- shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
- keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
- }
-
shadowAlpha =
if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA
else LIGHT_THEME_SHADOW_ALPHA
@@ -90,10 +96,11 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
}
fun updateStashedHandleWidth(dp: DeviceProfile, res: Resources) {
- stashedHandleWidth = res.getDimensionPixelSize(
+ stashedHandleWidth =
+ res.getDimensionPixelSize(
if (TaskbarManager.isPhoneMode(dp)) R.dimen.taskbar_stashed_small_screen
else R.dimen.taskbar_stashed_handle_width
- )
+ )
}
/**
@@ -102,7 +109,7 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
* @param cornerRoundness 0 has no round corner, 1 has complete round corner.
*/
fun setCornerRoundness(cornerRoundness: Float) {
- if (isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
+ if (DisplayController.isTransientTaskbar(context) && !transientBackgroundBounds.isEmpty) {
return
}
@@ -126,63 +133,123 @@ class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
/** Draws the background with the given paint and height, on the provided canvas. */
fun draw(canvas: Canvas) {
+ if (isInSetup) return
+ val isTransientTaskbar = backgroundProgress == 0f
canvas.save()
- if (!isTransientTaskbar || transientBackgroundBounds.isEmpty) {
- canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
+ if (!isTransientTaskbar || transientBackgroundBounds.isEmpty || isAnimatingPinning) {
+ drawPersistentBackground(canvas)
+ }
+ canvas.restore()
+ canvas.save()
+ if (isAnimatingPinning || isTransientTaskbar) {
+ drawTransientBackground(canvas)
+ }
+ canvas.restore()
+ }
+
+ private fun drawPersistentBackground(canvas: Canvas) {
+ if (isAnimatingPinning) {
+ val persistentTaskbarHeight = maxPersistentTaskbarHeight * backgroundProgress
+ canvas.translate(0f, canvas.height - persistentTaskbarHeight)
// Draw the background behind taskbar content.
- canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
-
- // Draw the inverted rounded corners above the taskbar.
- canvas.translate(0f, -leftCornerRadius)
- canvas.drawPath(invertedLeftCornerPath, paint)
- canvas.translate(0f, leftCornerRadius)
- canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
- canvas.drawPath(invertedRightCornerPath, paint)
- } else if (!isInSetup) {
- // backgroundHeight is a value from [0...maxBackgroundHeight], so we can use it as a
- // proxy to figure out the animation progress of the stash/unstash animation.
- val progress = backgroundHeight / maxBackgroundHeight
-
- // At progress 0, we draw the background as the stashed handle.
- // At progress 1, we draw the background as the full taskbar.
- val newBackgroundHeight =
- mapRange(progress, stashedHandleHeight.toFloat(), maxBackgroundHeight)
- val fullWidth = transientBackgroundBounds.width()
- val newWidth = mapRange(progress, stashedHandleWidth.toFloat(), fullWidth.toFloat())
- val halfWidthDelta = (fullWidth - newWidth) / 2f
- val radius = newBackgroundHeight / 2f
- val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
-
- // Aligns the bottom with the bottom of the stashed handle.
- val bottom =
- canvas.height - bottomMargin +
- bottomMarginProgress +
- translationYForSwipe +
- translationYForStash +
- -mapRange(1f - progress, 0f, stashedHandleHeight / 2f)
-
- // Draw shadow.
- val newShadowAlpha =
- mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR)
- paint.setShadowLayer(
- shadowBlur,
- 0f,
- keyShadowDistance,
- setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha))
- )
+ canvas.drawRect(0f, 0f, canvas.width.toFloat(), persistentTaskbarHeight, paint)
+ } else {
+ val persistentTaskbarHeight = min(maxPersistentTaskbarHeight, backgroundHeight)
+ canvas.translate(0f, canvas.height - persistentTaskbarHeight)
+ // Draw the background behind taskbar content.
+ canvas.drawRect(0f, 0f, canvas.width.toFloat(), persistentTaskbarHeight, paint)
+ }
- lastDrawnTransientRect.set(
- transientBackgroundBounds.left + halfWidthDelta,
- bottom - newBackgroundHeight,
- transientBackgroundBounds.right - halfWidthDelta,
- bottom
- )
- val horizontalInset = fullWidth * widthInsetPercentage
- lastDrawnTransientRect.inset(horizontalInset, 0f)
+ // Draw the inverted rounded corners above the taskbar.
+ canvas.translate(0f, -leftCornerRadius)
+ canvas.drawPath(invertedLeftCornerPath, paint)
+ canvas.translate(0f, leftCornerRadius)
+ canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
+ canvas.drawPath(invertedRightCornerPath, paint)
+ }
+
+ private fun drawTransientBackground(canvas: Canvas) {
+ val res = context.resources
+ val transientTaskbarHeight = maxTransientTaskbarHeight * (1f - backgroundProgress)
+ val heightProgressWhileAnimating =
+ if (isAnimatingPinning) transientTaskbarHeight else backgroundHeight
- canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, paint)
+ var progress = heightProgressWhileAnimating / maxTransientTaskbarHeight
+ progress = Math.round(progress * 100f) / 100f
+ if (isAnimatingPinning) {
+ var scale = transientTaskbarHeight / maxTransientTaskbarHeight
+ scale = Math.round(scale * 100f) / 100f
+ bottomMargin =
+ mapRange(
+ scale,
+ 0f,
+ res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin).toFloat()
+ )
+ .toInt()
+ shadowBlur =
+ mapRange(scale, 0f, res.getDimension(R.dimen.transient_taskbar_shadow_blur))
+ keyShadowDistance =
+ mapRange(scale, 0f, res.getDimension(R.dimen.transient_taskbar_key_shadow_distance))
+ } else {
+ bottomMargin = res.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)
+ shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur)
+ keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance)
}
- canvas.restore()
+
+ // At progress 0, we draw the background as the stashed handle.
+ // At progress 1, we draw the background as the full taskbar.
+ // Min height capped to max persistent taskbar height for animation
+ val backgroundHeightWhileAnimating =
+ if (isAnimatingPinning) maxPersistentTaskbarHeight else stashedHandleHeight.toFloat()
+ val newBackgroundHeight =
+ mapRange(progress, backgroundHeightWhileAnimating, maxTransientTaskbarHeight)
+ val fullWidth = transientBackgroundBounds.width()
+
+ // .9f is here to restrict min width of the background while animating, so transient
+ // background keeps it pill shape until animation end.
+ val animationWidth =
+ if (DisplayController.isTransientTaskbar(context)) fullWidth.toFloat() * .9f
+ else fullWidth.toFloat()
+ val backgroundWidthWhileAnimating =
+ if (isAnimatingPinning) animationWidth else stashedHandleWidth.toFloat()
+
+ val newWidth = mapRange(progress, backgroundWidthWhileAnimating, fullWidth.toFloat())
+ val halfWidthDelta = (fullWidth - newWidth) / 2f
+ val radius = newBackgroundHeight / 2f
+ val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f)
+
+ // Aligns the bottom with the bottom of the stashed handle.
+ val bottom =
+ canvas.height - bottomMargin +
+ bottomMarginProgress +
+ translationYForSwipe +
+ translationYForStash +
+ -mapRange(
+ 1f - progress,
+ 0f,
+ if (isAnimatingPinning) 0f else stashedHandleHeight / 2f
+ )
+
+ // Draw shadow.
+ val newShadowAlpha =
+ mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR)
+ paint.setShadowLayer(
+ shadowBlur,
+ 0f,
+ keyShadowDistance,
+ setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha))
+ )
+
+ lastDrawnTransientRect.set(
+ transientBackgroundBounds.left + halfWidthDelta,
+ bottom - newBackgroundHeight,
+ transientBackgroundBounds.right - halfWidthDelta,
+ bottom
+ )
+ val horizontalInset = fullWidth * widthInsetPercentage
+ lastDrawnTransientRect.inset(horizontalInset, 0f)
+
+ canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, paint)
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index d82f50116d..f9ddc3db2f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -62,7 +62,7 @@ public class TaskbarControllers {
public final TaskbarOverlayController taskbarOverlayController;
public final TaskbarEduTooltipController taskbarEduTooltipController;
public final KeyboardQuickSwitchController keyboardQuickSwitchController;
- public final TaskbarDividerPopupController taskbarPinningController;
+ public final TaskbarPinningController taskbarPinningController;
public final Optional<BubbleControllers> bubbleControllers;
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
@@ -110,7 +110,7 @@ public class TaskbarControllers {
TaskbarRecentAppsController taskbarRecentAppsController,
TaskbarEduTooltipController taskbarEduTooltipController,
KeyboardQuickSwitchController keyboardQuickSwitchController,
- TaskbarDividerPopupController taskbarPinningController,
+ TaskbarPinningController taskbarPinningController,
Optional<BubbleControllers> bubbleControllers) {
this.taskbarActivityContext = taskbarActivityContext;
this.taskbarDragController = taskbarDragController;
@@ -171,7 +171,7 @@ public class TaskbarControllers {
taskbarTranslationController.init(this);
taskbarEduTooltipController.init(this);
keyboardQuickSwitchController.init(this);
- taskbarPinningController.init(this);
+ taskbarPinningController.init(this, mSharedState);
bubbleControllers.ifPresent(controllers -> controllers.init(this));
mControllersToLog = new LoggableTaskbarController[] {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
deleted file mode 100644
index 83a33435a8..0000000000
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupController.kt
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.taskbar
-
-import android.view.View
-import com.android.launcher3.LauncherPrefs
-import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
-import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
-import java.io.PrintWriter
-
-/** Controls taskbar pinning through a popup view. */
-class TaskbarDividerPopupController(private val context: TaskbarActivityContext) :
- TaskbarControllers.LoggableTaskbarController {
-
- private lateinit var controllers: TaskbarControllers
- private val launcherPrefs = LauncherPrefs.get(context)
-
- fun init(taskbarControllers: TaskbarControllers) {
- controllers = taskbarControllers
- }
-
- fun showPinningView(view: View) {
- context.isTaskbarWindowFullscreen = true
-
- view.post {
- val popupView = createAndPopulate(view, context)
- popupView.requestFocus()
-
- popupView.onCloseCallback =
- callback@{ didPreferenceChange ->
- context.dragLayer.post { context.onPopupVisibilityChanged(false) }
-
- if (!didPreferenceChange) {
- return@callback
- }
-
- if (launcherPrefs.get(TASKBAR_PINNING)) {
- animateTransientToPersistentTaskbar()
- } else {
- animatePersistentToTransientTaskbar()
- }
- }
- popupView.changePreference = {
- launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
- }
- context.onPopupVisibilityChanged(true)
- popupView.show()
- }
- }
-
- // TODO(b/265436799): provide animation/transition from transient taskbar to persistent one
- private fun animateTransientToPersistentTaskbar() {}
-
- // TODO(b/265436799): provide animation/transition from persistent taskbar to transient one
- private fun animatePersistentToTransientTaskbar() {}
-
- override fun dumpLogs(prefix: String, pw: PrintWriter) {
- pw.println(prefix + "TaskbarPinningController:")
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index e215bc9d46..3f9b66a717 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -15,22 +15,29 @@
*/
package com.android.launcher3.taskbar
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.GradientDrawable
import android.util.AttributeSet
+import android.util.Property
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.widget.LinearLayout
import android.widget.Switch
import androidx.core.view.postDelayed
+import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.launcher3.R
import com.android.launcher3.popup.ArrowPopup
import com.android.launcher3.popup.RoundedArrowDrawable
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Themes
+import com.android.launcher3.views.ActivityContext
/** Popup view with arrow for taskbar pinning */
class TaskbarDividerPopupView<T : TaskbarActivityContext>
@@ -42,7 +49,8 @@ constructor(
) : ArrowPopup<T>(context, attrs, defStyleAttr) {
companion object {
private const val TAG = "TaskbarDividerPopupView"
- private const val DIVIDER_POPUP_CLOSING_DELAY = 500L
+ private const val DIVIDER_POPUP_CLOSING_DELAY = 333L
+ private const val DIVIDER_POPUP_CLOSING_ANIMATION_DURATION = 83L
@JvmStatic
fun createAndPopulate(
@@ -59,10 +67,11 @@ constructor(
return taskMenuViewWithArrow.populateForView(view)
}
}
+
private lateinit var dividerView: View
private val menuWidth =
- context.resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_width)
+ resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_width)
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height)
@@ -70,16 +79,12 @@ constructor(
private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
private var didPreferenceChange = false
+ private var verticalOffsetForPopupView =
+ resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_vertical_margin)
/** Callback invoked when the pinning popup view is closing. */
var onCloseCallback: (preferenceChanged: Boolean) -> Unit = {}
- /**
- * Callback invoked when the user preference changes in popup view. Preference change will be
- * based upon current value stored in [LauncherPrefs] for `TASKBAR_PINNING`
- */
- var changePreference: () -> Unit = {}
-
init {
// This synchronizes the arrow and menu to open at the same time
mOpenChildFadeStartDelay = mOpenFadeStartDelay
@@ -99,11 +104,22 @@ constructor(
super.onFinishInflate()
val taskbarSwitchOption = requireViewById<LinearLayout>(R.id.taskbar_switch_option)
val alwaysShowTaskbarSwitch = requireViewById<Switch>(R.id.taskbar_pinning_switch)
+ val taskbarVisibilityIcon = requireViewById<View>(R.id.taskbar_pinning_visibility_icon)
alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn
- taskbarSwitchOption.setOnClickListener {
- alwaysShowTaskbarSwitch.isClickable = true
- alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
- onClickAlwaysShowTaskbarSwitchOption()
+ if (ActivityContext.lookupContext<TaskbarActivityContext>(context).isGestureNav) {
+ taskbarSwitchOption.setOnClickListener {
+ alwaysShowTaskbarSwitch.isClickable = true
+ alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
+ onClickAlwaysShowTaskbarSwitchOption()
+ }
+ } else {
+ alwaysShowTaskbarSwitch.isEnabled = false
+ }
+
+ if (!alwaysShowTaskbarSwitch.isEnabled) {
+ taskbarVisibilityIcon.background.setTint(
+ resources.getColor(android.R.color.system_neutral2_500, context.theme)
+ )
}
}
@@ -176,15 +192,80 @@ constructor(
}
}
- override fun closeComplete() {
+ override fun getExtraVerticalOffset(): Int {
+ return (mActivityContext.deviceProfile.taskbarHeight -
+ mActivityContext.deviceProfile.taskbarIconSize) / 2 + verticalOffsetForPopupView
+ }
+
+ override fun animateClose() {
+ if (!mIsOpen) {
+ return
+ }
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel()
+ }
+ mIsOpen = false
+
+ mOpenCloseAnimator = getCloseAnimator()
+
+ mOpenCloseAnimator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ mOpenCloseAnimator = null
+ if (mDeferContainerRemoval) {
+ setVisibility(INVISIBLE)
+ } else {
+ closeComplete()
+ }
+ }
+ }
+ )
onCloseCallback(didPreferenceChange)
- super.closeComplete()
+ onCloseCallback = {}
+ mOpenCloseAnimator.start()
+ }
+
+ private fun getCloseAnimator(): AnimatorSet {
+ val alphaValues = floatArrayOf(1f, 0f)
+ val translateYValue =
+ if (!alwaysShowTaskbarOn) verticalOffsetForPopupView else -verticalOffsetForPopupView
+ val alpha = getAnimatorOfFloat(this, ALPHA, *alphaValues)
+ val arrowAlpha = getAnimatorOfFloat(mArrow, ALPHA, *alphaValues)
+ val translateY =
+ ObjectAnimator.ofFloat(
+ this,
+ TRANSLATION_Y,
+ *floatArrayOf(this.translationY, this.translationY + translateYValue)
+ )
+ val arrowTranslateY =
+ ObjectAnimator.ofFloat(
+ mArrow,
+ TRANSLATION_Y,
+ *floatArrayOf(mArrow.translationY, mArrow.translationY + translateYValue)
+ )
+ val animatorSet = AnimatorSet()
+ animatorSet.playTogether(alpha, arrowAlpha, translateY, arrowTranslateY)
+ return animatorSet
+ }
+
+ private fun getAnimatorOfFloat(
+ view: View,
+ property: Property<View, Float>,
+ vararg values: Float
+ ): Animator {
+ val animator: Animator = ObjectAnimator.ofFloat(view, property, *values)
+ animator.setDuration(DIVIDER_POPUP_CLOSING_ANIMATION_DURATION)
+ animator.interpolator = EMPHASIZED_ACCELERATE
+ return animator
}
private fun onClickAlwaysShowTaskbarSwitchOption() {
didPreferenceChange = true
- changePreference()
// Allow switch animation to finish and then close the popup.
- postDelayed(DIVIDER_POPUP_CLOSING_DELAY) { close(true) }
+ postDelayed(DIVIDER_POPUP_CLOSING_DELAY) {
+ if (isOpen) {
+ close(true)
+ }
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 3c7196a36f..6ddf9e940a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -21,6 +21,8 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APP
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
+import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -64,6 +66,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -259,6 +262,8 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
DraggableView originalView, int dragLayerX, int dragLayerY, DragSource source,
ItemInfo dragInfo, Rect dragRegion, float initialDragViewScale,
float dragViewScaleOnDrop, DragOptions options) {
+ mActivity.hideKeyboard();
+
mOptions = options;
mRegistrationX = mMotionDown.x - dragLayerX;
@@ -624,7 +629,9 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
if (tag instanceof ItemInfo) {
ItemInfo item = (ItemInfo) tag;
- if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
+ if (item.container == CONTAINER_ALL_APPS
+ || item.container == CONTAINER_PREDICTION
+ || isInSearchResultContainer(item)) {
if (mDisallowGlobalDrag) {
// We're dragging in taskbarAllApps, we don't have folders or shortcuts
return iconView;
@@ -646,6 +653,13 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
return iconView;
}
+ private static boolean isInSearchResultContainer(ItemInfo item) {
+ ContainerInfo containerInfo = item.getContainerInfo();
+ return containerInfo.getContainerCase() == EXTENDED_CONTAINERS
+ && containerInfo.getExtendedContainers().getContainerCase()
+ == DEVICE_SEARCH_RESULT_CONTAINER;
+ }
+
private void setupReturnDragAnimator(float fromX, float fromY, View originalView,
TaskbarReturnPropertiesListener animListener) {
// Finish any pending return animation before starting a new return
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index e521154497..a24cf4ba58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -18,6 +18,8 @@ package com.android.launcher3.taskbar;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_BACK;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
+
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.RectF;
@@ -73,6 +75,8 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
private SafeCloseable mViewCaptureCloseable;
private float mTaskbarBackgroundOffset;
+ private float mTaskbarBackgroundProgress;
+ private boolean mIsAnimatingTaskbarPinning = false;
private final MultiPropertyFactory<TaskbarDragLayer> mTaskbarBackgroundAlpha;
@@ -124,7 +128,7 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
}
protected void onDestroy() {
- onDestroy(!TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW);
+ onDestroy(!ENABLE_TASKBAR_NAVBAR_UNIFICATION);
}
@Override
@@ -162,10 +166,19 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
float backgroundHeight = mControllerCallbacks.getTaskbarBackgroundHeight()
* (1f - mTaskbarBackgroundOffset);
mBackgroundRenderer.setBackgroundHeight(backgroundHeight);
+ mBackgroundRenderer.setBackgroundProgress(mTaskbarBackgroundProgress);
mBackgroundRenderer.draw(canvas);
super.dispatchDraw(canvas);
}
+ /**
+ * Sets animation boolean when taskbar pinning animation starts or stops.
+ */
+ public void setAnimatingTaskbarPinning(boolean animatingTaskbarPinning) {
+ mIsAnimatingTaskbarPinning = animatingTaskbarPinning;
+ mBackgroundRenderer.setAnimatingPinning(mIsAnimatingTaskbarPinning);
+ }
+
protected MultiProperty getBackgroundRendererAlpha() {
return mTaskbarBackgroundAlpha.get(INDEX_ALL_OTHER_STATES);
}
@@ -175,6 +188,15 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
}
/**
+ * Sets the value for taskbar background switching between persistent and transient backgrounds.
+ * @param progress 0 is transient background, 1 is persistent background.
+ */
+ protected void setTaskbarBackgroundProgress(float progress) {
+ mTaskbarBackgroundProgress = progress;
+ invalidate();
+ }
+
+ /**
* Sets the translation of the background color behind all the Taskbar contents.
* @param offset 0 is fully onscreen, 1 is fully offscreen.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 867b062be1..73e32abb2c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.taskbar;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_PERSISTENT;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
+
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
@@ -24,6 +27,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.util.DimensionUtils;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.TouchController;
@@ -58,6 +62,9 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
// changes the inset visibility.
private final AnimatedFloat mTaskbarAlpha = new AnimatedFloat(this::updateTaskbarAlpha);
+ private final AnimatedFloat mTaskbarBackgroundProgress = new AnimatedFloat(
+ this::updateTaskbarBackgroundProgress);
+
// Initialized in init.
private TaskbarControllers mControllers;
private TaskbarStashViaTouchController mTaskbarStashViaTouchController;
@@ -83,6 +90,10 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
mOnBackgroundNavButtonColorIntensity = mControllers.navbarButtonsViewController
.getOnTaskbarBackgroundNavButtonColorOverride();
+ mTaskbarBackgroundProgress.updateValue(DisplayController.isTransientTaskbar(mActivity)
+ ? PINNING_TRANSIENT
+ : PINNING_PERSISTENT);
+
mBgTaskbar.value = 1;
mKeyguardBgTaskbar.value = 1;
mNotificationShadeBgTaskbar.value = 1;
@@ -138,6 +149,11 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
return mBgOffset;
}
+ // AnimatedFloat is for animating between pinned and transient taskbar
+ public AnimatedFloat getTaskbarBackgroundProgress() {
+ return mTaskbarBackgroundProgress;
+ }
+
public AnimatedFloat getTaskbarAlpha() {
return mTaskbarAlpha;
}
@@ -180,10 +196,13 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa
private void updateBackgroundOffset() {
mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
-
updateOnBackgroundNavButtonColorIntensity();
}
+ private void updateTaskbarBackgroundProgress() {
+ mTaskbarDragLayer.setTaskbarBackgroundProgress(mTaskbarBackgroundProgress.value);
+ }
+
private void updateTaskbarAlpha() {
mTaskbarDragLayer.setAlpha(mTaskbarAlpha.value);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index de4175ddc1..6d1b55870f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -29,8 +29,10 @@ import androidx.core.view.updateLayoutParams
import com.airbnb.lottie.LottieAnimationView
import com.android.launcher3.R
import com.android.launcher3.Utilities
+import com.android.launcher3.config.FeatureFlags.enableTaskbarPinningEdu
import com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_EDU_OPEN
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
+import com.android.launcher3.taskbar.TaskbarManager.isPhoneMode
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP
import com.android.quickstep.util.LottieAnimationColorUtils
@@ -40,16 +42,19 @@ import java.io.PrintWriter
const val TOOLTIP_STEP_SWIPE = 0
/** Second EDU step for explaining Taskbar functionality when unstashed. */
const val TOOLTIP_STEP_FEATURES = 1
+/** Third EDU step for explaining Taskbar pinning. */
+const val TOOLTIP_STEP_PINNING = 2
+
/**
* EDU is completed.
*
* This value should match the maximum count for [TASKBAR_EDU_TOOLTIP_STEP].
*/
-const val TOOLTIP_STEP_NONE = 2
+const val TOOLTIP_STEP_NONE = 3
/** Current step in the tooltip EDU flow. */
@Retention(AnnotationRetention.SOURCE)
-@IntDef(TOOLTIP_STEP_SWIPE, TOOLTIP_STEP_FEATURES, TOOLTIP_STEP_NONE)
+@IntDef(TOOLTIP_STEP_SWIPE, TOOLTIP_STEP_FEATURES, TOOLTIP_STEP_PINNING, TOOLTIP_STEP_NONE)
annotation class TaskbarEduTooltipStep
/** Controls stepping through the Taskbar tooltip EDU. */
@@ -57,7 +62,7 @@ class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
LoggableTaskbarController {
private val isTooltipEnabled: Boolean
- get() = !Utilities.isRunningInTestHarness()
+ get() = !Utilities.isRunningInTestHarness() && !isPhoneMode(activityContext.deviceProfile)
private val isOpen: Boolean
get() = tooltip?.isOpen ?: false
val isBeforeTooltipFeaturesStep: Boolean
@@ -67,11 +72,10 @@ class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
@TaskbarEduTooltipStep
var tooltipStep: Int
get() {
- return activityContext.onboardingPrefs?.getCount(TASKBAR_EDU_TOOLTIP_STEP)
- ?: TOOLTIP_STEP_NONE
+ return TASKBAR_EDU_TOOLTIP_STEP.get(activityContext)
}
private set(step) {
- activityContext.onboardingPrefs?.setEventCount(step, TASKBAR_EDU_TOOLTIP_STEP)
+ TASKBAR_EDU_TOOLTIP_STEP.set(step, activityContext)
}
private var tooltip: TaskbarEduTooltip? = null
@@ -114,19 +118,19 @@ class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
tooltip?.run {
val splitscreenAnim = requireViewById<LottieAnimationView>(R.id.splitscreen_animation)
val suggestionsAnim = requireViewById<LottieAnimationView>(R.id.suggestions_animation)
- val settingsAnim = requireViewById<LottieAnimationView>(R.id.settings_animation)
- val settingsEdu = requireViewById<View>(R.id.settings_edu)
+ val pinningAnim = requireViewById<LottieAnimationView>(R.id.pinning_animation)
+ val pinningEdu = requireViewById<View>(R.id.pinning_edu)
splitscreenAnim.supportLightTheme()
suggestionsAnim.supportLightTheme()
- settingsAnim.supportLightTheme()
+ pinningAnim.supportLightTheme()
if (DisplayController.isTransientTaskbar(activityContext)) {
splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient)
suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient)
- settingsEdu.visibility = GONE
+ pinningEdu.visibility = if (enableTaskbarPinningEdu()) VISIBLE else GONE
} else {
splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_persistent)
suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_persistent)
- settingsEdu.visibility = VISIBLE
+ pinningEdu.visibility = GONE
}
// Set up layout parameters.
@@ -135,13 +139,16 @@ class TaskbarEduTooltipController(val activityContext: TaskbarActivityContext) :
if (DisplayController.isTransientTaskbar(activityContext)) {
width =
resources.getDimensionPixelSize(
- R.dimen.taskbar_edu_features_tooltip_width_transient
+ if (enableTaskbarPinningEdu())
+ R.dimen.taskbar_edu_features_tooltip_width_with_three_features
+ else R.dimen.taskbar_edu_features_tooltip_width_with_two_features
)
+
bottomMargin += activityContext.deviceProfile.taskbarHeight
} else {
width =
resources.getDimensionPixelSize(
- R.dimen.taskbar_edu_features_tooltip_width_persistent
+ R.dimen.taskbar_edu_features_tooltip_width_with_two_features
)
}
}
@@ -251,5 +258,5 @@ private fun LottieAnimationView.supportLightTheme() {
return
}
- LottieAnimationColorUtils.updateColors(this, DARK_TO_LIGHT_COLORS, context.theme)
+ LottieAnimationColorUtils.updateToColorResources(this, DARK_TO_LIGHT_COLORS, context.theme)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
index ffaee455d9..333c07b8fa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarForceVisibleImmersiveController.java
@@ -22,7 +22,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBIL
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static com.android.launcher3.taskbar.NavbarButtonsViewController.ALPHA_INDEX_IMMERSIVE_MODE;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IMMERSIVE_MODE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import android.os.Bundle;
import android.os.Handler;
@@ -84,7 +84,7 @@ public class TaskbarForceVisibleImmersiveController implements TouchController {
/** Update values tracked via sysui flags. */
public void updateSysuiFlags(int sysuiFlags) {
- mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_IMMERSIVE_MODE) != 0;
+ mIsImmersiveMode = (sysuiFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) == 0;
if (mContext.isNavBarForceVisible()) {
if (mIsImmersiveMode) {
startIconDimming();
@@ -158,8 +158,7 @@ public class TaskbarForceVisibleImmersiveController implements TouchController {
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (!isNavbarShownInImmersiveMode()
- || mControllers.taskbarStashController.supportsManualStashing()) {
+ if (!isNavbarShownInImmersiveMode()) {
return false;
}
return onControllerTouchEvent(ev);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
index c3ec1e5ad0..044319796e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHoverToolTipController.java
@@ -35,8 +35,6 @@ import android.view.ContextThemeWrapper;
import android.view.MotionEvent;
import android.view.View;
-import androidx.annotation.VisibleForTesting;
-
import com.android.app.animation.Interpolators;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
@@ -51,8 +49,7 @@ import com.android.launcher3.views.ArrowTipView;
*/
public class TaskbarHoverToolTipController implements View.OnHoverListener {
- @VisibleForTesting protected static final int HOVER_TOOL_TIP_REVEAL_START_DELAY = 400;
- private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 300;
+ private static final int HOVER_TOOL_TIP_REVEAL_DURATION = 250;
private static final int HOVER_TOOL_TIP_EXIT_DURATION = 150;
private final Handler mHoverToolTipHandler = new Handler(Looper.getMainLooper());
@@ -84,6 +81,12 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
R.style.ArrowTipTaskbarStyle);
mHoverToolTipView = new ArrowTipView(arrowContextWrapper, /* isPointingUp = */ false,
R.layout.arrow_toast);
+ int verticalPadding = arrowContextWrapper.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_tooltip_vertical_padding);
+ int horizontalPadding = arrowContextWrapper.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_tooltip_horizontal_padding);
+ mHoverToolTipView.findViewById(R.id.text).setPadding(horizontalPadding, verticalPadding,
+ horizontalPadding, verticalPadding);
AnimatorSet hoverCloseAnimator = new AnimatorSet();
ObjectAnimator textCloseAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 0);
@@ -101,17 +104,18 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
mHoverToolTipView.setCustomCloseAnimation(hoverCloseAnimator);
AnimatorSet hoverOpenAnimator = new AnimatorSet();
- ObjectAnimator textOpenAnimator = ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 255);
- textOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.33f, 1f));
- ObjectAnimator scaleOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 1f);
+ ObjectAnimator textOpenAnimator =
+ ObjectAnimator.ofInt(mHoverToolTipView, TEXT_ALPHA, 0, 255);
+ textOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.15f, 0.75f));
+ ObjectAnimator scaleOpenAnimator =
+ ObjectAnimator.ofFloat(mHoverToolTipView, SCALE_Y, 0f, 1f);
scaleOpenAnimator.setInterpolator(Interpolators.EMPHASIZED);
- ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 1f);
- alphaOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0.1f, 0.33f));
+ ObjectAnimator alphaOpenAnimator = ObjectAnimator.ofFloat(mHoverToolTipView, ALPHA, 0f, 1f);
+ alphaOpenAnimator.setInterpolator(Interpolators.clampToProgress(LINEAR, 0f, 0.33f));
hoverOpenAnimator.playTogether(
scaleOpenAnimator,
textOpenAnimator,
alphaOpenAnimator);
- hoverOpenAnimator.setStartDelay(HOVER_TOOL_TIP_REVEAL_START_DELAY);
hoverOpenAnimator.setDuration(HOVER_TOOL_TIP_REVEAL_DURATION);
mHoverToolTipView.setCustomOpenAnimation(hoverOpenAnimator);
@@ -120,8 +124,6 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
mHoverToolTipView.setPivotY(bottom);
mHoverToolTipView.setY(mTaskbarView.getTop() - (bottom - top));
});
- mHoverToolTipView.setScaleY(0f);
- mHoverToolTipView.setAlpha(0f);
}
@Override
@@ -147,8 +149,7 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
}
private void startRevealHoverToolTip() {
- mHoverToolTipHandler.postDelayed(mRevealHoverToolTipRunnable,
- HOVER_TOOL_TIP_REVEAL_START_DELAY);
+ mHoverToolTipHandler.post(mRevealHoverToolTipRunnable);
}
private void revealHoverToolTip() {
@@ -158,14 +159,12 @@ public class TaskbarHoverToolTipController implements View.OnHoverListener {
if (mHoverView instanceof FolderIcon && !((FolderIcon) mHoverView).getIconVisible()) {
return;
}
- mActivity.setTaskbarWindowFullscreen(true);
Rect iconViewBounds = Utilities.getViewBounds(mHoverView);
mHoverToolTipView.showAtLocation(mToolTipText, iconViewBounds.centerX(),
mTaskbarView.getTop(), /* shouldAutoClose= */ false);
}
private void startHideHoverToolTip() {
- mHoverToolTipHandler.removeCallbacks(mRevealHoverToolTipRunnable);
int accessibilityHideTimeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(
mActivity, /* originalTimeout= */ 0, FLAG_CONTENT_TEXT);
mHoverToolTipHandler.postDelayed(mHideHoverToolTipRunnable, accessibilityHideTimeout);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 5a4534e651..1a34b7a267 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -15,9 +15,9 @@
*/
package com.android.launcher3.taskbar
-import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
import android.graphics.Insets
import android.graphics.Region
+import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
import android.os.Binder
import android.os.IBinder
import android.view.Gravity
@@ -41,6 +41,8 @@ import com.android.internal.policy.GestureNavigationSettingsObserver
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.anim.AlphaUpdateListener
+import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION
+import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
import com.android.launcher3.util.DisplayController
import java.io.PrintWriter
@@ -97,7 +99,14 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
0
}
- windowLayoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag)
+ windowLayoutParams.providedInsets =
+ if (enableTaskbarNoRecreate()) {
+ getProvidedInsets(controllers.sharedState!!.insetsFrameProviders!!,
+ insetsRoundedCornerFlag)
+ } else {
+ getProvidedInsets(insetsRoundedCornerFlag)
+ }
+
if (!context.isGestureNav) {
if (windowLayoutParams.paramsForRotation != null) {
for (layoutParams in windowLayoutParams.paramsForRotation) {
@@ -154,6 +163,26 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
}
/**
+ * This is for when ENABLE_TASKBAR_NO_RECREATION is enabled. We generate one instance of
+ * providedInsets and use it across the entire lifecycle of TaskbarManager. The only thing
+ * we need to reset is nav bar flags based on insetsRoundedCornerFlag.
+ */
+ private fun getProvidedInsets(providedInsets: Array<InsetsFrameProvider>,
+ insetsRoundedCornerFlag: Int): Array<InsetsFrameProvider> {
+ val navBarsFlag =
+ (if (context.isGestureNav) FLAG_SUPPRESS_SCRIM else 0) or insetsRoundedCornerFlag
+ for (provider in providedInsets) {
+ if (provider.type == navigationBars()) {
+ provider.setFlags(
+ navBarsFlag,
+ FLAG_SUPPRESS_SCRIM or FLAG_INSETS_ROUNDED_CORNER
+ )
+ }
+ }
+ return providedInsets
+ }
+
+ /**
* The inset types and number of insets provided have to match for both gesture nav and button
* nav. The values and the order of the elements in array are allowed to differ.
* Reason being WM does not allow types and number of insets changing for a given window once it
@@ -197,7 +226,6 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
provider.insetsSize = Insets.of(0, 0, rightIndexInset, 0)
}
-
// When in gesture nav, report the stashed height to the IME, to allow hiding the
// IME navigation bar.
val imeInsetsSize = if (ENABLE_HIDE_IME_CAPTION_BAR && context.isGestureNav) {
@@ -208,6 +236,12 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
val imeInsetsSizeOverride =
arrayOf(
InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
+ InsetsFrameProvider.InsetsSizeOverride(TYPE_VOICE_INTERACTION,
+ // No-op override to keep the size and types in sync with the
+ // override below (insetsSizeOverrides must have the same length and
+ // types after the window is added according to
+ // WindowManagerService#relayoutWindow)
+ provider.insetsSize)
)
// Use 0 tappableElement insets for the VoiceInteractionWindow when gesture nav is enabled.
val visInsetsSizeForTappableElement =
@@ -216,12 +250,11 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
val insetsSizeOverrideForTappableElement =
arrayOf(
InsetsFrameProvider.InsetsSizeOverride(TYPE_INPUT_METHOD, imeInsetsSize),
- InsetsFrameProvider.InsetsSizeOverride(
- TYPE_VOICE_INTERACTION,
+ InsetsFrameProvider.InsetsSizeOverride(TYPE_VOICE_INTERACTION,
visInsetsSizeForTappableElement
),
)
- if ((context.isGestureNav || TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW)
+ if ((context.isGestureNav || ENABLE_TASKBAR_NAVBAR_UNIFICATION)
&& provider.type == tappableElement()) {
provider.insetsSizeOverrides = insetsSizeOverrideForTappableElement
} else if (provider.type != systemGestures()) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 90f7beaf9c..057b71b74f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -17,7 +17,9 @@ package com.android.launcher3.taskbar;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
+import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_OVERVIEW;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
@@ -44,6 +46,7 @@ import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
@@ -66,13 +69,13 @@ public class TaskbarLauncherStateController {
private static final String TAG = TaskbarLauncherStateController.class.getSimpleName();
private static final boolean DEBUG = false;
- /** Launcher activity is resumed and focused. */
- public static final int FLAG_RESUMED = 1 << 0;
+ /** Launcher activity is visible and focused. */
+ public static final int FLAG_VISIBLE = 1 << 0;
/**
- * A external transition / animation is running that will result in FLAG_RESUMED being set.
+ * A external transition / animation is running that will result in FLAG_VISIBLE being set.
**/
- public static final int FLAG_TRANSITION_TO_RESUMED = 1 << 1;
+ public static final int FLAG_TRANSITION_TO_VISIBLE = 1 << 1;
/**
* Set while the launcher state machine is performing a state transition, see {@link
@@ -116,7 +119,7 @@ public class TaskbarLauncherStateController {
*/
private static final int FLAG_TASKBAR_HIDDEN = 1 << 6;
- private static final int FLAGS_LAUNCHER_ACTIVE = FLAG_RESUMED | FLAG_TRANSITION_TO_RESUMED;
+ private static final int FLAGS_LAUNCHER_ACTIVE = FLAG_VISIBLE | FLAG_TRANSITION_TO_VISIBLE;
/** Equivalent to an int with all 1s for binary operation purposes */
private static final int FLAGS_ALL = ~0;
@@ -202,19 +205,32 @@ public class TaskbarLauncherStateController {
public void onStateTransitionComplete(LauncherState finalState) {
mLauncherState = finalState;
updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false);
- // TODO(b/279514548) Cleans up bad state that can occur when user interacts with
- // taskbar on top of transparent activity.
- if (finalState == LauncherState.NORMAL && mLauncher.hasBeenResumed()) {
- updateStateForFlag(FLAG_RESUMED, true);
- }
applyState();
- boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
+ boolean disallowLongClick =
+ FeatureFlags.enableSplitContextually()
+ ? mLauncher.isSplitSelectionEnabled()
+ : finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
com.android.launcher3.taskbar.Utilities.setOverviewDragState(
mControllers, finalState.disallowTaskbarGlobalDrag(),
disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
}
};
+ /**
+ * Callback for when launcher state transition completes after user swipes to home.
+ * @param finalState The final state of the transition.
+ */
+ public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) {
+ // TODO(b/279514548) Cleans up bad state that can occur when user interacts with
+ // taskbar on top of transparent activity.
+ if (!FeatureFlags.enableHomeTransitionListener()
+ && (finalState == LauncherState.NORMAL)
+ && mLauncher.hasBeenResumed()) {
+ updateStateForFlag(FLAG_VISIBLE, true);
+ applyState();
+ }
+ }
+
/** Initializes the controller instance, and applies the initial state immediately. */
public void init(TaskbarControllers controllers, QuickstepLauncher launcher,
int sysuiStateFlags) {
@@ -277,7 +293,7 @@ public class TaskbarLauncherStateController {
}
stashController.updateStateForFlag(FLAG_IN_APP, false);
- updateStateForFlag(FLAG_TRANSITION_TO_RESUMED, true);
+ updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, true);
animatorSet.play(stashController.createApplyStateAnimator(duration));
animatorSet.play(applyState(duration, false));
@@ -416,6 +432,9 @@ public class TaskbarLauncherStateController {
controllers.bubbleStashController.setBubblesShowingOnOverview(onOverview);
});
+ mControllers.taskbarStashController.updateStateForFlag(FLAG_IN_OVERVIEW,
+ mLauncherState == LauncherState.OVERVIEW);
+
AnimatorSet animatorSet = new AnimatorSet();
if (hasAnyFlag(changedFlags, FLAG_LAUNCHER_IN_STATE_TRANSITION)) {
@@ -425,7 +444,7 @@ public class TaskbarLauncherStateController {
if (launcherTransitionCompleted
&& mLauncherState == LauncherState.QUICK_SWITCH_FROM_HOME) {
// We're about to be paused, set immediately to ensure seamless handoff.
- updateStateForFlag(FLAG_RESUMED, false);
+ updateStateForFlag(FLAG_VISIBLE, false);
applyState(0 /* duration */);
}
if (mLauncherState == LauncherState.NORMAL) {
@@ -715,6 +734,7 @@ public class TaskbarLauncherStateController {
}
mIconAlphaForHome.setValue(alpha);
boolean hotseatVisible = alpha == 0
+ || isPhoneMode(mLauncher.getDeviceProfile())
|| (!mControllers.uiController.isHotseatIconOnTopWhenAligned()
&& mIconAlignment.value > 0);
/*
@@ -751,10 +771,10 @@ public class TaskbarLauncherStateController {
mTaskBarRecentsAnimationListener = null;
((RecentsView) mLauncher.getOverviewPanel()).setTaskLaunchListener(null);
- // Update the resumed state immediately to ensure a seamless handoff
- boolean launcherResumed = !finishedToApp;
- updateStateForFlag(FLAG_TRANSITION_TO_RESUMED, false);
- updateStateForFlag(FLAG_RESUMED, launcherResumed);
+ // Update the visible state immediately to ensure a seamless handoff
+ boolean launcherVisible = !finishedToApp;
+ updateStateForFlag(FLAG_TRANSITION_TO_VISIBLE, false);
+ updateStateForFlag(FLAG_VISIBLE, launcherVisible);
applyState();
TaskbarStashController controller = mControllers.taskbarStashController;
@@ -768,8 +788,8 @@ public class TaskbarLauncherStateController {
private static String getStateString(int flags) {
StringJoiner result = new StringJoiner("|");
- appendFlag(result, flags, FLAG_RESUMED, "resumed");
- appendFlag(result, flags, FLAG_TRANSITION_TO_RESUMED, "transition_to_resumed");
+ appendFlag(result, flags, FLAG_VISIBLE, "flag_visible");
+ appendFlag(result, flags, FLAG_TRANSITION_TO_VISIBLE, "transition_to_visible");
appendFlag(result, flags, FLAG_LAUNCHER_IN_STATE_TRANSITION,
"launcher_in_state_transition");
appendFlag(result, flags, FLAG_AWAKE, "awake");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index b115ca8032..bbac11625d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -18,33 +18,37 @@ package com.android.launcher3.taskbar;
import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
+import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
+import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
import android.annotation.SuppressLint;
-import android.app.Activity;
import android.app.PendingIntent;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Handler;
-import android.os.SystemProperties;
import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
import android.view.Display;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -53,12 +57,11 @@ import androidx.annotation.VisibleForTesting;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.RecentsActivity;
@@ -68,6 +71,7 @@ import com.android.quickstep.util.AssistUtils;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider;
+import com.android.wm.shell.Flags;
import java.io.PrintWriter;
import java.util.StringJoiner;
@@ -95,9 +99,6 @@ public class TaskbarManager {
| ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- public static final boolean FLAG_HIDE_NAVBAR_WINDOW =
- SystemProperties.getBoolean("persist.wm.debug.hide_navbar_window", false);
-
private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE);
@@ -105,6 +106,10 @@ public class TaskbarManager {
Settings.Secure.NAV_BAR_KIDS_MODE);
private final Context mContext;
+ private final @Nullable Context mNavigationBarPanelContext;
+ private WindowManager mWindowManager;
+ private FrameLayout mTaskbarRootLayout;
+ private boolean mAddedWindow;
private final TaskbarNavButtonController mNavButtonController;
private final ComponentCallbacks mComponentCallbacks;
@@ -135,44 +140,28 @@ public class TaskbarManager {
private boolean mUserUnlocked = false;
- public static final int SYSTEM_ACTION_ID_TASKBAR = 499;
-
- /**
- * For Taskbar broadcast intent filter.
- */
- public static final String ACTION_SHOW_TASKBAR = "ACTION_SHOW_TASKBAR";
-
private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver =
new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast);
- private final SharedPreferences.OnSharedPreferenceChangeListener
- mTaskbarPinningPreferenceChangeListener = (sharedPreferences, key) -> {
- if (TASKBAR_PINNING_KEY.equals(key)) {
- recreateTaskbar();
- }
- };
-
- private final ActivityLifecycleCallbacksAdapter mLifecycleCallbacks =
- new ActivityLifecycleCallbacksAdapter() {
- @Override
- public void onActivityDestroyed(Activity activity) {
- if (mActivity != activity) return;
- if (mActivity != null) {
- mActivity.removeOnDeviceProfileChangeListener(
- mDebugActivityDeviceProfileChanged);
- Log.d(TASKBAR_NOT_DESTROYED_TAG,
- "unregistering activity lifecycle callbacks from "
- + "onActivityDestroyed.");
- mActivity.unregisterActivityLifecycleCallbacks(this);
- }
- mActivity = null;
- debugWhyTaskbarNotDestroyed("clearActivity");
- if (mTaskbarActivityContext != null) {
- mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT);
- }
- mUnfoldProgressProvider.setSourceProvider(null);
- }
- };
+ private final Runnable mActivityOnDestroyCallback = new Runnable() {
+ @Override
+ public void run() {
+ if (mActivity != null) {
+ mActivity.removeOnDeviceProfileChangeListener(
+ mDebugActivityDeviceProfileChanged);
+ Log.d(TASKBAR_NOT_DESTROYED_TAG,
+ "unregistering activity lifecycle callbacks from "
+ + "onActivityDestroyed.");
+ mActivity.removeEventCallback(EVENT_DESTROYED, this);
+ }
+ mActivity = null;
+ debugWhyTaskbarNotDestroyed("clearActivity");
+ if (mTaskbarActivityContext != null) {
+ mTaskbarActivityContext.setUIController(TaskbarUIController.DEFAULT);
+ }
+ mUnfoldProgressProvider.setSourceProvider(null);
+ }
+ };
UnfoldTransitionProgressProvider.TransitionProgressListener mUnfoldTransitionProgressListener =
new UnfoldTransitionProgressProvider.TransitionProgressListener() {
@@ -207,7 +196,27 @@ public class TaskbarManager {
public TaskbarManager(TouchInteractionService service) {
Display display =
service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
- mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
+ mContext = service.createWindowContext(display,
+ ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL,
+ null);
+ mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ ? service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
+ : null;
+ if (enableTaskbarNoRecreate()) {
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ mTaskbarRootLayout = new FrameLayout(mContext) {
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ // The motion events can be outside the view bounds of task bar, and hence
+ // manually dispatching them to the drag layer here.
+ if (mTaskbarActivityContext != null
+ && mTaskbarActivityContext.getDragLayer().isAttachedToWindow()) {
+ return mTaskbarActivityContext.getDragLayer().dispatchTouchEvent(ev);
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+ };
+ }
mNavButtonController = new TaskbarNavButtonController(service,
SystemUiProxy.INSTANCE.get(mContext), new Handler(),
AssistUtils.newInstance(mContext));
@@ -244,7 +253,7 @@ public class TaskbarManager {
destroyExistingTaskbar();
} else {
if (dp != null && isTaskbarPresent(dp)) {
- if (FLAG_HIDE_NAVBAR_WINDOW) {
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
// Re-initialize for screen size change? Should this be done
// by looking at screen-size change flag in configDiff in the
// block above?
@@ -288,13 +297,16 @@ public class TaskbarManager {
private void destroyExistingTaskbar() {
debugWhyTaskbarNotDestroyed("destroyExistingTaskbar: " + mTaskbarActivityContext);
if (mTaskbarActivityContext != null) {
- LauncherPrefs.get(mContext).removeListener(mTaskbarPinningPreferenceChangeListener,
- TASKBAR_PINNING);
mTaskbarActivityContext.onDestroy();
- if (!FLAG_HIDE_NAVBAR_WINDOW) {
+ if (!ENABLE_TASKBAR_NAVBAR_UNIFICATION || enableTaskbarNoRecreate()) {
mTaskbarActivityContext = null;
}
}
+ DeviceProfile dp = mUserUnlocked ?
+ LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
+ if (dp == null || !isTaskbarPresent(dp)) {
+ removeTaskbarRootViewFromWindow();
+ }
}
/**
@@ -322,7 +334,7 @@ public class TaskbarManager {
return;
}
- mTaskbarActivityContext.toggleAllApps();
+ mTaskbarActivityContext.toggleAllAppsSearch();
}
/**
@@ -343,6 +355,7 @@ public class TaskbarManager {
mUserUnlocked = true;
LauncherAppState.getIDP(mContext).addOnChangeListener(mIdpChangeListener);
recreateTaskbar();
+ addTaskbarRootViewToWindow();
}
/**
@@ -358,7 +371,7 @@ public class TaskbarManager {
mActivity.addOnDeviceProfileChangeListener(mDebugActivityDeviceProfileChanged);
Log.d(TASKBAR_NOT_DESTROYED_TAG,
"registering activity lifecycle callbacks from setActivity().");
- mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
+ mActivity.addEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback);
UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
getUnfoldTransitionProgressProviderForActivity(activity);
if (unfoldTransitionProgressProvider != null) {
@@ -417,7 +430,7 @@ public class TaskbarManager {
boolean isTaskbarEnabled = dp != null && isTaskbarPresent(dp);
debugWhyTaskbarNotDestroyed("recreateTaskbar: isTaskbarEnabled=" + isTaskbarEnabled
+ " [dp != null (i.e. mUserUnlocked)]=" + (dp != null)
- + " FLAG_HIDE_NAVBAR_WINDOW=" + FLAG_HIDE_NAVBAR_WINDOW
+ + " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent));
if (!isTaskbarEnabled) {
SystemUiProxy.INSTANCE.get(mContext)
@@ -425,13 +438,15 @@ public class TaskbarManager {
return;
}
- if (mTaskbarActivityContext == null) {
- mTaskbarActivityContext = new TaskbarActivityContext(mContext, dp,
- mNavButtonController,
- mUnfoldProgressProvider);
+ if (enableTaskbarNoRecreate() || mTaskbarActivityContext == null) {
+ mTaskbarActivityContext = new TaskbarActivityContext(mContext,
+ mNavigationBarPanelContext, dp, mNavButtonController,
+ mUnfoldProgressProvider);
} else {
mTaskbarActivityContext.updateDeviceProfile(dp);
}
+ mSharedState.startTaskbarVariantIsTransient =
+ DisplayController.isTransientTaskbar(mTaskbarActivityContext);
mTaskbarActivityContext.init(mSharedState);
if (mActivity != null) {
@@ -439,9 +454,12 @@ public class TaskbarManager {
createTaskbarUIControllerForActivity(mActivity));
}
- // We to wait until user unlocks the device to attach listener.
- LauncherPrefs.get(mContext).addListener(mTaskbarPinningPreferenceChangeListener,
- TASKBAR_PINNING);
+ if (enableTaskbarNoRecreate()) {
+ addTaskbarRootViewToWindow();
+ mTaskbarRootLayout.removeAllViews();
+ mTaskbarRootLayout.addView(mTaskbarActivityContext.getDragLayer());
+ mTaskbarActivityContext.notifyUpdateLayoutParams();
+ }
} finally {
Trace.endSection();
}
@@ -479,7 +497,7 @@ public class TaskbarManager {
* and we are using a single window for taskbar and navbar.
*/
public static boolean isPhoneMode(DeviceProfile deviceProfile) {
- return TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW && deviceProfile.isPhone;
+ return ENABLE_TASKBAR_NAVBAR_UNIFICATION && deviceProfile.isPhone;
}
/**
@@ -491,7 +509,7 @@ public class TaskbarManager {
}
private boolean isTaskbarPresent(DeviceProfile deviceProfile) {
- return FLAG_HIDE_NAVBAR_WINDOW || deviceProfile.isTaskbarPresent;
+ return ENABLE_TASKBAR_NAVBAR_UNIFICATION || deviceProfile.isTaskbarPresent;
}
public void onRotationProposal(int rotation, boolean isValid) {
@@ -530,7 +548,7 @@ public class TaskbarManager {
Log.d(TASKBAR_NOT_DESTROYED_TAG,
"unregistering activity lifecycle callbacks from "
+ "removeActivityCallbackAndListeners().");
- mActivity.unregisterActivityLifecycleCallbacks(mLifecycleCallbacks);
+ mActivity.removeEventCallback(EVENT_DESTROYED, mActivityOnDestroyCallback);
UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
getUnfoldTransitionProgressProviderForActivity(mActivity);
if (unfoldTransitionProgressProvider != null) {
@@ -573,6 +591,21 @@ public class TaskbarManager {
}
}
+ private void addTaskbarRootViewToWindow() {
+ if (enableTaskbarNoRecreate() && !mAddedWindow && mTaskbarActivityContext != null) {
+ mWindowManager.addView(mTaskbarRootLayout,
+ mTaskbarActivityContext.getWindowLayoutParams());
+ mAddedWindow = true;
+ }
+ }
+
+ private void removeTaskbarRootViewFromWindow() {
+ if (enableTaskbarNoRecreate() && mAddedWindow) {
+ mWindowManager.removeViewImmediate(mTaskbarRootLayout);
+ mAddedWindow = false;
+ }
+ }
+
/** Temp logs for b/254119092. */
public void debugWhyTaskbarNotDestroyed(String debugReason) {
StringJoiner log = new StringJoiner("\n");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 03df0365bb..3f72e5d9d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -27,6 +27,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -52,7 +53,6 @@ import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.AssistUtils;
-import com.android.quickstep.views.DesktopTaskView;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -274,7 +274,7 @@ public class TaskbarNavButtonController implements TaskbarControllers.LoggableTa
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
- if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ if (isDesktopModeSupported()) {
DesktopVisibilityController desktopVisibilityController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
new file mode 100644
index 0000000000..6cb28eee36
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -0,0 +1,134 @@
+/*
+ * 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.taskbar
+
+import android.animation.AnimatorSet
+import android.annotation.SuppressLint
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.core.animation.doOnEnd
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN
+import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
+import java.io.PrintWriter
+
+/** Controls taskbar pinning through a popup view. */
+class TaskbarPinningController(private val context: TaskbarActivityContext) :
+ TaskbarControllers.LoggableTaskbarController {
+
+ private lateinit var controllers: TaskbarControllers
+ private lateinit var taskbarSharedState: TaskbarSharedState
+ private lateinit var launcherPrefs: LauncherPrefs
+ private val statsLogManager = context.statsLogManager
+ @VisibleForTesting var isAnimatingTaskbarPinning = false
+ @VisibleForTesting lateinit var onCloseCallback: (preferenceChanged: Boolean) -> Unit
+
+ @SuppressLint("VisibleForTests")
+ fun init(taskbarControllers: TaskbarControllers, sharedState: TaskbarSharedState) {
+ controllers = taskbarControllers
+ taskbarSharedState = sharedState
+ launcherPrefs = context.launcherPrefs
+ onCloseCallback =
+ fun(didPreferenceChange: Boolean) {
+ statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE)
+ context.dragLayer.post { context.onPopupVisibilityChanged(false) }
+
+ if (!didPreferenceChange) {
+ return
+ }
+ val animateToValue =
+ if (!launcherPrefs.get(TASKBAR_PINNING)) {
+ PINNING_PERSISTENT
+ } else {
+ PINNING_TRANSIENT
+ }
+ taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT
+ animateTaskbarPinning(animateToValue)
+ }
+ }
+
+ fun showPinningView(view: View) {
+ context.isTaskbarWindowFullscreen = true
+ view.post {
+ val popupView = getPopupView(view)
+ popupView.requestFocus()
+ popupView.onCloseCallback = onCloseCallback
+ context.onPopupVisibilityChanged(true)
+ popupView.show()
+ statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN)
+ }
+ }
+
+ @VisibleForTesting
+ fun getPopupView(view: View): TaskbarDividerPopupView<*> {
+ return createAndPopulate(view, context)
+ }
+
+ @VisibleForTesting
+ fun animateTaskbarPinning(animateToValue: Float) {
+ val taskbarViewController = controllers.taskbarViewController
+ val animatorSet =
+ getAnimatorSetForTaskbarPinningAnimation(animateToValue).apply {
+ doOnEnd { recreateTaskbarAndUpdatePinningValue() }
+ duration = PINNING_ANIMATION_DURATION
+ }
+ controllers.taskbarOverlayController.hideWindow()
+ updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true)
+ taskbarViewController.animateAwayNotificationDotsDuringTaskbarPinningAnimation()
+ animatorSet.start()
+ }
+
+ @VisibleForTesting
+ fun getAnimatorSetForTaskbarPinningAnimation(animateToValue: Float): AnimatorSet {
+ val animatorSet = AnimatorSet()
+ val taskbarViewController = controllers.taskbarViewController
+ val dragLayerController = controllers.taskbarDragLayerController
+
+ animatorSet.playTogether(
+ dragLayerController.taskbarBackgroundProgress.animateToValue(animateToValue),
+ taskbarViewController.taskbarIconTranslationYForPinning.animateToValue(animateToValue),
+ taskbarViewController.taskbarIconScaleForPinning.animateToValue(animateToValue),
+ taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue)
+ )
+
+ return animatorSet
+ }
+
+ private fun updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(isAnimating: Boolean) {
+ isAnimatingTaskbarPinning = isAnimating
+ context.dragLayer.setAnimatingTaskbarPinning(isAnimating)
+ }
+
+ @VisibleForTesting
+ fun recreateTaskbarAndUpdatePinningValue() {
+ updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false)
+ launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
+ }
+
+ override fun dumpLogs(prefix: String, pw: PrintWriter) {
+ pw.println(prefix + "TaskbarPinningController:")
+ pw.println("$prefix\tisAnimatingTaskbarPinning=$isAnimatingTaskbarPinning")
+ pw.println("$prefix\tTASKBAR_PINNING shared pref =" + launcherPrefs.get(TASKBAR_PINNING))
+ }
+
+ companion object {
+ const val PINNING_PERSISTENT = 1f
+ const val PINNING_TRANSIENT = 0f
+ const val PINNING_ANIMATION_DURATION = 500L
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 512b77a92a..a667dcaa6d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
import static com.android.launcher3.util.SplitConfigurationOptions.getLogEventForPosition;
import android.content.Intent;
@@ -163,19 +162,9 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
.filter(Objects::nonNull)
.collect(Collectors.toList());
- if (ENABLE_MATERIAL_U_POPUP.get()) {
- container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
- R.layout.popup_container_material_u, context.getDragLayer(), false);
- container.populateAndShowRowsMaterialU(icon, deepShortcutCount, systemShortcuts);
- } else {
- container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
+ container = (PopupContainerWithArrow) context.getLayoutInflater().inflate(
R.layout.popup_container, context.getDragLayer(), false);
- container.populateAndShow(
- icon,
- deepShortcutCount,
- mPopupDataProvider.getNotificationKeysForItem(item),
- systemShortcuts);
- }
+ container.populateAndShowRows(icon, deepShortcutCount, systemShortcuts);
container.addOnAttachStateChangeListener(
new PopupLiveUpdateHandler<BaseTaskbarContext>(context, container) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
index cdc6d59bf1..e40757a490 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimView.java
@@ -70,6 +70,10 @@ public class TaskbarScrimView extends View {
invalidate();
}
+ protected float getScrimAlpha() {
+ return mRenderer.getPaint().getAlpha() / 255f;
+ }
+
/**
* Sets the roundness of the round corner above Taskbar.
* @param cornerRoundness 0 has no round corner, 1 has complete round corner.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 1c250bf377..712374db67 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -15,9 +15,13 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.launcher3.taskbar.bubbles.BubbleBarController.BUBBLE_BAR_ENABLED;
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.taskbar.bubbles.BubbleBarController.isBubbleBarEnabled;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED;
+import static com.android.wm.shell.common.bubbles.BubbleConstants.BUBBLE_EXPANDED_SCRIM_ALPHA;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
import android.animation.ObjectAnimator;
import android.view.animation.Interpolator;
@@ -35,13 +39,13 @@ import java.io.PrintWriter;
public class TaskbarScrimViewController implements TaskbarControllers.LoggableTaskbarController,
TaskbarControllers.BackgroundRendererController {
- private static final float SCRIM_ALPHA = 0.6f;
-
private static final Interpolator SCRIM_ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
private static final Interpolator SCRIM_ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
private final TaskbarActivityContext mActivity;
private final TaskbarScrimView mScrimView;
+ private boolean mTaskbarVisible;
+ private int mSysUiStateFlags;
// Alpha property for the scrim.
private final AnimatedFloat mScrimAlpha = new AnimatedFloat(this::updateScrimAlpha);
@@ -62,35 +66,60 @@ public class TaskbarScrimViewController implements TaskbarControllers.LoggableTa
}
/**
+ * Called when the taskbar visibility changes.
+ *
+ * @param visibility the current visibility of {@link TaskbarView}.
+ */
+ public void onTaskbarVisibilityChanged(int visibility) {
+ mTaskbarVisible = visibility == VISIBLE;
+ if (shouldShowScrim()) {
+ showScrim(true, getScrimAlpha(), false /* skipAnim */);
+ } else if (mScrimView.getScrimAlpha() > 0f) {
+ showScrim(false, 0, false /* skipAnim */);
+ }
+ }
+
+ /**
* Updates the scrim state based on the flags.
*/
public void updateStateForSysuiFlags(int stateFlags, boolean skipAnim) {
- if (BUBBLE_BAR_ENABLED && DisplayController.isTransientTaskbar(mActivity)) {
+ if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
// These scrims aren't used if bubble bar & transient taskbar are active.
return;
}
- final boolean bubblesExpanded = (stateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+ mSysUiStateFlags = stateFlags;
+ showScrim(shouldShowScrim(), getScrimAlpha(), skipAnim);
+ }
+
+ private boolean shouldShowScrim() {
+ final boolean bubblesExpanded = (mSysUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) != 0;
+ boolean isShadeVisible = (mSysUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE) != 0;
+ return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
+ && !isShadeVisible
+ && !mControllers.taskbarStashController.isStashed()
+ && mTaskbarVisible;
+ }
+
+ private float getScrimAlpha() {
final boolean manageMenuExpanded =
- (stateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
- final boolean showScrim = !mControllers.navbarButtonsViewController.isImeVisible()
- && bubblesExpanded
- && mControllers.taskbarStashController.isTaskbarVisibleAndNotStashing();
- final float scrimAlpha = manageMenuExpanded
+ (mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
+ return manageMenuExpanded
// When manage menu shows there's the first scrim and second scrim so figure out
// what the total transparency would be.
- ? (SCRIM_ALPHA + (SCRIM_ALPHA * (1 - SCRIM_ALPHA)))
- : showScrim ? SCRIM_ALPHA : 0;
- showScrim(showScrim, scrimAlpha, skipAnim);
+ ? (BUBBLE_EXPANDED_SCRIM_ALPHA + (BUBBLE_EXPANDED_SCRIM_ALPHA
+ * (1 - BUBBLE_EXPANDED_SCRIM_ALPHA)))
+ : shouldShowScrim() ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0;
}
private void showScrim(boolean showScrim, float alpha, boolean skipAnim) {
mScrimView.setOnClickListener(showScrim ? (view) -> onClick() : null);
mScrimView.setClickable(showScrim);
- ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
- anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
- anim.start();
if (skipAnim) {
- anim.end();
+ mScrimView.setScrimAlpha(alpha);
+ } else {
+ ObjectAnimator anim = mScrimAlpha.animateToValue(showScrim ? alpha : 0);
+ anim.setInterpolator(showScrim ? SCRIM_ALPHA_IN : SCRIM_ALPHA_OUT);
+ anim.start();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
index 66ca7d927f..d09f74c65a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java
@@ -15,15 +15,28 @@
*/
package com.android.launcher3.taskbar;
+import static android.view.InsetsFrameProvider.SOURCE_DISPLAY;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.systemGestures;
+import static android.view.WindowInsets.Type.tappableElement;
+
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.DISPLAY_PROGRESS_COUNT;
import android.app.PendingIntent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.view.InsetsFrameProvider;
/**
* State shared across different taskbar instance
*/
public class TaskbarSharedState {
+ private final IBinder mInsetsOwner = new Binder();
+ private static int INDEX_LEFT = 0;
+ private static int INDEX_RIGHT = 1;
+
// TaskbarManager#onSystemUiFlagsChanged
public int sysuiStateFlags;
@@ -48,4 +61,33 @@ public class TaskbarSharedState {
// Taskbar System Action
public PendingIntent taskbarSystemActionPendingIntent;
+
+ public final InsetsFrameProvider[] insetsFrameProviders = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(mInsetsOwner, 0, navigationBars()),
+ new InsetsFrameProvider(mInsetsOwner, 0, tappableElement()),
+ new InsetsFrameProvider(mInsetsOwner, 0, mandatorySystemGestures()),
+ new InsetsFrameProvider(mInsetsOwner, INDEX_LEFT, systemGestures())
+ .setSource(SOURCE_DISPLAY),
+ new InsetsFrameProvider(mInsetsOwner, INDEX_RIGHT, systemGestures())
+ .setSource(SOURCE_DISPLAY)
+ };
+
+ // Allows us to shift translation logic when doing taskbar pinning animation.
+ public boolean startTaskbarVariantIsTransient = true;
+
+ // To track if taskbar was pinned using taskbar pinning feature at the time of recreate,
+ // so we can unstash transient taskbar when we un-pinning taskbar.
+ private boolean mTaskbarWasPinned = false;
+
+ public boolean getTaskbarWasPinned() {
+ return mTaskbarWasPinned;
+ }
+
+ public void setTaskbarWasPinned(boolean taskbarWasPinned) {
+ mTaskbarWasPinned = taskbarWasPinned;
+ }
+
+ // To track if taskbar was stashed / unstashed between configuration changes (which recreates
+ // the task bar).
+ public Boolean taskbarWasStashedAuto = true;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
index e8c8fc49fc..bfbecf3f77 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarShortcutMenuAccessibilityDelegate.java
@@ -34,6 +34,7 @@ import com.android.launcher3.R;
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ShortcutUtil;
@@ -84,9 +85,9 @@ public class TaskbarShortcutMenuAccessibilityDelegate
@Override
protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
- if (item instanceof WorkspaceItemInfo
+ if (item instanceof ItemInfoWithIcon
&& (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
- WorkspaceItemInfo info = (WorkspaceItemInfo) item;
+ ItemInfoWithIcon info = (ItemInfoWithIcon) item;
int side = action == MOVE_TO_TOP_OR_LEFT
? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -97,10 +98,11 @@ public class TaskbarShortcutMenuAccessibilityDelegate
.withInstanceId(instanceIds.second)
.log(getLogEventForPosition(side));
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && item instanceof WorkspaceItemInfo) {
SystemUiProxy.INSTANCE.get(mContext).startShortcut(
info.getIntent().getPackage(),
- info.getDeepShortcutId(),
+ ((WorkspaceItemInfo) info).getDeepShortcutId(),
side,
/* bundleOpts= */ null,
info.user,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index a920dfaf6a..9aaa80f74c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -15,24 +15,21 @@
*/
package com.android.launcher3.taskbar;
-import static android.view.HapticFeedbackConstants.LONG_PRESS;
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.FINAL_FRAME;
import static com.android.app.animation.Interpolators.INSTANT;
import static com.android.app.animation.Interpolators.LINEAR;
-import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW;
import static com.android.launcher3.taskbar.TaskbarKeyguardController.MASK_ANY_SYSUI_LOCKED;
-import static com.android.launcher3.taskbar.TaskbarManager.SYSTEM_ACTION_ID_TASKBAR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
+import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE;
@@ -43,7 +40,6 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.app.RemoteAction;
-import android.content.SharedPreferences;
import android.graphics.drawable.Icon;
import android.os.SystemClock;
import android.util.Log;
@@ -61,9 +57,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.launcher3.Alarm;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.util.DisplayController;
@@ -85,26 +79,26 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private static final boolean DEBUG = false;
public static final int FLAG_IN_APP = 1 << 0;
- public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
- public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 2; // shade open, ...
- public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 3; // setup wizard and AllSetActivity
- public static final int FLAG_STASHED_IN_APP_IME = 1 << 4; // IME is visible
- public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 5;
- public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 6; // All apps is visible.
- public static final int FLAG_IN_SETUP = 1 << 7; // In the Setup Wizard
- public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 8; // phone screen gesture nav, stashed
- public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 9; // Autohide (transient taskbar).
- public static final int FLAG_STASHED_SYSUI = 1 << 10; // app pinning,...
- public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 11; // device is locked: keyguard, ...
+ public static final int FLAG_STASHED_IN_APP_SYSUI = 1 << 1; // shade open, ...
+ public static final int FLAG_STASHED_IN_APP_SETUP = 1 << 2; // setup wizard and AllSetActivity
+ public static final int FLAG_STASHED_IN_APP_IME = 1 << 3; // IME is visible
+ public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 4;
+ public static final int FLAG_STASHED_IN_TASKBAR_ALL_APPS = 1 << 5; // All apps is visible.
+ public static final int FLAG_IN_SETUP = 1 << 6; // In the Setup Wizard
+ public static final int FLAG_STASHED_SMALL_SCREEN = 1 << 7; // phone screen gesture nav, stashed
+ public static final int FLAG_STASHED_IN_APP_AUTO = 1 << 8; // Autohide (transient taskbar).
+ public static final int FLAG_STASHED_SYSUI = 1 << 9; // app pinning,...
+ public static final int FLAG_STASHED_DEVICE_LOCKED = 1 << 10; // device is locked: keyguard, ...
+ public static final int FLAG_IN_OVERVIEW = 1 << 11; // launcher is in overview
// If any of these flags are enabled, isInApp should return true.
private static final int FLAGS_IN_APP = FLAG_IN_APP | FLAG_IN_SETUP;
// If we're in an app and any of these flags are enabled, taskbar should be stashed.
- private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
- | FLAG_STASHED_IN_APP_SYSUI | FLAG_STASHED_IN_APP_SETUP
- | FLAG_STASHED_IN_APP_IME | FLAG_STASHED_IN_TASKBAR_ALL_APPS
- | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
+ private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_SYSUI
+ | FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_APP_IME
+ | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN
+ | FLAG_STASHED_IN_APP_AUTO;
// If any of these flags are enabled, inset apps by our stashed height instead of our unstashed
// height. This way the reported insets are consistent even during transitions out of the app.
@@ -165,21 +159,11 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private static final long TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY = 66;
/**
- * The scale that TaskbarView animates to when hinting towards the stashed state.
- */
- private static final float STASHED_TASKBAR_HINT_SCALE = 0.9f;
-
- /**
* The scale that the stashed handle animates to when hinting towards the unstashed state.
*/
private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f;
/**
- * The SharedPreferences key for whether user has manually stashed the taskbar.
- */
- private static final String SHARED_PREFS_STASHED_KEY = "taskbar_is_stashed";
-
- /**
* Whether taskbar should be stashed out of the box.
*/
private static final boolean DEFAULT_STASHED_PREF = false;
@@ -222,7 +206,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private @interface StashAnimation {}
private final TaskbarActivityContext mActivity;
- private final SharedPreferences mPrefs;
private final int mStashedHeight;
private final int mUnstashedHeight;
private final SystemUiProxy mSystemUiProxy;
@@ -251,8 +234,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private boolean mIsImeShowing;
private boolean mIsImeSwitcherShowing;
- private boolean mEnableManualStashingDuringTests = false;
-
private final Alarm mTimeoutAlarm = new Alarm();
private boolean mEnableBlockingTimeoutDuringTests = false;
@@ -272,12 +253,18 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
public TaskbarStashController(TaskbarActivityContext activity) {
mActivity = activity;
- mPrefs = LauncherPrefs.getPrefs(mActivity);
mSystemUiProxy = SystemUiProxy.INSTANCE.get(activity);
mAccessibilityManager = mActivity.getSystemService(AccessibilityManager.class);
- mUnstashedHeight = mActivity.getDeviceProfile().taskbarHeight;
- mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarHeight;
+ if (isPhoneMode()) {
+ mUnstashedHeight = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_phone_size);
+ mStashedHeight = mActivity.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_stashed_size);
+ } else {
+ mUnstashedHeight = mActivity.getDeviceProfile().taskbarHeight;
+ mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarHeight;
+ }
}
/**
@@ -320,16 +307,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
- // We use supportsVisualStashing() here instead of supportsManualStashing() because we want
- // it to work properly for tests that recreate taskbar. This check is here just to ensure
- // that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
- boolean isManuallyStashedInApp = supportsVisualStashing()
- && !isTransientTaskbar
- && !ENABLE_TASKBAR_PINNING.get()
- && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
- updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
- updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isTransientTaskbar);
+ boolean isStashedInAppAuto =
+ isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned();
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
+ isStashedInAppAuto = isStashedInAppAuto && mTaskbarSharedState.taskbarWasStashedAuto;
+ }
+ updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, isStashedInAppAuto);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup);
updateStateForFlag(FLAG_IN_SETUP, isInSetup);
updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode()
@@ -338,6 +322,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
// us that we're paused until a bit later. This avoids flickering upon recreating taskbar.
updateStateForFlag(FLAG_IN_APP, true);
applyState(/* duration = */ 0);
+ if (mTaskbarSharedState.getTaskbarWasPinned()
+ || !mTaskbarSharedState.taskbarWasStashedAuto) {
+ tryStartTaskbarTimeout();
+ }
notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp());
}
@@ -350,28 +338,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
/**
- * Returns whether the user can manually stash the taskbar based on the current device state.
- */
- protected boolean supportsManualStashing() {
- if (ENABLE_TASKBAR_PINNING.get() && mPrefs.getBoolean(TASKBAR_PINNING_KEY, false)) {
- return false;
- }
- return supportsVisualStashing()
- && isInApp()
- && (!Utilities.isRunningInTestHarness() || mEnableManualStashingDuringTests)
- && !DisplayController.isTransientTaskbar(mActivity);
- }
-
- /**
- * Enables support for manual stashing. This should only be used to add this functionality
- * to Launcher specific tests.
- */
- @VisibleForTesting
- public void enableManualStashingDuringTests(boolean enableManualStashing) {
- mEnableManualStashingDuringTests = enableManualStashing;
- }
-
- /**
* Enables the auto timeout for taskbar stashing. This method should only be used for taskbar
* testing.
*/
@@ -532,6 +498,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO) != stash) {
+ mTaskbarSharedState.taskbarWasStashedAuto = stash;
updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, stash);
applyState();
}
@@ -565,53 +532,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
/* shouldBubblesFollow= */ !bubbleBarExpanded);
}
- /**
- * Should be called when long pressing the nav region when taskbar is present.
- * @return Whether taskbar was stashed and now is unstashed.
- */
- public boolean onLongPressToUnstashTaskbar() {
- if (!isStashed()) {
- // We only listen for long press on the nav region to unstash the taskbar. To stash the
- // taskbar, we use an OnLongClickListener on TaskbarView instead.
- return false;
- }
- if (!canCurrentlyManuallyUnstash()) {
- return false;
- }
- if (updateAndAnimateIsManuallyStashedInApp(false)) {
- mControllers.taskbarActivityContext.getDragLayer().performHapticFeedback(LONG_PRESS);
- return true;
- }
- return false;
- }
-
- /**
- * Returns whether taskbar will unstash when long pressing it based on the current state. The
- * only time this is true is if the user is in an app and the taskbar is only stashed because
- * the user previously long pressed to manually stash (not due to other reasons like IME).
- */
- private boolean canCurrentlyManuallyUnstash() {
- return (mState & (FLAG_IN_APP | FLAGS_STASHED_IN_APP))
- == (FLAG_IN_APP | FLAG_STASHED_IN_APP_MANUAL);
- }
-
- /**
- * Updates whether we should stash the taskbar when in apps, and animates to the changed state.
- * @return Whether we started an animation to either be newly stashed or unstashed.
- */
- public boolean updateAndAnimateIsManuallyStashedInApp(boolean isManuallyStashedInApp) {
- if (!supportsManualStashing()) {
- return false;
- }
- if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL) != isManuallyStashedInApp) {
- mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, isManuallyStashedInApp).apply();
- updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
- applyState();
- return true;
- }
- return false;
- }
-
/** Toggles the Taskbar's stash state. */
public void toggleTaskbarStash() {
if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return;
@@ -898,21 +818,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
});
}
- /**
- * Creates and starts a partial stash animation, hinting at the new state that will trigger when
- * long press is detected.
- * @param animateForward Whether we are going towards the new stashed state or returning to the
- * unstashed state.
- */
- public void startStashHint(boolean animateForward) {
- if (isStashed() || !supportsManualStashing()) {
- // Already stashed, no need to hint in that direction.
- return;
- }
- mIconScaleForStash.animateToValue(
- animateForward ? STASHED_TASKBAR_HINT_SCALE : 1)
- .setDuration(TASKBAR_HINT_STASH_DURATION).start();
- }
/**
* Creates and starts a partial unstash animation, hinting at the new state that will trigger
@@ -920,19 +825,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
*
* @param animateForward Whether we are going towards the new unstashed state or returning to
* the stashed state.
- * @param forceUnstash Whether we force the unstash hint to animate.
*/
- protected void startUnstashHint(boolean animateForward, boolean forceUnstash) {
+ protected void startUnstashHint(boolean animateForward) {
if (!isStashed()) {
// Already unstashed, no need to hint in that direction.
return;
}
- // TODO(b/270395798): Clean up after removing long-press unstashing code path.
- if (!canCurrentlyManuallyUnstash() && !forceUnstash) {
- // If any other flags are causing us to be stashed, long press won't cause us to
- // unstash, so don't hint that it will.
- return;
- }
mTaskbarStashedHandleHintScale.animateToValue(
animateForward ? UNSTASHED_TASKBAR_HANDLE_HINT_SCALE : 1)
.setDuration(TASKBAR_HINT_STASH_DURATION).start();
@@ -1015,9 +913,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
updateStateForFlag(FLAG_STASHED_IN_APP_SYSUI, hasAnyFlag(systemUiStateFlags,
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE));
- updateStateForFlag(FLAG_STASHED_SYSUI,
- hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
+ boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
+ && hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
+ && DisplayController.isTransientTaskbar(mActivity);
+ updateStateForFlag(FLAG_STASHED_SYSUI,
+ hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles);
boolean isLocked = hasAnyFlag(systemUiStateFlags, MASK_ANY_SYSUI_LOCKED)
&& !hasAnyFlag(systemUiStateFlags, SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY);
updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED, isLocked);
@@ -1040,10 +941,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* * in small screen AND
* * 3 button nav AND
* * landscape (or seascape)
- * We do not stash if taskbar is transient
+ * We do not stash if taskbar is transient or hardware keyboard is active.
*/
private boolean shouldStashForIme() {
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (DisplayController.isTransientTaskbar(mActivity) || mActivity.isHardwareKeyboard()) {
return false;
}
return (mIsImeShowing || mIsImeSwitcherShowing) &&
@@ -1082,13 +983,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
mControllers.taskbarAutohideSuspendController.updateFlag(
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_IN_LAUNCHER, !isInApp());
}
- if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_MANUAL)) {
- if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL)) {
- mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_HIDE);
- } else {
- mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW);
- }
- }
if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_AUTO)) {
mActivity.getStatsLogManager().logger().log(hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)
? LAUNCHER_TRANSIENT_TASKBAR_HIDE
@@ -1212,7 +1106,6 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
private static String getStateString(int flags) {
StringJoiner sj = new StringJoiner("|");
appendFlag(sj, flags, FLAGS_IN_APP, "FLAG_IN_APP");
- appendFlag(sj, flags, FLAG_STASHED_IN_APP_MANUAL, "FLAG_STASHED_IN_APP_MANUAL");
appendFlag(sj, flags, FLAG_STASHED_IN_APP_SYSUI, "FLAG_STASHED_IN_APP_SYSUI");
appendFlag(sj, flags, FLAG_STASHED_IN_APP_SETUP, "FLAG_STASHED_IN_APP_SETUP");
appendFlag(sj, flags, FLAG_STASHED_IN_APP_IME, "FLAG_STASHED_IN_APP_IME");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 6fad655687..df2a43b170 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -30,6 +30,7 @@ import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -43,7 +44,6 @@ import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import java.io.PrintWriter;
-import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
@@ -156,7 +156,7 @@ public class TaskbarUIController {
mControllers.taskbarActivityContext.startTranslationSpring();
}
- /*
+ /**
* @param ev MotionEvent in screen coordinates.
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
*/
@@ -165,6 +165,14 @@ public class TaskbarUIController {
|| mControllers.navbarButtonsViewController.isEventOverAnyItem(ev);
}
+ /** Checks if the given {@link MotionEvent} is over the bubble bar stash handle. */
+ public boolean isEventOverBubbleBarStashHandle(MotionEvent ev) {
+ return mControllers.bubbleControllers.map(
+ bubbleControllers ->
+ bubbleControllers.bubbleStashController.isEventOverStashHandle(ev))
+ .orElse(false);
+ }
+
/**
* Returns true if icons should be aligned to hotseat in the current transition.
*/
@@ -209,6 +217,7 @@ public class TaskbarUIController {
recentsView.getSplitSelectController().findLastActiveTasksAndRunCallback(
Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()),
+ false /* findExactPairMatch */,
foundTasks -> {
@Nullable Task foundTask = foundTasks.get(0);
splitSelectSource.alreadyRunningTaskId = foundTask == null
@@ -227,6 +236,7 @@ public class TaskbarUIController {
RecentsView recents = getRecentsView();
recents.getSplitSelectController().findLastActiveTasksAndRunCallback(
Collections.singletonList(info.getComponentKey()),
+ false /* findExactPairMatch */,
foundTasks -> {
@Nullable Task foundTask = foundTasks.get(0);
if (foundTask != null) {
@@ -290,11 +300,9 @@ public class TaskbarUIController {
}
/**
- * Launches the focused task in splitscreen.
- *
- * No-op if the view is not yet open.
+ * Launches the given task in split-screen.
*/
- public void launchSplitTasks(@NonNull View taskview, @NonNull GroupTask groupTask) { }
+ public void launchSplitTasks(@NonNull GroupTask groupTask) { }
/**
* Returns the matching view (if any) in the taskbar.
@@ -323,6 +331,14 @@ public class TaskbarUIController {
}
/**
+ * Callback for when launcher state transition completes after user swipes to home.
+ * @param finalState The final state of the transition.
+ */
+ public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) {
+ // Overridden
+ }
+
+ /**
* Refreshes the resumed state of this ui controller.
*/
public void refreshResumedState() {}
@@ -336,4 +352,7 @@ public class TaskbarUIController {
.stream()
.map(mControllers.taskbarPopupController::createSplitShortcutFactory);
}
+
+ /** Adjusts the hotseat for the bubble bar. */
+ public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index fa5a1ae6fa..2ab00665ed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -18,7 +18,11 @@ package com.android.launcher3.taskbar;
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES;
+import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import android.content.Context;
@@ -27,12 +31,15 @@ import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.view.InputDevice;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import androidx.annotation.DimenRes;
+import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -42,7 +49,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.FolderInfo;
@@ -90,11 +97,9 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
// Only non-null when device supports having an All Apps button.
private @Nullable IconButtonView mTaskbarDivider;
- private View mQsb;
+ private final View mQsb;
- private float mTransientTaskbarMinWidth;
-
- private float mTransientTaskbarAllAppsButtonTranslationXOffset;
+ private final float mTransientTaskbarMinWidth;
private boolean mShouldTryStartAlign;
@@ -120,17 +125,17 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
&& !TaskbarManager.isPhoneMode(mActivityContext.getDeviceProfile());
mIsRtl = Utilities.isRtl(resources);
- mTransientTaskbarMinWidth = mContext.getResources().getDimension(
- R.dimen.transient_taskbar_min_width);
- mTransientTaskbarAllAppsButtonTranslationXOffset =
- resources.getDimension(isTransientTaskbar
- ? R.dimen.transient_taskbar_all_apps_button_translation_x_offset
- : R.dimen.taskbar_all_apps_button_translation_x_offset);
+ mTransientTaskbarMinWidth = resources.getDimension(R.dimen.transient_taskbar_min_width);
+
onDeviceProfileChanged(mActivityContext.getDeviceProfile());
int actualMargin = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
int actualIconSize = mActivityContext.getDeviceProfile().taskbarIconSize;
+ if (enableTaskbarPinning()) {
+ DeviceProfile deviceProfile = mActivityContext.getTransientTaskbarDeviceProfile();
+ actualIconSize = deviceProfile.taskbarIconSize;
+ }
int visualIconSize = (int) (actualIconSize * ICON_VISIBLE_AREA_FACTOR);
mIconTouchSize = Math.max(actualIconSize,
@@ -138,8 +143,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
// We layout the icons to be of mIconTouchSize in width and height
mItemMarginLeftRight = actualMargin - (mIconTouchSize - visualIconSize) / 2;
- mItemPadding = (mIconTouchSize - actualIconSize) / 2;
+ // We always layout taskbar as a transient taskbar when we have taskbar pinning feature on,
+ // then we scale and translate the icons to match persistent taskbar designs, so we use
+ // taskbar icon size from current device profile to calculate correct item padding.
+ mItemPadding = (mIconTouchSize - mActivityContext.getDeviceProfile().taskbarIconSize) / 2;
mFolderLeaveBehindColor = Themes.getAttrColor(mActivityContext,
android.R.attr.textColorTertiary);
@@ -149,15 +157,13 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
if (!mActivityContext.getPackageManager().hasSystemFeature(FEATURE_PC)) {
mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
.inflate(R.layout.taskbar_all_apps_button, this, false);
- mAllAppsButton.setIconDrawable(resources.getDrawable(isTransientTaskbar
- ? R.drawable.ic_transient_taskbar_all_apps_button
- : R.drawable.ic_taskbar_all_apps_button));
- mAllAppsButton.setScaleX(mIsRtl ? -1 : 1);
+ mAllAppsButton.setIconDrawable(resources.getDrawable(
+ getAllAppsButton(isTransientTaskbar)));
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
mAllAppsButton.setForegroundTint(
mActivityContext.getColor(R.color.all_apps_button_color));
- if (FeatureFlags.ENABLE_TASKBAR_PINNING.get()) {
+ if (enableTaskbarPinning()) {
mTaskbarDivider = (IconButtonView) LayoutInflater.from(context).inflate(
R.layout.taskbar_divider,
this, false);
@@ -171,6 +177,42 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
}
+ @DrawableRes
+ private int getAllAppsButton(boolean isTransientTaskbar) {
+ boolean shouldSelectTransientIcon =
+ (isTransientTaskbar || enableTaskbarPinning())
+ && !mActivityContext.isThreeButtonNav();
+ if (ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
+ return shouldSelectTransientIcon
+ ? R.drawable.ic_transient_taskbar_all_apps_search_button
+ : R.drawable.ic_taskbar_all_apps_search_button;
+ } else {
+ return shouldSelectTransientIcon
+ ? R.drawable.ic_transient_taskbar_all_apps_button
+ : R.drawable.ic_taskbar_all_apps_button;
+ }
+ }
+
+ @DimenRes
+ public int getAllAppsButtonTranslationXOffset(boolean isTransientTaskbar) {
+ if (isTransientTaskbar) {
+ return R.dimen.transient_taskbar_all_apps_button_translation_x_offset;
+ } else {
+ return ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()
+ ? R.dimen.taskbar_all_apps_search_button_translation_x_offset
+ : R.dimen.taskbar_all_apps_button_translation_x_offset;
+ }
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ boolean changed = getVisibility() != visibility;
+ super.setVisibility(visibility);
+ if (changed && mControllerCallbacks != null) {
+ mControllerCallbacks.notifyVisibilityChanged();
+ }
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -222,14 +264,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
mIconClickListener = mControllerCallbacks.getIconOnClickListener();
mIconLongClickListener = mControllerCallbacks.getIconOnLongClickListener();
- setOnLongClickListener(mControllerCallbacks.getBackgroundOnLongClickListener());
-
if (mAllAppsButton != null) {
mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
}
if (mTaskbarDivider != null) {
mTaskbarDivider.setOnLongClickListener(
mControllerCallbacks.getTaskbarDividerLongClickListener());
+ mTaskbarDivider.setOnTouchListener(
+ mControllerCallbacks.getTaskbarDividerRightClickListener());
}
}
@@ -268,12 +310,14 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
// Replace any Hotseat views with the appropriate type if it's not already that type.
final int expectedLayoutResId;
- boolean isFolder = false;
+ boolean isCollection = false;
if (hotseatItemInfo.isPredictedItem()) {
expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
- } else if (hotseatItemInfo instanceof FolderInfo) {
- expectedLayoutResId = R.layout.folder_icon;
- isFolder = true;
+ } else if (hotseatItemInfo instanceof FolderInfo fi) {
+ expectedLayoutResId = fi.itemType == ITEM_TYPE_APP_PAIR
+ ? R.layout.app_pair_icon
+ : R.layout.folder_icon;
+ isCollection = true;
} else {
expectedLayoutResId = R.layout.taskbar_app_icon;
}
@@ -284,7 +328,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
// see if the view can be reused
if ((hotseatView.getSourceLayoutResId() != expectedLayoutResId)
- || (isFolder && (hotseatView.getTag() != hotseatItemInfo))) {
+ || (isCollection && (hotseatView.getTag() != hotseatItemInfo))) {
// Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation,
// so if the info changes we need to reinflate. This should only happen if a new
// folder is dragged to the position that another folder previously existed.
@@ -297,12 +341,23 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
if (hotseatView == null) {
- if (isFolder) {
+ if (isCollection) {
FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
- FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
- mActivityContext, this, folderInfo);
- folderIcon.setTextVisible(false);
- hotseatView = folderIcon;
+ switch (hotseatItemInfo.itemType) {
+ case ITEM_TYPE_FOLDER:
+ hotseatView = FolderIcon.inflateFolderAndIcon(
+ expectedLayoutResId, mActivityContext, this, folderInfo);
+ ((FolderIcon) hotseatView).setTextVisible(false);
+ break;
+ case ITEM_TYPE_APP_PAIR:
+ hotseatView = AppPairIcon.inflateIcon(
+ expectedLayoutResId, mActivityContext, this, folderInfo);
+ ((AppPairIcon) hotseatView).setTextVisible(false);
+ break;
+ default:
+ throw new IllegalStateException(
+ "Unexpected item type: " + hotseatItemInfo.itemType);
+ }
} else {
hotseatView = inflate(expectedLayoutResId);
}
@@ -324,7 +379,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
}
setClickAndLongClickListenersForIcon(hotseatView);
- if (ENABLE_CURSOR_HOVER_STATES.get()) {
+ if (enableCursorHoverStates()) {
setHoverListenerForIcon(hotseatView);
}
nextViewIndex++;
@@ -335,8 +390,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
if (mAllAppsButton != null) {
- mAllAppsButton.setTranslationXForTaskbarAllAppsIcon(getChildCount() > 0
- ? mTransientTaskbarAllAppsButtonTranslationXOffset : 0f);
addView(mAllAppsButton, mIsRtl ? getChildCount() : 0);
// if only all apps button present, don't include divider view.
@@ -372,6 +425,16 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
public void setClickAndLongClickListenersForIcon(View icon) {
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
+ // Add right-click support to btv icons.
+ icon.setOnTouchListener((v, event) -> {
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)
+ && (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0
+ && v instanceof BubbleTextView) {
+ mActivityContext.showPopupMenuForIcon((BubbleTextView) v);
+ return true;
+ }
+ return false;
+ });
}
/**
@@ -464,24 +527,6 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
}
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) {
- // Don't allow long pressing between icons, or above/below them.
- return true;
- }
- if (mControllerCallbacks.onTouchEvent(event)) {
- int oldAction = event.getAction();
- try {
- event.setAction(MotionEvent.ACTION_CANCEL);
- return super.onTouchEvent(event);
- } finally {
- event.setAction(oldAction);
- }
- }
- return super.onTouchEvent(event);
- }
-
/**
* Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
* touch bounds.
@@ -509,7 +554,7 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
int iconLayoutBoundsWidth =
countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
- if (FeatureFlags.ENABLE_TASKBAR_PINNING.get() && countExcludingQsb > 1) {
+ if (enableTaskbarPinning() && countExcludingQsb > 1) {
// We are removing 4 * mItemMarginLeftRight as there should be no space between
// All Apps icon, divider icon, and first app icon in taskbar
iconLayoutBoundsWidth -= mItemMarginLeftRight * 4;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 4614e8ddc7..c0cbd45cc1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -21,14 +21,19 @@ import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.Utilities.mapRange;
import static com.android.launcher3.anim.AnimatedFloat.VALUE;
import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
+import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_PERSISTENT;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneButtonNavMode;
import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM;
+import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_PINNING_ANIM;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM;
import android.animation.Animator;
@@ -38,6 +43,7 @@ import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.Log;
+import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
@@ -47,6 +53,7 @@ import androidx.core.graphics.ColorUtils;
import androidx.core.view.OneShotPreDrawListener;
import com.android.app.animation.Interpolators;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -61,11 +68,13 @@ import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiTranslateDelegate;
import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.views.IconButtonView;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -98,12 +107,27 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
this::updateTranslationY);
private final AnimatedFloat mTaskbarIconTranslationYForStash = new AnimatedFloat(
this::updateTranslationY);
+
+ private final AnimatedFloat mTaskbarIconScaleForPinning = new AnimatedFloat(
+ this::updateTaskbarIconsScale);
+
+ private final AnimatedFloat mTaskbarIconTranslationXForPinning = new AnimatedFloat(
+ this::updateTaskbarIconTranslationXForPinning);
+
+ private final AnimatedFloat mTaskbarIconTranslationYForPinning = new AnimatedFloat(
+ this::updateTranslationY);
+
+ private final View.OnLayoutChangeListener mTaskbarViewLayoutChangeListener =
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
+ -> updateTaskbarIconTranslationXForPinning();
+
+
private AnimatedFloat mTaskbarNavButtonTranslationY;
private AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay;
private float mTaskbarIconTranslationYForSwipe;
private float mTaskbarIconTranslationYForSpringOnStash;
- private final int mTaskbarBottomMargin;
+ private int mTaskbarBottomMargin;
private final int mStashedHandleHeight;
private final int mLauncherThemedIconsBackgroundColor;
private final int mTaskbarThemedIconsBackgroundColor;
@@ -131,8 +155,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private final boolean mIsRtl;
+ private final DeviceProfile mTransientTaskbarDp;
+ private final DeviceProfile mPersistentTaskbarDp;
+
+ private final int mTransientIconSize;
+ private final int mPersistentIconSize;
+
public TaskbarViewController(TaskbarActivityContext activity, TaskbarView taskbarView) {
mActivity = activity;
+ mTransientTaskbarDp = mActivity.getTransientTaskbarDeviceProfile();
+ mPersistentTaskbarDp = mActivity.getPersistentTaskbarDeviceProfile();
+ mTransientIconSize = mTransientTaskbarDp.taskbarIconSize;
+ mPersistentIconSize = mPersistentTaskbarDp.taskbarIconSize;
mTaskbarView = taskbarView;
mTaskbarIconAlpha = new MultiValueAlpha(mTaskbarView, NUM_ALPHA_CHANNELS);
mTaskbarIconAlpha.setUpdateVisibility(true);
@@ -158,10 +192,16 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
mControllers = controllers;
mTaskbarView.init(new TaskbarViewCallbacks());
mTaskbarView.getLayoutParams().height = isPhoneMode(mActivity.getDeviceProfile())
- ? mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_size)
+ ? mActivity.getResources().getDimensionPixelSize(R.dimen.taskbar_phone_size)
: mActivity.getDeviceProfile().taskbarHeight;
mTaskbarIconScaleForStash.updateValue(1f);
+ float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+ ? PINNING_TRANSIENT
+ : PINNING_PERSISTENT;
+ mTaskbarIconScaleForPinning.updateValue(pinningValue);
+ mTaskbarIconTranslationYForPinning.updateValue(pinningValue);
+ mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
mModelCallbacks.init(controllers);
if (mActivity.isUserSetupComplete()) {
@@ -175,12 +215,15 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
- if (TaskbarManager.FLAG_HIDE_NAVBAR_WINDOW) {
+ if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
// This gets modified in NavbarButtonsViewController, but the initial value it reads
// may be incorrect since it's state gets destroyed on taskbar recreate, so reset here
mTaskbarIconAlpha.get(ALPHA_INDEX_SMALL_SCREEN)
.animateToValue(isPhoneButtonNavMode(mActivity) ? 0 : 1).start();
}
+ if (enableTaskbarPinning()) {
+ mTaskbarView.addOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
+ }
}
/**
@@ -191,6 +234,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
public void onDestroy() {
+ if (enableTaskbarPinning()) {
+ mTaskbarView.removeOnLayoutChangeListener(mTaskbarViewLayoutChangeListener);
+ }
LauncherAppState.getInstance(mActivity).getModel().removeCallbacks(mModelCallbacks);
mActivity.removeOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
mModelCallbacks.unregisterListeners();
@@ -253,6 +299,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
return mTaskbarIconTranslationYForStash;
}
+ public AnimatedFloat getTaskbarIconScaleForPinning() {
+ return mTaskbarIconScaleForPinning;
+ }
+
+ public AnimatedFloat getTaskbarIconTranslationXForPinning() {
+ return mTaskbarIconTranslationXForPinning;
+ }
+
+ public AnimatedFloat getTaskbarIconTranslationYForPinning() {
+ return mTaskbarIconTranslationYForPinning;
+ }
+
/**
* Applies scale properties for the entire TaskbarView (rather than individual icons).
*/
@@ -263,6 +321,80 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
/**
+ * Applies scale properties for the taskbar icons
+ */
+ private void updateTaskbarIconsScale() {
+ float scale = mTaskbarIconScaleForPinning.value;
+ View[] iconViews = mTaskbarView.getIconViews();
+
+ float finalScale;
+ if (mControllers.getSharedState().startTaskbarVariantIsTransient) {
+ finalScale = mapRange(scale, 1f, ((float) mPersistentIconSize / mTransientIconSize));
+ } else {
+ finalScale = mapRange(scale, ((float) mTransientIconSize / mPersistentIconSize), 1f);
+ }
+
+ for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
+ iconViews[iconIndex].setScaleX(finalScale);
+ iconViews[iconIndex].setScaleY(finalScale);
+ }
+ }
+
+ /**
+ * Animate away taskbar icon notification dots during the taskbar pinning animation.
+ */
+ public void animateAwayNotificationDotsDuringTaskbarPinningAnimation() {
+ for (View iconView : mTaskbarView.getIconViews()) {
+ if (iconView instanceof BubbleTextView && ((BubbleTextView) iconView).hasDot()) {
+ ((BubbleTextView) iconView).animateDotScale(0);
+ }
+ }
+ }
+
+ private void updateTaskbarIconTranslationXForPinning() {
+ View[] iconViews = mTaskbarView.getIconViews();
+ float scale = mTaskbarIconTranslationXForPinning.value;
+ float taskbarCenterX =
+ mTaskbarView.getLeft() + (mTaskbarView.getRight() - mTaskbarView.getLeft()) / 2.0f;
+
+ float finalMarginScale = mapRange(scale, 0f, mTransientIconSize - mPersistentIconSize);
+
+ float transientTaskbarAllAppsOffset = mActivity.getResources().getDimension(
+ mTaskbarView.getAllAppsButtonTranslationXOffset(true));
+ float persistentTaskbarAllAppsOffset = mActivity.getResources().getDimension(
+ mTaskbarView.getAllAppsButtonTranslationXOffset(false));
+
+ float allAppIconTranslateRange = mapRange(scale, transientTaskbarAllAppsOffset,
+ persistentTaskbarAllAppsOffset);
+
+ if (mIsRtl) {
+ allAppIconTranslateRange *= -1;
+ }
+
+ float halfIconCount = iconViews.length / 2.0f;
+ for (int iconIndex = 0; iconIndex < iconViews.length; iconIndex++) {
+ View iconView = iconViews[iconIndex];
+ MultiTranslateDelegate translateDelegate =
+ ((Reorderable) iconView).getTranslateDelegate();
+ float iconCenterX =
+ iconView.getLeft() + (iconView.getRight() - iconView.getLeft()) / 2.0f;
+ if (iconCenterX <= taskbarCenterX) {
+ translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
+ finalMarginScale * (halfIconCount - iconIndex));
+ } else {
+ translateDelegate.getTranslationX(INDEX_TASKBAR_PINNING_ANIM).setValue(
+ -finalMarginScale * (iconIndex - halfIconCount));
+ }
+
+ if (iconView.equals(mTaskbarView.getAllAppsButtonView()) && iconViews.length > 1) {
+ ((IconButtonView) iconView).setTranslationXForTaskbarAllAppsIcon(
+ allAppIconTranslateRange);
+ }
+ }
+ }
+
+
+ /**
* Sets the translation of the TaskbarView during the swipe up gesture.
*/
public void setTranslationYForSwipe(float transY) {
@@ -282,10 +414,41 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
mTaskbarView.setTranslationY(mTaskbarIconTranslationYForHome.value
+ mTaskbarIconTranslationYForStash.value
+ mTaskbarIconTranslationYForSwipe
+ + getTaskbarIconTranslationYForPinningValue()
+ mTaskbarIconTranslationYForSpringOnStash);
}
/**
+ * Computes translation y for taskbar pinning.
+ */
+ private float getTaskbarIconTranslationYForPinningValue() {
+ if (mControllers.getSharedState() == null) return 0f;
+
+ float scale = mTaskbarIconTranslationYForPinning.value;
+ float taskbarIconTranslationYForPinningValue;
+
+ // transY is calculated here by adding/subtracting the taskbar bottom margin
+ // aligning the icon bound to be at bottom of current taskbar view and then
+ // finally placing the icon in the middle of new taskbar background height.
+ if (mControllers.getSharedState().startTaskbarVariantIsTransient) {
+ float transY =
+ mTransientTaskbarDp.taskbarBottomMargin + (mTransientTaskbarDp.taskbarHeight
+ - mTaskbarView.getIconLayoutBounds().bottom)
+ - (mPersistentTaskbarDp.taskbarHeight
+ - mTransientTaskbarDp.taskbarIconSize) / 2f;
+ taskbarIconTranslationYForPinningValue = mapRange(scale, 0f, transY);
+ } else {
+ float transY =
+ -mTransientTaskbarDp.taskbarBottomMargin + (mPersistentTaskbarDp.taskbarHeight
+ - mTaskbarView.getIconLayoutBounds().bottom)
+ - (mTransientTaskbarDp.taskbarHeight
+ - mTransientTaskbarDp.taskbarIconSize) / 2f;
+ taskbarIconTranslationYForPinningValue = mapRange(scale, transY, 0f);
+ }
+ return taskbarIconTranslationYForPinningValue;
+ }
+
+ /**
* Updates the Taskbar's themed icons background according to the progress between in-app/home.
*/
protected void updateIconsBackground() {
@@ -435,6 +598,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
* 1 => fully aligned
*/
public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
+ if (isPhoneMode(launcherDp)) {
+ mIconAlignControllerLazy = null;
+ return;
+ }
+
boolean isHotseatIconOnTopWhenAligned =
mControllers.uiController.isHotseatIconOnTopWhenAligned();
boolean isStashed = mControllers.taskbarStashController.isStashed();
@@ -453,19 +621,21 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
}
+ /** Resets the icon alignment controller so that it can be recreated again later. */
+ void resetIconAlignmentController() {
+ mIconAlignControllerLazy = null;
+ }
+
/**
* Creates an animation for aligning the Taskbar icons with the provided Launcher device profile
*/
private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) {
PendingAnimation setter = new PendingAnimation(100);
- if (TaskbarManager.isPhoneButtonNavMode(mActivity)) {
- // No animation for icons in small-screen
- return setter.createPlaybackController();
- }
-
mOnControllerPreCreateCallback.run();
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+
float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize;
int borderSpacing = launcherDp.hotseatBorderSpace;
int hotseatCellSize = DeviceProfile.calculateCellWidth(
@@ -492,6 +662,10 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
+ mTaskbarBottomMargin = isTransientTaskbar
+ ? mTransientTaskbarDp.taskbarBottomMargin
+ : mPersistentTaskbarDp.taskbarBottomMargin;
+
for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
View child = mTaskbarView.getChildAt(i);
boolean isAllAppsButton = child == mTaskbarView.getAllAppsButtonView();
@@ -502,7 +676,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
// to avoid icons disappearing rather than fading out visually.
setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
} else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())
- || (isTaskbarDividerView && FeatureFlags.ENABLE_TASKBAR_PINNING.get())) {
+ || (isTaskbarDividerView && enableTaskbarPinning())) {
if (!isToHome
&& mIsHotseatIconOnTopWhenAligned
&& mIsStashed) {
@@ -523,6 +697,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
+ launcherDp.hotseatQsbWidth / 2f
: hotseatPadding.left - borderSpacing - launcherDp.hotseatQsbWidth / 2f;
float childCenter = (child.getLeft() + child.getRight()) / 2f;
+ childCenter += ((Reorderable) child).getTranslateDelegate().getTranslationX(
+ INDEX_TASKBAR_PINNING_ANIM).getValue();
float halfQsbIconWidthDiff =
(launcherDp.hotseatQsbWidth - taskbarDp.taskbarIconSize) / 2f;
float scale = ((float) taskbarDp.taskbarIconSize)
@@ -555,13 +731,19 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
continue;
}
- int positionInHotseat;
+ float positionInHotseat;
if (isAllAppsButton) {
- // Note that there is no All Apps button in the hotseat, this position is only used
- // as its convenient for animation purposes.
+ // Note that there is no All Apps button in the hotseat,
+ // this position is only used as its convenient for animation purposes.
positionInHotseat = Utilities.isRtl(child.getResources())
? taskbarDp.numShownHotseatIcons
: -1;
+ } else if (isTaskbarDividerView) {
+ // Note that there is no taskbar divider view in the hotseat,
+ // this position is only used as its convenient for animation purposes.
+ positionInHotseat = Utilities.isRtl(child.getResources())
+ ? taskbarDp.numShownHotseatIcons - 0.5f
+ : -0.5f;
} else if (child.getTag() instanceof ItemInfo) {
positionInHotseat = ((ItemInfo) child.getTag()).screenId;
} else {
@@ -569,14 +751,24 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
continue;
}
- float hotseatIconCenter = hotseatPadding.left
- + (hotseatCellSize + borderSpacing) * positionInHotseat
- + hotseatCellSize / 2f;
+ float hotseatAdjustedBorderSpace =
+ launcherDp.getHotseatAdjustedBorderSpaceForBubbleBar(child.getContext());
+ float hotseatIconCenter;
+ if (bubbleBarHasBubbles() && hotseatAdjustedBorderSpace != 0) {
+ hotseatIconCenter = hotseatPadding.left + hotseatCellSize
+ + (hotseatCellSize + hotseatAdjustedBorderSpace) * positionInHotseat
+ + hotseatCellSize / 2f;
+ } else {
+ hotseatIconCenter = hotseatPadding.left
+ + (hotseatCellSize + borderSpacing) * positionInHotseat
+ + hotseatCellSize / 2f;
+ }
float childCenter = (child.getLeft() + child.getRight()) / 2f;
+ childCenter += ((Reorderable) child).getTranslateDelegate().getTranslationX(
+ INDEX_TASKBAR_PINNING_ANIM).getValue();
float toX = hotseatIconCenter - childCenter;
if (child instanceof Reorderable) {
MultiTranslateDelegate mtd = ((Reorderable) child).getTranslateDelegate();
-
setter.setFloat(mtd.getTranslationX(INDEX_TASKBAR_ALIGNMENT_ANIM),
MULTI_PROPERTY_VALUE, toX, interpolator);
setter.setFloat(mtd.getTranslationY(INDEX_TASKBAR_ALIGNMENT_ANIM),
@@ -593,6 +785,11 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
return controller;
}
+ private boolean bubbleBarHasBubbles() {
+ return mControllers.bubbleControllers.isPresent()
+ && mControllers.bubbleControllers.get().bubbleBarViewController.hasBubbles();
+ }
+
public void onRotationChanged(DeviceProfile deviceProfile) {
if (!mControllers.uiController.isIconAlignedWithHotseat()) {
// We only translate on rotation when icon is aligned with hotseat
@@ -686,13 +883,19 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
};
}
- public View.OnLongClickListener getIconOnLongClickListener() {
- return mControllers.taskbarDragController::startDragOnLongClick;
+ public View.OnTouchListener getTaskbarDividerRightClickListener() {
+ return (v, event) -> {
+ if (event.isFromSource(InputDevice.SOURCE_MOUSE)
+ && event.getButtonState() == MotionEvent.BUTTON_SECONDARY) {
+ mControllers.taskbarPinningController.showPinningView(v);
+ return true;
+ }
+ return false;
+ };
}
- public View.OnLongClickListener getBackgroundOnLongClickListener() {
- return view -> mControllers.taskbarStashController
- .updateAndAnimateIsManuallyStashedInApp(true);
+ public View.OnLongClickListener getIconOnLongClickListener() {
+ return mControllers.taskbarDragController::startDragOnLongClick;
}
/** Gets the hover listener for the provided icon view. */
@@ -701,46 +904,18 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
}
/**
- * Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to
- * consume the touch so TaskbarView treats it as an ACTION_CANCEL.
- * TODO(b/270395798): We can remove this entirely once we remove the Transient Taskbar flag.
+ * Notifies launcher to update icon alignment.
*/
- public boolean onTouchEvent(MotionEvent motionEvent) {
- final float x = motionEvent.getRawX();
- final float y = motionEvent.getRawY();
- switch (motionEvent.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mDownX = x;
- mDownY = y;
- mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
- mCanceledStashHint = false;
- break;
- case MotionEvent.ACTION_MOVE:
- if (!mCanceledStashHint
- && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
- mControllers.taskbarStashController.startStashHint(
- /* animateForward= */ false);
- mCanceledStashHint = true;
- return true;
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (!mCanceledStashHint) {
- mControllers.taskbarStashController.startStashHint(
- /* animateForward= */ false);
- }
- break;
- }
-
- return false;
+ public void notifyIconLayoutBoundsChanged() {
+ mControllers.uiController.onIconLayoutBoundsChanged();
}
/**
- * Notifies launcher to update icon alignment.
+ * Notifies the taskbar scrim when the visibility of taskbar changes.
*/
- public void notifyIconLayoutBoundsChanged() {
- mControllers.uiController.onIconLayoutBoundsChanged();
+ public void notifyVisibilityChanged() {
+ mControllers.taskbarScrimViewController.onTaskbarVisibilityChanged(
+ mTaskbarView.getVisibility());
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index d786d94149..07d86e4e78 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -89,7 +89,8 @@ public final class TaskbarAllAppsController {
mAppsModelFlags = flags;
mPackageUserKeytoUidMap = map;
if (mAppsView != null) {
- mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap);
+ mAppsView.getAppsStore().setApps(
+ mApps, mAppsModelFlags, mPackageUserKeytoUidMap, false);
}
}
@@ -128,10 +129,19 @@ public final class TaskbarAllAppsController {
/** Toggles visibility of {@link TaskbarAllAppsContainerView} in the overlay window. */
public void toggle() {
+ toggle(false);
+ }
+
+ /** Toggles visibility of {@link TaskbarAllAppsContainerView} with the keyboard for search. */
+ public void toggleSearch() {
+ toggle(true);
+ }
+
+ private void toggle(boolean showKeyboard) {
if (isOpen()) {
mSlideInView.close(true);
} else {
- show(true);
+ show(true, showKeyboard);
}
}
@@ -141,13 +151,13 @@ public final class TaskbarAllAppsController {
}
private void show(boolean animate) {
+ show(animate, false);
+ }
+
+ private void show(boolean animate, boolean showKeyboard) {
if (mAppsView != null) {
return;
}
- // mControllers and getSharedState should never be null here. Do not handle null-pointer
- // to catch invalid states.
- mControllers.getSharedState().allAppsVisible = true;
-
mOverlayContext = mControllers.taskbarOverlayController.requestWindow();
// Initialize search session for All Apps.
@@ -161,16 +171,20 @@ public final class TaskbarAllAppsController {
mSlideInView = (TaskbarAllAppsSlideInView) mOverlayContext.getLayoutInflater().inflate(
R.layout.taskbar_all_apps_sheet, mOverlayContext.getDragLayer(), false);
- mSlideInView.addOnCloseListener(() -> {
- mControllers.getSharedState().allAppsVisible = false;
- cleanUpOverlay();
- });
+ // Ensures All Apps gets touch events in case it is not the top floating view. Floating
+ // views above it may not be able to intercept the touch, so All Apps should try to.
+ mOverlayContext.getDragLayer().addTouchController(mSlideInView);
+ mSlideInView.addOnCloseListener(this::cleanUpOverlay);
TaskbarAllAppsViewController viewController = new TaskbarAllAppsViewController(
- mOverlayContext, mSlideInView, mControllers, mSearchSessionController);
+ mOverlayContext,
+ mSlideInView,
+ mControllers,
+ mSearchSessionController,
+ showKeyboard);
viewController.show(animate);
mAppsView = mOverlayContext.getAppsView();
- mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap);
+ mAppsView.getAppsStore().setApps(mApps, mAppsModelFlags, mPackageUserKeytoUidMap, false);
mAppsView.getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class)
.setPredictedApps(mPredictedApps);
@@ -195,6 +209,7 @@ public final class TaskbarAllAppsController {
mSearchSessionController = null;
}
if (mOverlayContext != null) {
+ mOverlayContext.getDragLayer().removeTouchController(mSlideInView);
mOverlayContext.setSearchSessionController(null);
mOverlayContext = null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 001c3bcfc0..964d329066 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -220,7 +220,9 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = !mAppsView.shouldContainerScroll(ev);
+ mNoIntercept = !mAppsView.shouldContainerScroll(ev)
+ || getTopOpenViewWithType(
+ mActivityContext, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null;
}
return super.onControllerInterceptTouchEvent(ev);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 85633e9037..b1c515177b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -18,17 +18,22 @@ package com.android.launcher3.taskbar.allapps;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_STASHED_IN_TASKBAR_ALL_APPS;
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.allapps.AllAppsTransitionListener;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.appprediction.AppsDividerView;
import com.android.launcher3.taskbar.NavbarButtonsViewController;
import com.android.launcher3.taskbar.TaskbarControllers;
+import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
import com.android.launcher3.util.DisplayController;
+import java.util.Optional;
+
/**
* Handles the {@link TaskbarAllAppsContainerView} behavior and synchronizes its transitions with
* taskbar stashing.
@@ -41,12 +46,15 @@ final class TaskbarAllAppsViewController {
private final TaskbarStashController mTaskbarStashController;
private final NavbarButtonsViewController mNavbarButtonsViewController;
private final TaskbarOverlayController mOverlayController;
+ private final @Nullable TaskbarSharedState mTaskbarSharedState;
+ private final boolean mShowKeyboard;
TaskbarAllAppsViewController(
TaskbarOverlayContext context,
TaskbarAllAppsSlideInView slideInView,
TaskbarControllers taskbarControllers,
- TaskbarSearchSessionController searchSessionController) {
+ TaskbarSearchSessionController searchSessionController,
+ boolean showKeyboard) {
mContext = context;
mSlideInView = slideInView;
@@ -54,6 +62,8 @@ final class TaskbarAllAppsViewController {
mTaskbarStashController = taskbarControllers.taskbarStashController;
mNavbarButtonsViewController = taskbarControllers.navbarButtonsViewController;
mOverlayController = taskbarControllers.taskbarOverlayController;
+ mTaskbarSharedState = taskbarControllers.getSharedState();
+ mShowKeyboard = showKeyboard;
mSlideInView.init(new TaskbarAllAppsCallbacks(searchSessionController));
setUpAppDivider();
@@ -73,26 +83,27 @@ final class TaskbarAllAppsViewController {
private void setUpAppDivider() {
mAppsView.getFloatingHeaderView()
.findFixedRowByType(AppsDividerView.class)
- .setShowAllAppsLabel(!mContext.getOnboardingPrefs().hasReachedMaxCount(
- ALL_APPS_VISITED_COUNT));
- mContext.getOnboardingPrefs().incrementEventCount(ALL_APPS_VISITED_COUNT);
+ .setShowAllAppsLabel(!ALL_APPS_VISITED_COUNT.hasReachedMax(mContext));
+ ALL_APPS_VISITED_COUNT.increment(mContext);
}
private void setUpTaskbarStashing() {
if (DisplayController.isTransientTaskbar(mContext)) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
- mTaskbarStashController.applyState(mOverlayController.getOpenDuration());
+ mTaskbarStashController.applyState();
}
+ Optional.ofNullable(mTaskbarSharedState).ifPresent(s -> s.allAppsVisible = true);
mNavbarButtonsViewController.setSlideInViewVisible(true);
mSlideInView.setOnCloseBeginListener(() -> {
+ Optional.ofNullable(mTaskbarSharedState).ifPresent(s -> s.allAppsVisible = false);
mNavbarButtonsViewController.setSlideInViewVisible(false);
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
if (DisplayController.isTransientTaskbar(mContext)) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
- mTaskbarStashController.applyState(mOverlayController.getCloseDuration());
+ mTaskbarStashController.applyState();
}
});
}
@@ -120,6 +131,11 @@ final class TaskbarAllAppsViewController {
@Override
public void onAllAppsTransitionEnd(boolean toAllApps) {
mSearchSessionController.onAllAppsTransitionEnd(toAllApps);
+ if (toAllApps
+ && mShowKeyboard
+ && mAppsView.getSearchUiManager().getEditText() != null) {
+ mAppsView.getSearchUiManager().getEditText().requestFocus();
+ }
}
/** Invoked on back press, returning {@code true} if the search session handled it. */
@@ -128,7 +144,7 @@ final class TaskbarAllAppsViewController {
}
void onAllAppsAnimationPending(PendingAnimation animation, boolean toAllApps) {
- mSearchSessionController.onAllAppsAnimationPending(animation, toAllApps);
+ mSearchSessionController.onAllAppsAnimationPending(animation, toAllApps, mShowKeyboard);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
index 8a2041fc34..3d15fbdebf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarSearchSessionController.kt
@@ -51,7 +51,11 @@ open class TaskbarSearchSessionController : ResourceBasedOverride, AllAppsTransi
open fun handleBackInvoked(): Boolean = false
- open fun onAllAppsAnimationPending(animation: PendingAnimation, toAllApps: Boolean) = Unit
+ open fun onAllAppsAnimationPending(
+ animation: PendingAnimation,
+ toAllApps: Boolean,
+ showKeyboard: Boolean,
+ ) = Unit
companion object {
@JvmStatic
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 24db380553..3fb7247d58 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -85,17 +85,31 @@ import java.util.concurrent.Executors;
* information to render each of the bubbles & dispatches changes to
* {@link BubbleBarViewController} which will then update {@link BubbleBarView} as needed.
*
- * For details around the behavior of the bubble bar, see {@link BubbleBarView}.
+ * <p>For details around the behavior of the bubble bar, see {@link BubbleBarView}.
*/
public class BubbleBarController extends IBubblesListener.Stub {
private static final String TAG = BubbleBarController.class.getSimpleName();
private static final boolean DEBUG = false;
- // Whether bubbles are showing in the bubble bar from launcher
- public static final boolean BUBBLE_BAR_ENABLED =
+ /**
+ * Determines whether bubbles can be shown in the bubble bar. This value updates when the
+ * taskbar is recreated.
+ *
+ * @see #onTaskbarRecreated()
+ */
+ private static boolean sBubbleBarEnabled =
SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+ /** Whether showing bubbles in the launcher bubble bar is enabled. */
+ public static boolean isBubbleBarEnabled() {
+ return sBubbleBarEnabled;
+ }
+
+ /** Re-reads the value of the flag from SystemProperties when taskbar is recreated. */
+ public static void onTaskbarRecreated() {
+ sBubbleBarEnabled = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
+ }
private static final int MASK_HIDE_BUBBLE_BAR = SYSUI_STATE_BOUNCER_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
@@ -137,6 +151,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
private static class BubbleBarViewUpdate {
boolean expandedChanged;
boolean expanded;
+ boolean shouldShowEducation;
String selectedBubbleKey;
String suppressedBubbleKey;
String unsuppressedBubbleKey;
@@ -151,6 +166,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
BubbleBarViewUpdate(BubbleBarUpdate update) {
expandedChanged = update.expandedChanged;
expanded = update.expanded;
+ shouldShowEducation = update.shouldShowEducation;
selectedBubbleKey = update.selectedBubbleKey;
suppressedBubbleKey = update.suppressedBubbleKey;
unsuppressedBubbleKey = update.unsupressedBubbleKey;
@@ -165,7 +181,7 @@ public class BubbleBarController extends IBubblesListener.Stub {
mSystemUiProxy = SystemUiProxy.INSTANCE.get(context);
- if (BUBBLE_BAR_ENABLED) {
+ if (sBubbleBarEnabled) {
mSystemUiProxy.setBubblesListener(this);
}
mMainExecutor = MAIN_EXECUTOR;
@@ -188,8 +204,10 @@ public class BubbleBarController extends IBubblesListener.Stub {
mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
bubbleControllers.runAfterInit(() -> {
- mBubbleBarViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
- mBubbleStashedHandleViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
+ mBubbleBarViewController.setHiddenForBubbles(
+ !sBubbleBarEnabled || mBubbles.isEmpty());
+ mBubbleStashedHandleViewController.setHiddenForBubbles(
+ !sBubbleBarEnabled || mBubbles.isEmpty());
mBubbleBarViewController.setUpdateSelectedBubbleAfterCollapse(
key -> setSelectedBubble(mBubbles.get(key)));
});
@@ -366,7 +384,9 @@ public class BubbleBarController extends IBubblesListener.Stub {
mBubbleStashController.animateToInitialState(update.expanded);
}
}
-
+ if (update.shouldShowEducation) {
+ mBubbleBarViewController.prepareToShowEducation();
+ }
if (update.expandedChanged) {
if (update.expanded != mBubbleBarViewController.isExpanded()) {
mBubbleBarViewController.setExpandedFromSysui(update.expanded);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 20b8e3b708..065dd58f16 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -18,6 +18,7 @@ package com.android.launcher3.taskbar.bubbles;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.Log;
import android.view.MotionEvent;
@@ -74,7 +75,8 @@ public class BubbleBarViewController {
// Whether the bar is hidden for a sysui state.
private boolean mHiddenForSysui;
// Whether the bar is hidden because there are no bubbles.
- private boolean mHiddenForNoBubbles;
+ private boolean mHiddenForNoBubbles = true;
+ private boolean mShouldShowEducation;
public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) {
mActivity = activity;
@@ -98,7 +100,7 @@ public class BubbleBarViewController {
mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight;
mBubbleBarScale.updateValue(1f);
mBubbleClickListener = v -> onBubbleClicked(v);
- mBubbleBarClickListener = v -> setExpanded(true);
+ mBubbleBarClickListener = v -> onBubbleBarClicked();
mBubbleDragController.setupBubbleBarView(mBarView);
mBarView.setOnClickListener(mBubbleBarClickListener);
mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
@@ -121,6 +123,21 @@ public class BubbleBarViewController {
}
}
+ private void onBubbleBarClicked() {
+ if (mShouldShowEducation) {
+ mShouldShowEducation = false;
+ // Get the bubble bar bounds on screen
+ Rect bounds = new Rect();
+ mBarView.getBoundsOnScreen(bounds);
+ // Calculate user education reference position in Screen coordinates
+ Point position = new Point(bounds.centerX(), bounds.top);
+ // Show user education relative to the reference point
+ mSystemUiProxy.showUserEducation(position);
+ } else {
+ setExpanded(true);
+ }
+ }
+
//
// The below animators are exposed to BubbleStashController so it can manage the stashing
// animation.
@@ -195,6 +212,7 @@ public class BubbleBarViewController {
if (mHiddenForNoBubbles != hidden) {
mHiddenForNoBubbles = hidden;
updateVisibilityForStateChange();
+ mActivity.bubbleBarVisibilityChanged(!hidden);
}
}
@@ -326,6 +344,12 @@ public class BubbleBarViewController {
}
}
+ /** Marks as should show education and shows the bubble bar in a collapsed state */
+ public void prepareToShowEducation() {
+ mShouldShowEducation = true;
+ mBubbleStashController.showBubbleBar(false /* expand the bubbles */);
+ }
+
/**
* Updates the dragged bubble view in the bubble bar view, and notifies SystemUI
* that a bubble is being dragged to dismiss.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
index 4b235a97d2..6c3f0d8796 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissViewExt.kt
@@ -30,6 +30,7 @@ import com.android.wm.shell.common.bubbles.DismissView
fun DismissView.setup() {
setup(
DismissView.Config(
+ dismissViewResId = R.id.dismiss_view,
targetSizeResId = R.dimen.bubblebar_dismiss_target_size,
iconSizeResId = R.dimen.bubblebar_dismiss_target_icon_size,
bottomMarginResId = R.dimen.bubblebar_dismiss_target_bottom_margin,
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index a5ea5a9955..09021edb91 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.Nullable;
import android.view.InsetsController;
+import android.view.MotionEvent;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.StashedHandleViewController;
@@ -350,4 +351,9 @@ public class BubbleStashController {
return mBubblesShowingOnHome ? getBubbleBarTranslationYForHotseat()
: getBubbleBarTranslationYForTaskbar();
}
+
+ /** Checks whether the motion event is over the stash handle. */
+ public boolean isEventOverStashHandle(MotionEvent ev) {
+ return mHandleViewController.isEventOverHandle(ev);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index fbab59531c..c998d9704e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -24,6 +24,7 @@ import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
@@ -268,4 +269,22 @@ public class BubbleStashedHandleViewController {
});
return revealAnim;
}
+
+ /** Checks that the stash handle is visible and that the motion event is within bounds. */
+ public boolean isEventOverHandle(MotionEvent ev) {
+ if (mStashedHandleView.getVisibility() != VISIBLE) {
+ return false;
+ }
+
+ // the bounds of the handle only include the visible part, so we check that the Y coordinate
+ // is anywhere within the stashed taskbar height.
+ int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight;
+
+ return (int) ev.getRawY() >= top && containsX((int) ev.getRawX());
+ }
+
+ /** Checks if the given x coordinate is within the stashed handle bounds. */
+ public boolean containsX(int x) {
+ return x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 12cb8c5353..6549ad6846 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -163,6 +163,7 @@ public class BubbleView extends ConstraintLayout {
mBubble = overflow;
mBubbleIcon.setImageBitmap(bitmap);
mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge
+ setContentDescription(getResources().getString(R.string.bubble_bar_overflow_description));
}
/** Returns the bubble being rendered in this view. */
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
index b6820817c8..b516d6febe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/AbstractNavButtonLayoutter.kt
@@ -18,12 +18,15 @@ package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
import android.graphics.drawable.RotateDrawable
+import android.view.Gravity
import android.view.ViewGroup
+import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+import com.android.systemui.shared.rotation.RotationButton
/**
* Meant to be a simple container for data subclasses will need
@@ -37,10 +40,13 @@ import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonL
* @property startContextualContainer ViewGroup that holds the start contextual button (ex, A11y).
*/
abstract class AbstractNavButtonLayoutter(
- val resources: Resources,
- val navButtonContainer: LinearLayout,
- protected val endContextualContainer: ViewGroup,
- protected val startContextualContainer: ViewGroup
+ val resources: Resources,
+ val navButtonContainer: LinearLayout,
+ protected val endContextualContainer: ViewGroup,
+ protected val startContextualContainer: ViewGroup,
+ protected val imeSwitcher: ImageView?,
+ protected val rotationButton: RotationButton?,
+ protected val a11yButton: ImageView?
) : NavButtonLayoutter {
protected val homeButton: ImageView? = navButtonContainer.findViewById(R.id.home)
protected val recentsButton: ImageView? = navButtonContainer.findViewById(R.id.recent_apps)
@@ -56,4 +62,25 @@ abstract class AbstractNavButtonLayoutter(
backButton.setImageDrawable(rotateDrawable)
}
}
+
+ fun getParamsToCenterView(): FrameLayout.LayoutParams {
+ val params = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ params.gravity = Gravity.CENTER
+ return params;
+ }
+
+ open fun repositionContextualContainer(contextualContainer: ViewGroup, barAxisMargin: Int,
+ gravity: Int) {
+ val contextualContainerParams = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ contextualContainerParams.apply {
+ marginStart = barAxisMargin
+ marginEnd = barAxisMargin
+ topMargin = 0
+ bottomMargin = 0
+ }
+ contextualContainerParams.gravity = gravity or Gravity.CENTER_VERTICAL
+ contextualContainer.layoutParams = contextualContainerParams
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
index 4a53c0c3b1..3f51958402 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/KidsNavLayoutter.kt
@@ -25,22 +25,30 @@ import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.*
+import com.android.systemui.shared.rotation.RotationButton
class KidsNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ override fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean) {
val iconSize: Int = resources.getDimensionPixelSize(DIMEN_TASKBAR_ICON_SIZE_KIDS)
val buttonWidth: Int = resources.getDimensionPixelSize(DIMEN_TASKBAR_NAV_BUTTONS_WIDTH_KIDS)
val buttonHeight: Int =
@@ -89,5 +97,25 @@ class KidsNavLayoutter(
navButtonContainer.requestLayout()
homeButton.onLongClickListener = null
+
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
+
+ val contextualMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_padding)
+ repositionContextualContainer(endContextualContainer, 0, Gravity.END)
+ repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.START)
+
+ if (imeSwitcher != null) {
+ startContextualContainer.addView(imeSwitcher)
+ imeSwitcher.layoutParams = getParamsToCenterView()
+ }
+ if (a11yButton != null) {
+ endContextualContainer.addView(a11yButton)
+ }
+ if (rotationButton != null) {
+ endContextualContainer.addView(rotationButton.currentView)
+ rotationButton.currentView.layoutParams = getParamsToCenterView()
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
index 931f692fa2..6b05e9a891 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NavButtonLayoutFactory.kt
@@ -21,11 +21,13 @@ import android.view.Surface.ROTATION_90
import android.view.Surface.Rotation
import android.view.ViewGroup
import android.widget.FrameLayout
+import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
import com.android.launcher3.taskbar.navbutton.LayoutResourceHelper.*
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.Companion
import com.android.launcher3.taskbar.navbutton.NavButtonLayoutFactory.NavButtonLayoutter
+import com.android.systemui.shared.rotation.RotationButton
/**
* Select the correct layout for nav buttons
@@ -52,14 +54,17 @@ class NavButtonLayoutFactory {
* @param isThreeButtonNav are no-ops when taskbar is present/showing
*/
fun getUiLayoutter(
- deviceProfile: DeviceProfile,
- navButtonsView: FrameLayout,
- resources: Resources,
- isKidsMode: Boolean,
- isInSetup: Boolean,
- isThreeButtonNav: Boolean,
- phoneMode: Boolean,
- @Rotation surfaceRotation: Int
+ deviceProfile: DeviceProfile,
+ navButtonsView: FrameLayout,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?,
+ resources: Resources,
+ isKidsMode: Boolean,
+ isInSetup: Boolean,
+ isThreeButtonNav: Boolean,
+ phoneMode: Boolean,
+ @Rotation surfaceRotation: Int
): NavButtonLayoutter {
val navButtonContainer =
navButtonsView.requireViewById<LinearLayout>(ID_END_NAV_BUTTONS)
@@ -73,24 +78,33 @@ class NavButtonLayoutFactory {
isPhoneNavMode -> {
if (!deviceProfile.isLandscape) {
PhonePortraitNavLayoutter(
- resources,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
} else if (surfaceRotation == ROTATION_90) {
PhoneLandscapeNavLayoutter(
- resources,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
} else {
PhoneSeascapeNavLayoutter(
resources,
navButtonContainer,
endContextualContainer,
- startContextualContainer
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
}
}
@@ -99,33 +113,45 @@ class NavButtonLayoutFactory {
resources,
navButtonContainer,
endContextualContainer,
- startContextualContainer
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
}
deviceProfile.isTaskbarPresent -> {
return when {
isInSetup -> {
SetupNavLayoutter(
- resources,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
}
isKidsMode -> {
KidsNavLayoutter(
- resources,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
}
else ->
TaskbarNavLayoutter(
- resources,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
)
}
}
@@ -136,6 +162,6 @@ class NavButtonLayoutFactory {
/** Lays out and provides access to the home, recents, and back buttons for various mischief */
interface NavButtonLayoutter {
- fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean)
+ fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
index 8525c6c90c..5a7bc4969c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneGestureLayoutter.kt
@@ -18,24 +18,33 @@ package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
import android.view.ViewGroup
+import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
+import com.android.systemui.shared.rotation.RotationButton
/** Layoutter for showing gesture navigation on phone screen. No buttons here, no-op container */
class PhoneGestureLayoutter(
resources: Resources,
navBarContainer: LinearLayout,
endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?
) :
AbstractNavButtonLayoutter(
resources,
navBarContainer,
endContextualContainer,
- startContextualContainer
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
- // no-op
+ override fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean) {
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
index 2acd5d4eff..382e29832d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneLandscapeNavLayoutter.kt
@@ -20,27 +20,35 @@ import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
+import android.widget.ImageView
import android.widget.LinearLayout
import androidx.core.view.children
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.util.DimensionUtils
+import com.android.systemui.shared.rotation.RotationButton
open class PhoneLandscapeNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?,
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ override fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean) {
// TODO(b/230395757): Polish pending, this is just to make it usable
val endStartMargins = resources.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
val taskbarDimensions = DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
@@ -57,14 +65,11 @@ open class PhoneLandscapeNavLayoutter(
marginStart = 0
}
- // Swap recents and back button
- navButtonContainer.addView(recentsButton)
- navButtonContainer.addView(homeButton)
- navButtonContainer.addView(backButton)
-
navButtonContainer.layoutParams = navContainerParams
navButtonContainer.gravity = Gravity.CENTER
+ addThreeButtons()
+
// Add the spaces in between the nav buttons
val spaceInBetween: Int =
resources.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween_phone)
@@ -84,5 +89,49 @@ open class PhoneLandscapeNavLayoutter(
}
}
}
+
+ repositionContextualButtons()
+ }
+
+ open fun addThreeButtons() {
+ // Swap recents and back button
+ navButtonContainer.addView(recentsButton)
+ navButtonContainer.addView(homeButton)
+ navButtonContainer.addView(backButton)
+ }
+
+ open fun repositionContextualButtons() {
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
+
+ val contextualMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_padding)
+ repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.TOP)
+
+ if (imeSwitcher != null) {
+ startContextualContainer.addView(imeSwitcher)
+ imeSwitcher.layoutParams = getParamsToCenterView()
+ }
+ if (a11yButton != null) {
+ startContextualContainer.addView(a11yButton)
+ }
+ if (rotationButton != null) {
+ startContextualContainer.addView(rotationButton.currentView)
+ rotationButton.currentView.layoutParams = getParamsToCenterView()
+ }
+ }
+
+ override fun repositionContextualContainer(contextualContainer: ViewGroup, barAxisMargin: Int,
+ gravity: Int) {
+ val contextualContainerParams = FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
+ contextualContainerParams.apply {
+ marginStart = 0
+ marginEnd = 0
+ topMargin = barAxisMargin
+ bottomMargin = barAxisMargin
+ }
+ contextualContainerParams.gravity = gravity or Gravity.CENTER_HORIZONTAL
+ contextualContainer.layoutParams = contextualContainerParams
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
index c76311582d..e1ffa4dabf 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhonePortraitNavLayoutter.kt
@@ -20,26 +20,34 @@ import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
+import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
import com.android.launcher3.taskbar.TaskbarManager
import com.android.launcher3.util.DimensionUtils
+import com.android.systemui.shared.rotation.RotationButton
class PhonePortraitNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?,
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ override fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean) {
// TODO(b/230395757): Polish pending, this is just to make it usable
val taskbarDimensions =
DimensionUtils.getTaskbarPhoneDimensions(dp, resources,
@@ -90,5 +98,24 @@ class PhonePortraitNavLayoutter(
}
}
}
+
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
+
+ val contextualMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_padding)
+ repositionContextualContainer(endContextualContainer, contextualMargin, Gravity.END)
+
+ if (imeSwitcher != null) {
+ endContextualContainer.addView(imeSwitcher)
+ imeSwitcher.layoutParams = getParamsToCenterView()
+ }
+ if (a11yButton != null) {
+ endContextualContainer.addView(a11yButton)
+ }
+ if (rotationButton != null) {
+ endContextualContainer.addView(rotationButton.currentView)
+ rotationButton.currentView.layoutParams = getParamsToCenterView()
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
index f0fe58197b..0368b1dea6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/PhoneSeascapeNavLayoutter.kt
@@ -17,30 +17,57 @@
package com.android.launcher3.taskbar.navbutton
import android.content.res.Resources
+import android.view.Gravity
import android.view.ViewGroup
+import android.widget.ImageView
import android.widget.LinearLayout
-import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.systemui.shared.rotation.RotationButton
class PhoneSeascapeNavLayoutter(
resources: Resources,
navBarContainer: LinearLayout,
endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?
) :
PhoneLandscapeNavLayoutter(
resources,
navBarContainer,
endContextualContainer,
- startContextualContainer
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
- // TODO(b/230395757): Polish pending, this is just to make it usable
- super.layoutButtons(dp, isContextualButtonShowing)
- navButtonContainer.removeAllViews()
+ override fun addThreeButtons() {
// Flip ordering of back and recents buttons
navButtonContainer.addView(backButton)
navButtonContainer.addView(homeButton)
navButtonContainer.addView(recentsButton)
}
+
+ override fun repositionContextualButtons() {
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
+
+ val contextualMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_padding)
+ repositionContextualContainer(endContextualContainer, contextualMargin, Gravity.BOTTOM)
+
+ if (imeSwitcher != null) {
+ endContextualContainer.addView(imeSwitcher)
+ imeSwitcher.layoutParams = getParamsToCenterView()
+ }
+ if (a11yButton != null) {
+ endContextualContainer.addView(a11yButton)
+ }
+ if (rotationButton != null) {
+ endContextualContainer.addView(rotationButton.currentView)
+ rotationButton.currentView.layoutParams = getParamsToCenterView()
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
index a24002c44d..abdd32c712 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/SetupNavLayoutter.kt
@@ -20,23 +20,32 @@ import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
+import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
+import com.android.launcher3.R
+import com.android.systemui.shared.rotation.RotationButton
class SetupNavLayoutter(
- resources: Resources,
- navButtonContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ resources: Resources,
+ navButtonContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?
) :
AbstractNavButtonLayoutter(
- resources,
- navButtonContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navButtonContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ override fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean) {
// Since setup wizard only has back button enabled, it looks strange to be
// end-aligned, so start-align instead.
val navButtonsLayoutParams = navButtonContainer.layoutParams as FrameLayout.LayoutParams
@@ -46,5 +55,25 @@ class SetupNavLayoutter(
gravity = Gravity.START
}
navButtonContainer.requestLayout()
+
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
+
+ val contextualMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_padding)
+ repositionContextualContainer(endContextualContainer, 0, Gravity.END)
+ repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.START)
+
+ if (imeSwitcher != null) {
+ startContextualContainer.addView(imeSwitcher)
+ imeSwitcher.layoutParams = getParamsToCenterView()
+ }
+ if (a11yButton != null) {
+ endContextualContainer.addView(a11yButton)
+ }
+ if (rotationButton != null) {
+ endContextualContainer.addView(rotationButton.currentView)
+ rotationButton.currentView.layoutParams = getParamsToCenterView()
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 8332b7dd74..f5a4c64fd5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -20,31 +20,41 @@ import android.content.res.Resources
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
+import android.widget.ImageView
import android.widget.LinearLayout
import com.android.launcher3.DeviceProfile
import com.android.launcher3.R
+import com.android.systemui.shared.rotation.RotationButton
-/** Layoutter for showing 3 button navigation on large screen */
+/**
+ * Layoutter for rendering task bar in large screen, both in 3-button and gesture nav mode.
+ */
class TaskbarNavLayoutter(
- resources: Resources,
- navBarContainer: LinearLayout,
- endContextualContainer: ViewGroup,
- startContextualContainer: ViewGroup
+ resources: Resources,
+ navBarContainer: LinearLayout,
+ endContextualContainer: ViewGroup,
+ startContextualContainer: ViewGroup,
+ imeSwitcher: ImageView?,
+ rotationButton: RotationButton?,
+ a11yButton: ImageView?
) :
AbstractNavButtonLayoutter(
- resources,
- navBarContainer,
- endContextualContainer,
- startContextualContainer
+ resources,
+ navBarContainer,
+ endContextualContainer,
+ startContextualContainer,
+ imeSwitcher,
+ rotationButton,
+ a11yButton
) {
- override fun layoutButtons(dp: DeviceProfile, isContextualButtonShowing: Boolean) {
+ override fun layoutButtons(dp: DeviceProfile, isA11yButtonPersistent: Boolean) {
// Add spacing after the end of the last nav button
var navMarginEnd = resources.getDimension(dp.inv.inlineNavButtonsEndSpacing).toInt()
val contextualWidth = endContextualContainer.width
// If contextual buttons are showing, we check if the end margin is enough for the
// contextual button to be showing - if not, move the nav buttons over a smidge
- if (isContextualButtonShowing && navMarginEnd < contextualWidth) {
+ if (isA11yButtonPersistent && navMarginEnd < contextualWidth) {
// Additional spacing, eat up half of space between last icon and nav button
navMarginEnd += resources.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing) / 2
}
@@ -77,5 +87,27 @@ class TaskbarNavLayoutter(
}
}
}
+
+ endContextualContainer.removeAllViews()
+ startContextualContainer.removeAllViews()
+
+ if (!dp.isGestureMode) {
+ val contextualMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_contextual_button_padding)
+ repositionContextualContainer(endContextualContainer, 0, Gravity.END)
+ repositionContextualContainer(startContextualContainer, contextualMargin, Gravity.START)
+
+ if (imeSwitcher != null) {
+ startContextualContainer.addView(imeSwitcher)
+ imeSwitcher.layoutParams = getParamsToCenterView()
+ }
+ if (a11yButton != null) {
+ endContextualContainer.addView(a11yButton)
+ }
+ if (rotationButton != null) {
+ endContextualContainer.addView(rotationButton.currentView)
+ rotationButton.currentView.layoutParams = getParamsToCenterView()
+ }
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index d4e2be9b49..adbec65ad3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar.overlay;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_CONSUME_IME_INSETS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
@@ -23,7 +24,6 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.LauncherState.ALL_APPS;
import android.annotation.SuppressLint;
-import android.content.ComponentName;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
@@ -60,14 +60,26 @@ public final class TaskbarOverlayController {
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
- public void onTaskCreated(int taskId, ComponentName componentName) {
- // Created task will be below existing overlay, so move out of the way.
- hideWindow();
+ public void onTaskMovedToFront(int taskId) {
+ // New front task will be below existing overlay, so move out of the way.
+ hideWindowOnTaskStackChange();
}
@Override
- public void onTaskMovedToFront(int taskId) {
- // New front task will be below existing overlay, so move out of the way.
+ public void onTaskStackChanged() {
+ // The other callbacks are insufficient for All Apps, because there are many cases where
+ // it can relaunch the same task already behind it. However, this callback needs to be a
+ // no-op when only EDU is shown, because going between the EDU steps invokes this
+ // callback.
+ if (mControllers.getSharedState() != null
+ && mControllers.getSharedState().allAppsVisible) {
+ hideWindowOnTaskStackChange();
+ }
+ }
+
+ private void hideWindowOnTaskStackChange() {
+ // A task was launched while overlay window was open, so stash Taskbar.
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
hideWindow();
}
};
@@ -176,6 +188,7 @@ public final class TaskbarOverlayController {
layoutParams.setFitInsetsTypes(0); // Handled by container view.
layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
layoutParams.setSystemApplicationOverlay(true);
+ layoutParams.privateFlags = PRIVATE_FLAG_CONSUME_IME_INSETS;
return layoutParams;
}
@@ -199,8 +212,10 @@ public final class TaskbarOverlayController {
@Override
protected void handleClose(boolean animate) {
- mTaskbarContext.getDragLayer().removeView(this);
- Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+ if (mIsOpen) {
+ mTaskbarContext.getDragLayer().removeView(this);
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> closeAllOpenViews(c, animate));
+ }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index ff00560956..432d272761 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -38,6 +38,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -69,6 +70,7 @@ public class TaskbarOverlayDragLayer extends
return true;
}
};
+ private final List<TouchController> mTouchControllers = new ArrayList<>();
TaskbarOverlayDragLayer(Context context) {
super(context, null, 1);
@@ -93,10 +95,13 @@ public class TaskbarOverlayDragLayer extends
@Override
public void recreateControllers() {
- mControllers = mOnClickListeners.isEmpty()
- ? new TouchController[]{mActivity.getDragController()}
- : new TouchController[] {
- mActivity.getDragController(), mClickListenerTouchController};
+ List<TouchController> controllers = new ArrayList<>();
+ controllers.add(mActivity.getDragController());
+ controllers.addAll(mTouchControllers);
+ if (!mOnClickListeners.isEmpty()) {
+ controllers.add(mClickListenerTouchController);
+ }
+ mControllers = controllers.toArray(new TouchController[0]);
}
@Override
@@ -113,6 +118,15 @@ public class TaskbarOverlayDragLayer extends
topView.onBackInvoked();
return true;
}
+ } else if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE && event.hasNoModifiers()) {
+ // Ignore escape if pressed in conjunction with any modifier keys. Close each
+ // floating view one at a time for each key press.
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ topView.close(/* animate= */ true);
+ return true;
+ }
}
return super.dispatchKeyEvent(event);
}
@@ -183,6 +197,18 @@ public class TaskbarOverlayDragLayer extends
});
}
+ /** Adds a {@link TouchController} to this drag layer. */
+ public void addTouchController(@NonNull TouchController touchController) {
+ mTouchControllers.add(touchController);
+ recreateControllers();
+ }
+
+ /** Removes a {@link TouchController} from this drag layer. */
+ public void removeTouchController(@NonNull TouchController touchController) {
+ mTouchControllers.remove(touchController);
+ recreateControllers();
+ }
+
/**
* Taskbar automatically stashes when opening all apps, but we don't report the insets as
* changing to avoid moving the underlying app. But internally, the apps view should still
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 475f465d0e..4a265592c5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,12 +21,20 @@ import android.app.Person;
import android.content.Context;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherUserInfo;
import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.ColorDrawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
import android.window.RemoteTransition;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.UserIconInfo;
import com.android.quickstep.util.FadeOutRemoteTransition;
+import java.util.List;
import java.util.Map;
/**
@@ -53,4 +61,56 @@ public class ApiWrapper {
options.setRemoteTransition(new RemoteTransition(new FadeOutRemoteTransition()));
return options;
}
+
+ /**
+ * Returns a map of all users on the device to their corresponding UI properties
+ */
+ public static Map<UserHandle, UserIconInfo> queryAllUsers(Context context) {
+ UserManager um = context.getSystemService(UserManager.class);
+ Map<UserHandle, UserIconInfo> users = new ArrayMap<>();
+ List<UserHandle> usersActual = um.getUserProfiles();
+ if (usersActual != null) {
+ for (UserHandle user : usersActual) {
+ if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()) {
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ LauncherUserInfo launcherUserInfo = launcherApps.getLauncherUserInfo(user);
+ // UserTypes not supported in Launcher are deemed to be the current
+ // Foreground User.
+ int userType = switch (launcherUserInfo.getUserType()) {
+ case UserManager.USER_TYPE_PROFILE_MANAGED -> UserIconInfo.TYPE_WORK;
+ case UserManager.USER_TYPE_PROFILE_CLONE -> UserIconInfo.TYPE_CLONED;
+ case UserManager.USER_TYPE_PROFILE_PRIVATE -> UserIconInfo.TYPE_PRIVATE;
+ default -> UserIconInfo.TYPE_MAIN;
+ };
+ long serial = launcherUserInfo.getUserSerialNumber();
+ users.put(user, new UserIconInfo(user, userType, serial));
+ } else {
+ long serial = um.getSerialNumberForUser(user);
+
+ // Simple check to check if the provided user is work profile
+ // TODO: Migrate to a better platform API
+ NoopDrawable d = new NoopDrawable();
+ boolean isWork = (d != context.getPackageManager().getUserBadgedIcon(d, user));
+ UserIconInfo info = new UserIconInfo(
+ user,
+ isWork ? UserIconInfo.TYPE_WORK : UserIconInfo.TYPE_MAIN,
+ serial);
+ users.put(user, info);
+ }
+ }
+ }
+ return users;
+ }
+
+ private static class NoopDrawable extends ColorDrawable {
+ @Override
+ public int getIntrinsicHeight() {
+ return 1;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return 1;
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index d78ca88249..e2f4f322a7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -107,8 +107,7 @@ public abstract class BaseRecentsViewStateController<T extends RecentsView>
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
- boolean exitingOverview = !FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
- && !toState.overviewUi;
+ boolean exitingOverview = !FeatureFlags.enableSplitContextually() && !toState.overviewUi;
if (mRecentsView.isSplitSelectionActive() && exitingOverview) {
setter.add(mRecentsView.getSplitSelectController().getSplitAnimationController()
.createPlaceholderDismissAnim(mLauncher));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 2064fe22f6..110ca167aa 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -44,13 +44,13 @@ import android.view.ViewGroup;
import androidx.core.graphics.ColorUtils;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.celllayout.CellLayoutLayoutParams;
+import com.android.launcher3.celllayout.DelegatedCellDrawing;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -418,7 +418,7 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView {
/**
* Draws Predicted Icon outline on cell layout
*/
- public static class PredictedIconOutlineDrawing extends CellLayout.DelegatedCellDrawing {
+ public static class PredictedIconOutlineDrawing extends DelegatedCellDrawing {
private final PredictedAppIcon mIcon;
private final Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index f5ba8f9cc5..8092582baf 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -27,8 +27,10 @@ import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.widget.RemoteViews;
+import android.widget.Toast;
import android.window.SplashScreen;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.ItemInfo;
@@ -56,8 +58,9 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler {
return RemoteViews.startPendingIntent(hostView, pendingIntent,
remoteResponse.getLaunchOptions(view));
}
- if (mLauncher.getSplitToWorkspaceController().handleSecondWidgetSelectionForSplit(view,
- pendingIntent)) {
+ if (mLauncher.isSplitSelectionEnabled()) {
+ Toast.makeText(hostView.getContext(), R.string.split_widgets_not_supported,
+ Toast.LENGTH_SHORT).show();
return true;
}
Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(view);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 5a46b8d8e9..9438e00b43 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -21,6 +21,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEAS
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
+import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
import static com.android.launcher3.LauncherSettings.Animation.VIEW_BACKGROUND;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
@@ -33,7 +35,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -50,9 +51,10 @@ import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STA
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -61,7 +63,6 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Rect;
@@ -93,6 +94,7 @@ import androidx.annotation.RequiresApi;
import com.android.app.viewcapture.SettingsAwareViewCapture;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.HomeTransitionController;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherState;
@@ -104,8 +106,10 @@ import com.android.launcher3.Workspace;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
@@ -113,7 +117,6 @@ import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.statehandlers.DepthController;
@@ -163,7 +166,6 @@ import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.SplitToWorkspaceController;
import com.android.quickstep.util.SplitWithKeyboardShortcutController;
import com.android.quickstep.util.TISBindHelper;
-import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.FloatingTaskView;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -185,6 +187,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -217,29 +220,40 @@ public class QuickstepLauncher extends Launcher {
private SplitWithKeyboardShortcutController mSplitWithKeyboardShortcutController;
private SplitToWorkspaceController mSplitToWorkspaceController;
- private AsyncClockEventDelegate mAsyncClockEventDelegate;
-
/**
* If Launcher restarted while in the middle of an Overview split select, it needs this data to
* recover. In all other cases this will remain null.
*/
private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
+ @Nullable
+ private DesktopRecentsTransitionController mDesktopRecentsTransitionController;
+
private SafeCloseable mViewCapture;
private boolean mEnableWidgetDepth;
+ private HomeTransitionController mHomeTransitionController;
+
@Override
protected void setupViews() {
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
RecentsView overviewPanel = getOverviewPanel();
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController(), getStatsLogManager(),
- SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this));
- overviewPanel.init(mActionsView, mSplitSelectStateController);
+ systemUiProxy, RecentsModel.INSTANCE.get(this),
+ () -> onStateBack());
+ if (isDesktopModeSupported()) {
+ mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
+ getStateManager(), systemUiProxy, getIApplicationThread(),
+ getDepthController());
+ }
+ overviewPanel.init(mActionsView, mSplitSelectStateController,
+ mDesktopRecentsTransitionController);
mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
mSplitSelectStateController);
mSplitToWorkspaceController = new SplitToWorkspaceController(this,
@@ -251,10 +265,15 @@ public class QuickstepLauncher extends Launcher {
mAppTransitionManager.registerRemoteAnimations();
mAppTransitionManager.registerRemoteTransitions();
+ if (FeatureFlags.enableHomeTransitionListener()) {
+ mHomeTransitionController = new HomeTransitionController(this);
+ mHomeTransitionController.registerHomeTransitionListener();
+ }
+
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mDesktopVisibilityController = new DesktopVisibilityController(this);
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (isDesktopModeSupported()) {
mDesktopVisibilityController.registerSystemUiListener();
mSplitSelectStateController.initSplitFromDesktopController(this);
}
@@ -263,6 +282,7 @@ public class QuickstepLauncher extends Launcher {
mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true);
getWorkspace().addOverlayCallback(progress ->
onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
+ addBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
}
@Override
@@ -332,11 +352,6 @@ public class QuickstepLauncher extends Launcher {
}
@Override
- protected QuickstepOnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
- return new QuickstepOnboardingPrefs(this, sharedPrefs);
- }
-
- @Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onStateOrResumeChanging(false /* inTransition */);
@@ -362,8 +377,8 @@ public class QuickstepLauncher extends Launcher {
}
if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
- if (mTaskbarUIController != null) {
- mTaskbarUIController.onLauncherResumedOrPaused(hasBeenResumed());
+ if (!FeatureFlags.enableHomeTransitionListener() && mTaskbarUIController != null) {
+ mTaskbarUIController.onLauncherVisibilityChanged(hasBeenResumed());
}
}
@@ -464,7 +479,11 @@ public class QuickstepLauncher extends Launcher {
@Override
public void onDestroy() {
- mAppTransitionManager.onActivityDestroyed();
+ if (mAppTransitionManager != null) {
+ mAppTransitionManager.onActivityDestroyed();
+ }
+ mAppTransitionManager = null;
+
if (mUnfoldTransitionProgressProvider != null) {
SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
mUnfoldTransitionProgressProvider.destroy();
@@ -476,6 +495,10 @@ public class QuickstepLauncher extends Launcher {
mLauncherUnfoldAnimationController.onDestroy();
}
+ if (mHomeTransitionController != null) {
+ mHomeTransitionController.unregisterHomeTransitionListener();
+ }
+
if (mDesktopVisibilityController != null) {
mDesktopVisibilityController.unregisterSystemUiListener();
}
@@ -484,14 +507,11 @@ public class QuickstepLauncher extends Launcher {
mSplitSelectStateController.onDestroy();
}
- if (mAsyncClockEventDelegate != null) {
- mAsyncClockEventDelegate.onDestroy();
- }
-
super.onDestroy();
mHotseatPredictionController.destroy();
mSplitWithKeyboardShortcutController.onDestroy();
if (mViewCapture != null) mViewCapture.close();
+ removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
}
@Override
@@ -610,6 +630,7 @@ public class QuickstepLauncher extends Launcher {
mViewCapture = SettingsAwareViewCapture.getInstance(this).startCapture(getWindow());
}
getWindow().addPrivateFlags(PRIVATE_FLAG_OPTIMIZE_MEASURE);
+ QuickstepOnboardingPrefs.setup(this);
View.setTraceLayoutSteps(TRACE_LAYOUTS);
View.setTracedRequestLayoutClassClass(TRACE_RELAYOUT_CLASS);
}
@@ -621,13 +642,14 @@ public class QuickstepLauncher extends Launcher {
// using that.
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
Collections.singletonList(splitSelectSource.itemInfo.getComponentKey()),
+ false /* findExactPairMatch */,
foundTasks -> {
@Nullable Task foundTask = foundTasks.get(0);
boolean taskWasFound = foundTask != null;
splitSelectSource.alreadyRunningTaskId = taskWasFound
? foundTask.key.id
: INVALID_TASK_ID;
- if (ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
+ if (FeatureFlags.enableSplitContextually()) {
startSplitToHome(splitSelectSource);
} else {
recentsView.initiateSplitSelect(splitSelectSource);
@@ -661,6 +683,9 @@ public class QuickstepLauncher extends Launcher {
floatingTaskView.setAlpha(1);
floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
+ floatingTaskView.setOnClickListener(view ->
+ mSplitSelectStateController.getSplitAnimationController().
+ playAnimPlaceholderToFullscreen(this, view, Optional.empty()));
mSplitSelectStateController.setFirstFloatingTaskView(floatingTaskView);
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -676,6 +701,17 @@ public class QuickstepLauncher extends Launcher {
anim.buildAnim().start();
}
+ @Override
+ public boolean isSplitSelectionEnabled() {
+ return mSplitSelectStateController.isSplitSelectActive();
+ }
+
+ @Override
+ public void onStateTransitionCompletedAfterSwipeToHome(LauncherState finalState) {
+ if (mTaskbarUIController != null) {
+ mTaskbarUIController.onStateTransitionCompletedAfterSwipeToHome(finalState);
+ }
+ }
@Override
protected void onResume() {
@@ -693,6 +729,15 @@ public class QuickstepLauncher extends Launcher {
}
super.onPause();
+
+ if (FeatureFlags.enableSplitContextually()) {
+ // If Launcher pauses before both split apps are selected, exit split screen.
+ if (!mSplitSelectStateController.isBothSplitAppsConfirmed() &&
+ !mSplitSelectStateController.isLaunchingFirstAppFullscreen()) {
+ mSplitSelectStateController.getSplitAnimationController()
+ .playPlaceholderDismissAnim(this);
+ }
+ }
}
@Override
@@ -739,15 +784,6 @@ public class QuickstepLauncher extends Launcher {
}
@Override
- protected void onScreenOnChanged(boolean isOn) {
- super.onScreenOnChanged(isOn);
- if (!isOn) {
- RecentsView recentsView = getOverviewPanel();
- recentsView.finishRecentsAnimation(true /* toRecents */, null);
- }
- }
-
- @Override
public void onAllAppsTransition(float progress) {
super.onAllAppsTransition(progress);
onTaskbarInAppDisplayProgressUpdate(progress, ALL_APPS_PAGE_PROGRESS_INDEX);
@@ -866,7 +902,7 @@ public class QuickstepLauncher extends Launcher {
@Override
public void setResumed() {
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (isDesktopModeSupported()) {
DesktopVisibilityController controller = mDesktopVisibilityController;
if (controller != null && controller.areFreeformTasksVisible()
&& !controller.isRecentsGestureInProgress()) {
@@ -971,6 +1007,13 @@ public class QuickstepLauncher extends Launcher {
.playPlaceholderDismissAnim(this);
}
+ @Override
+ public void dismissSplitSelection() {
+ super.dismissSplitSelection();
+ mSplitSelectStateController.getSplitAnimationController()
+ .playPlaceholderDismissAnim(this);
+ }
+
public <T extends OverviewActionsView> T getActionsView() {
return (T) mActionsView;
}
@@ -1003,7 +1046,7 @@ public class QuickstepLauncher extends Launcher {
@Override
public boolean supportsAdaptiveIconAnimation(View clickedView) {
- return mAppTransitionManager.hasControlRemoteAppTransitionPermission();
+ return true;
}
@Override
@@ -1038,10 +1081,7 @@ public class QuickstepLauncher extends Launcher {
@Override
public ActivityOptionsWrapper getActivityLaunchOptions(View v, @Nullable ItemInfo item) {
- ActivityOptionsWrapper activityOptions =
- mAppTransitionManager.hasControlRemoteAppTransitionPermission()
- ? mAppTransitionManager.getActivityLaunchOptions(v)
- : super.getActivityLaunchOptions(v, item);
+ ActivityOptionsWrapper activityOptions = mAppTransitionManager.getActivityLaunchOptions(v);
if (mLastTouchUpTime > 0) {
activityOptions.options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER,
mLastTouchUpTime);
@@ -1156,11 +1196,6 @@ public class QuickstepLauncher extends Launcher {
}
@Override
- public void tryClearAccessibilityFocus(View view) {
- view.clearAccessibilityFocus();
- }
-
- @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -1235,36 +1270,29 @@ public class QuickstepLauncher extends Launcher {
/**
* Launches the given {@link GroupTask} in splitscreen.
- *
- * If the second split task is missing, launches the first task normally.
*/
- public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
- if (groupTask.task2 == null) {
- UI_HELPER_EXECUTOR.execute(() ->
- ActivityManagerWrapper.getInstance().startActivityFromRecents(
- groupTask.task1.key,
- getActivityLaunchOptions(taskView, null).options));
- return;
- }
+ public void launchSplitTasks(@NonNull GroupTask groupTask) {
+ // Top/left and bottom/right tasks respectively.
+ Task task1 = groupTask.task1;
+ // task2 should never be null when calling this method. Allow a crash to catch invalid calls
+ Task task2 = groupTask.task2;
mSplitSelectStateController.launchExistingSplitPair(
null /* launchingTaskView */,
- groupTask.task1.key.id,
- groupTask.task2.key.id,
+ task1.key.id,
+ task2.key.id,
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
/* callback= */ success -> mSplitSelectStateController.resetState(),
- /* freezeTaskList= */ true,
+ /* freezeTaskList= */ false,
groupTask.mSplitBounds == null
- ? DEFAULT_SPLIT_RATIO
- : groupTask.mSplitBounds.appsStackedVertically
- ? groupTask.mSplitBounds.topTaskPercent
- : groupTask.mSplitBounds.leftTaskPercent);
+ ? SNAP_TO_50_50
+ : groupTask.mSplitBounds.snapPosition);
}
/**
* Launches two apps as an app pair.
*/
- public void launchAppPair(WorkspaceItemInfo app1, WorkspaceItemInfo app2) {
- mSplitSelectStateController.getAppPairsController().launchAppPair(app1, app2);
+ public void launchAppPair(AppPairIcon appPairIcon) {
+ mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon);
}
public boolean canStartHomeSafely() {
@@ -1272,6 +1300,16 @@ public class QuickstepLauncher extends Launcher {
return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely();
}
+ @Override
+ public boolean isBubbleBarEnabled() {
+ return (mTaskbarUIController != null && mTaskbarUIController.isBubbleBarEnabled());
+ }
+
+ @Override
+ public boolean hasBubbles() {
+ return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
+ }
+
private static final class LauncherTaskViewController extends
TaskViewTouchController<Launcher> {
@@ -1321,18 +1359,12 @@ public class QuickstepLauncher extends Launcher {
switch (name) {
case "TextClock", "android.widget.TextClock" -> {
TextClock tc = new TextClock(context, attrs);
- if (mAsyncClockEventDelegate == null) {
- mAsyncClockEventDelegate = new AsyncClockEventDelegate(this);
- }
- tc.setClockEventDelegate(mAsyncClockEventDelegate);
+ tc.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this));
return tc;
}
case "AnalogClock", "android.widget.AnalogClock" -> {
AnalogClock ac = new AnalogClock(context, attrs);
- if (mAsyncClockEventDelegate == null) {
- mAsyncClockEventDelegate = new AsyncClockEventDelegate(this);
- }
- ac.setClockEventDelegate(mAsyncClockEventDelegate);
+ ac.setClockEventDelegate(AsyncClockEventDelegate.INSTANCE.get(this));
return ac;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index f7bef031c7..f66bc602a8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -37,6 +37,7 @@ import com.android.launcher3.util.IntSet;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.LauncherWidgetHolder;
+import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -237,6 +238,14 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
@Override
public LauncherAppWidgetHostView createView(@NonNull Context context, int appWidgetId,
@NonNull LauncherAppWidgetProviderInfo appWidget) {
+
+ if (appWidget.isCustomWidget()) {
+ LauncherAppWidgetHostView lahv = new LauncherAppWidgetHostView(context);
+ lahv.setAppWidget(appWidgetId, appWidget);
+ CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
+ return lahv;
+ }
+
LauncherAppWidgetHostView widgetView = getPendingView(appWidgetId);
if (widgetView != null) {
removePendingView(appWidgetId);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java
index 481e20007e..630ef39627 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DebugFlag.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.uioverrides.flags;
+import static com.android.launcher3.uioverrides.flags.FlagsFactory.TEAMFOOD_FLAG;
+
import androidx.annotation.NonNull;
import com.android.launcher3.config.FeatureFlags.BooleanFlag;
@@ -35,6 +37,21 @@ class DebugFlag extends BooleanFlag {
this.description = description;
}
+ /**
+ * Returns {@code true} if this flag's value has been modified from its default.
+ * <p>
+ * This helps to identify which flags have been toggled in log dumps and bug reports to
+ * further help triaging and debugging.
+ */
+ boolean currentValueModified() {
+ switch (defaultValue) {
+ case ENABLED: return !get();
+ case TEAMFOOD: return TEAMFOOD_FLAG.get() != get();
+ case DISABLED: return get();
+ default: return true;
+ }
+ }
+
@Override
public String toString() {
return key + ": defaultValue=" + defaultValue + ", mCurrentValue=" + get();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
index a76eb437de..c1a85fa71d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsFragment.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
@@ -15,20 +15,30 @@
*/
package com.android.launcher3.uioverrides.flags;
-import static android.content.Intent.ACTION_PACKAGE_ADDED;
-import static android.content.Intent.ACTION_PACKAGE_CHANGED;
-import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
-import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS;
+import static com.android.launcher3.LauncherPrefs.PRIVATE_SPACE_APPS;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_HIGHLIGHT_KEY;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
+import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
+import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_COUNT;
+import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.util.OnboardingPrefs.HOTSEAT_DISCOVERY_TIP_COUNT;
+import static com.android.launcher3.util.OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN;
+import static com.android.launcher3.util.OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP;
-import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -36,23 +46,17 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
import android.provider.Settings;
import android.text.Editable;
-import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.ArrayMap;
import android.util.Pair;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceDataStore;
@@ -63,17 +67,16 @@ import androidx.preference.PreferenceViewHolder;
import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreference;
+import com.android.launcher3.ConstantItem;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.util.SimpleBroadcastReceiver;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -81,39 +84,41 @@ import java.util.stream.Collectors;
* Dev-build only UI allowing developers to toggle flag settings and plugins.
* See {@link FeatureFlags}.
*/
-@TargetApi(Build.VERSION_CODES.O)
-public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
+public class DeveloperOptionsUI {
- private static final String ACTION_PLUGIN_SETTINGS = "com.android.systemui.action.PLUGIN_SETTINGS";
+ private static final String ACTION_PLUGIN_SETTINGS =
+ "com.android.systemui.action.PLUGIN_SETTINGS";
private static final String PLUGIN_PERMISSION = "com.android.systemui.permission.PLUGIN";
- private final SimpleBroadcastReceiver mPluginReceiver =
- new SimpleBroadcastReceiver(i -> loadPluginPrefs());
-
- private PreferenceScreen mPreferenceScreen;
+ private final PreferenceFragmentCompat mFragment;
+ private final PreferenceScreen mPreferenceScreen;
private PreferenceCategory mPluginsCategory;
- private FlagTogglerPrefUi mFlagTogglerPrefUi;
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- mPluginReceiver.registerPkgActions(getContext(), null,
- ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED);
- mPluginReceiver.register(getContext(), Intent.ACTION_USER_UNLOCKED);
+ public DeveloperOptionsUI(PreferenceFragmentCompat fragment, PreferenceCategory flags) {
+ mFragment = fragment;
+ mPreferenceScreen = fragment.getPreferenceScreen();
+
+ // Add search bar
+ View listView = mFragment.getListView();
+ ViewGroup parent = (ViewGroup) listView.getParent();
+ View topBar = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.developer_options_top_bar, parent, false);
+ parent.addView(topBar, parent.indexOfChild(listView));
+ initSearch(topBar.findViewById(R.id.filter_box));
- mPreferenceScreen = getPreferenceManager().createPreferenceScreen(getContext());
- setPreferenceScreen(mPreferenceScreen);
+ new FlagTogglerPrefUi(mFragment.requireActivity(), topBar.findViewById(R.id.flag_apply_btn))
+ .applyTo(flags);
- initFlags();
loadPluginPrefs();
maybeAddSandboxCategory();
addOnboardingPrefsCatergory();
if (FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
addAllAppsFromOverviewCatergory();
}
-
- if (getActivity() != null) {
- getActivity().setTitle("Developer Options");
+ addCustomLpnhCategory();
+ if (Flags.enablePrivateSpace()) {
+ addCustomPrivateAppsCategory();
}
}
@@ -137,21 +142,13 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
pg.setVisible(hidden != count);
}
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- EditText filterBox = view.findViewById(R.id.filter_box);
- filterBox.setVisibility(VISIBLE);
+ private void initSearch(EditText filterBox) {
filterBox.addTextChangedListener(new TextWatcher() {
@Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
-
- }
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@Override
- public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
-
- }
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { }
@Override
public void afterTextChanged(Editable editable) {
@@ -160,85 +157,33 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
}
});
- if (getArguments() != null) {
- String filter = getArguments().getString(EXTRA_FRAGMENT_ARG_KEY);
+ if (mFragment.getArguments() != null) {
+ String filter = mFragment.getArguments().getString(EXTRA_FRAGMENT_HIGHLIGHT_KEY);
// Normally EXTRA_FRAGMENT_ARG_KEY is used to highlight the preference with the given
// key. This is a slight variation where we instead filter by the human-readable titles.
if (filter != null) {
filterBox.setText(filter);
}
}
-
- View listView = getListView();
- final int bottomPadding = listView.getPaddingBottom();
- listView.setOnApplyWindowInsetsListener((v, insets) -> {
- v.setPadding(
- v.getPaddingLeft(),
- v.getPaddingTop(),
- v.getPaddingRight(),
- bottomPadding + insets.getSystemWindowInsetBottom());
- return insets.consumeSystemWindowInsets();
- });
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mPluginReceiver.unregisterReceiverSafely(getContext());
}
private PreferenceCategory newCategory(String title) {
- return newCategory(title, null);
- }
-
- private PreferenceCategory newCategory(String title, @Nullable String summary) {
PreferenceCategory category = new PreferenceCategory(getContext());
category.setOrder(Preference.DEFAULT_ORDER);
category.setTitle(title);
- if (!TextUtils.isEmpty(summary)) {
- category.setSummary(summary);
- }
mPreferenceScreen.addPreference(category);
return category;
}
- private void initFlags() {
- if (!FeatureFlags.showFlagTogglerUi(getContext())) {
- return;
- }
-
- mFlagTogglerPrefUi = new FlagTogglerPrefUi(this);
- mFlagTogglerPrefUi.applyTo(newCategory("Feature flags", "Long press to reset"));
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if (mFlagTogglerPrefUi != null) {
- mFlagTogglerPrefUi.onCreateOptionsMenu(menu);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mFlagTogglerPrefUi != null) {
- mFlagTogglerPrefUi.onOptionsItemSelected(item);
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void onStop() {
- if (mFlagTogglerPrefUi != null) {
- mFlagTogglerPrefUi.onStop();
- }
- super.onStop();
+ private Context getContext() {
+ return mFragment.requireContext();
}
private void loadPluginPrefs() {
if (mPluginsCategory != null) {
mPreferenceScreen.removePreference(mPluginsCategory);
}
- if (!PluginManagerWrapper.hasPlugins(getActivity())) {
+ if (!PluginManagerWrapper.hasPlugins(getContext())) {
mPluginsCategory = null;
return;
}
@@ -311,118 +256,159 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchTutorialStepMenuPreference.setKey("launchTutorialStepMenu");
launchTutorialStepMenuPreference.setTitle("Launch Gesture Tutorial Steps menu");
launchTutorialStepMenuPreference.setSummary("Select a gesture tutorial step.");
- launchTutorialStepMenuPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("use_tutorial_menu", true));
- return true;
- });
+ launchTutorialStepMenuPreference.setIntent(
+ new Intent(launchSandboxIntent).putExtra("use_tutorial_menu", true));
+
sandboxCategory.addPreference(launchTutorialStepMenuPreference);
Preference launchOnboardingTutorialPreference = new Preference(context);
launchOnboardingTutorialPreference.setKey("launchOnboardingTutorial");
launchOnboardingTutorialPreference.setTitle("Launch Onboarding Tutorial");
launchOnboardingTutorialPreference.setSummary("Learn the basic navigation gestures.");
- launchOnboardingTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent
- .putExtra("use_tutorial_menu", false)
- .putExtra("tutorial_steps",
- new String[] {
- "HOME_NAVIGATION",
- "BACK_NAVIGATION",
- "OVERVIEW_NAVIGATION"}));
- return true;
- });
+ launchTutorialStepMenuPreference.setIntent(new Intent(launchSandboxIntent)
+ .putExtra("use_tutorial_menu", false)
+ .putExtra("tutorial_steps",
+ new String[] {
+ "HOME_NAVIGATION",
+ "BACK_NAVIGATION",
+ "OVERVIEW_NAVIGATION"}));
+
sandboxCategory.addPreference(launchOnboardingTutorialPreference);
Preference launchBackTutorialPreference = new Preference(context);
launchBackTutorialPreference.setKey("launchBackTutorial");
launchBackTutorialPreference.setTitle("Launch Back Tutorial");
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
- launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent
+ launchBackTutorialPreference.setIntent(new Intent(launchSandboxIntent)
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"BACK_NAVIGATION"}));
- return true;
- });
+
sandboxCategory.addPreference(launchBackTutorialPreference);
Preference launchHomeTutorialPreference = new Preference(context);
launchHomeTutorialPreference.setKey("launchHomeTutorial");
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
- launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent
+ launchHomeTutorialPreference.setIntent(new Intent(launchSandboxIntent)
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"HOME_NAVIGATION"}));
- return true;
- });
+
sandboxCategory.addPreference(launchHomeTutorialPreference);
Preference launchOverviewTutorialPreference = new Preference(context);
launchOverviewTutorialPreference.setKey("launchOverviewTutorial");
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
- launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent
+ launchOverviewTutorialPreference.setIntent(new Intent(launchSandboxIntent)
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"OVERVIEW_NAVIGATION"}));
- return true;
- });
+
sandboxCategory.addPreference(launchOverviewTutorialPreference);
Preference launchSecondaryDisplayPreference = new Preference(context);
launchSecondaryDisplayPreference.setKey("launchSecondaryDisplay");
launchSecondaryDisplayPreference.setTitle("Launch Secondary Display");
launchSecondaryDisplayPreference.setSummary("Launch secondary display activity");
- launchSecondaryDisplayPreference.setOnPreferenceClickListener(preference -> {
- startActivity(new Intent(context, SecondaryDisplayLauncher.class));
- return true;
- });
- sandboxCategory.addPreference(launchSecondaryDisplayPreference);
+ launchSecondaryDisplayPreference.setIntent(
+ new Intent(context, SecondaryDisplayLauncher.class));
+
}
private void addOnboardingPrefsCatergory() {
PreferenceCategory onboardingCategory = newCategory("Onboarding Flows");
onboardingCategory.setSummary("Reset these if you want to see the education again.");
- for (Map.Entry<String, String[]> titleAndKeys : OnboardingPrefs.ALL_PREF_KEYS.entrySet()) {
- String title = titleAndKeys.getKey();
- String[] keys = titleAndKeys.getValue();
- Preference onboardingPref = new Preference(getContext());
- onboardingPref.setTitle(title);
- onboardingPref.setSummary("Tap to reset");
- onboardingPref.setOnPreferenceClickListener(preference -> {
- SharedPreferences.Editor sharedPrefsEdit = LauncherPrefs.getPrefs(getContext())
- .edit();
- for (String key : keys) {
- sharedPrefsEdit.remove(key);
- }
- sharedPrefsEdit.apply();
- Toast.makeText(getContext(), "Reset " + title, Toast.LENGTH_SHORT).show();
- return true;
- });
- onboardingCategory.addPreference(onboardingPref);
- }
+
+ onboardingCategory.addPreference(createOnboardPref("All Apps Bounce",
+ HOME_BOUNCE_SEEN.getSharedPrefKey(), HOME_BOUNCE_COUNT.getSharedPrefKey()));
+ onboardingCategory.addPreference(createOnboardPref("Hybrid Hotseat Education",
+ HOTSEAT_DISCOVERY_TIP_COUNT.getSharedPrefKey(),
+ HOTSEAT_LONGPRESS_TIP_SEEN.getSharedPrefKey()));
+ onboardingCategory.addPreference(createOnboardPref("Taskbar Education",
+ TASKBAR_EDU_TOOLTIP_STEP.getSharedPrefKey()));
+ onboardingCategory.addPreference(createOnboardPref("All Apps Visited Count",
+ ALL_APPS_VISITED_COUNT.getSharedPrefKey()));
+ }
+
+ private Preference createOnboardPref(String title, String... keys) {
+ Preference onboardingPref = new Preference(getContext());
+ onboardingPref.setTitle(title);
+ onboardingPref.setSummary("Tap to reset");
+ onboardingPref.setOnPreferenceClickListener(preference -> {
+ SharedPreferences.Editor sharedPrefsEdit = LauncherPrefs.getPrefs(getContext())
+ .edit();
+ for (String key : keys) {
+ sharedPrefsEdit.remove(key);
+ }
+ sharedPrefsEdit.apply();
+ Toast.makeText(getContext(), "Reset " + title, Toast.LENGTH_SHORT).show();
+ return true;
+ });
+ return onboardingPref;
}
private void addAllAppsFromOverviewCatergory() {
PreferenceCategory category = newCategory("All Apps from Overview Config");
+ category.addPreference(createSeekBarPreference("Threshold to open All Apps from Overview",
+ 105, 500, 100, ALL_APPS_OVERVIEW_THRESHOLD));
+ }
- SeekBarPreference thresholdPref = new SeekBarPreference(getContext());
- thresholdPref.setTitle("Threshold to open All Apps from Overview");
- thresholdPref.setSingleLineTitle(false);
+ private void addCustomLpnhCategory() {
+ PreferenceCategory category = newCategory("Long Press Nav Handle Config");
+ if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
+ category.addPreference(createSeekBarPreference("Slop multiplier (applied to edge slop, "
+ + "which is generally already 50% higher than touch slop)",
+ 25, 200, 100, LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE));
+ category.addPreference(createSeekBarPreference("Trigger milliseconds",
+ 100, 500, 1, LONG_PRESS_NAV_HANDLE_TIMEOUT_MS));
+ }
+ if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) {
+ category.addPreference(createSeekBarPreference("Haptic hint start scale",
+ 0, 100, 100, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT));
+ category.addPreference(createSeekBarPreference("Haptic hint end scale",
+ 0, 100, 100, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT));
+ category.addPreference(createSeekBarPreference("Haptic hint scale exponent",
+ 1, 5, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT));
+ category.addPreference(createSeekBarPreference("Haptic hint iterations (12 ms each)",
+ 0, 200, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS));
+ category.addPreference(createSeekBarPreference("Haptic hint delay (ms)",
+ 0, 400, 1, LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY));
+ }
+ }
- // These values are 100x swipe up shift value (100 = where overview sits).
- thresholdPref.setMax(500);
- thresholdPref.setMin(105);
- thresholdPref.setUpdatesContinuously(true);
- thresholdPref.setIconSpaceReserved(false);
+ private void addCustomPrivateAppsCategory() {
+ PreferenceCategory category = newCategory("Apps in Private Space Config");
+ category.addPreference(createSeekBarPreference(
+ "Number of Apps to put in private region", 0, 100, 1, PRIVATE_SPACE_APPS));
+ }
+
+ /**
+ * Create a preference with text and a seek bar. Should be added to a PreferenceCategory.
+ *
+ * @param title text to show for this seek bar
+ * @param min min value for the seek bar
+ * @param max max value for the seek bar
+ * @param scale how much to divide the value to convert int to float
+ * @param launcherPref used to store the current value
+ */
+ private SeekBarPreference createSeekBarPreference(String title, int min, int max, int scale,
+ ConstantItem<Integer> launcherPref) {
+ SeekBarPreference seekBarPref = new SeekBarPreference(getContext());
+ seekBarPref.setTitle(title);
+ seekBarPref.setSingleLineTitle(false);
+
+ seekBarPref.setMax(max);
+ seekBarPref.setMin(min);
+ seekBarPref.setUpdatesContinuously(true);
+ seekBarPref.setIconSpaceReserved(false);
// Don't directly save to shared prefs, use LauncherPrefs instead.
- thresholdPref.setPersistent(false);
- thresholdPref.setOnPreferenceChangeListener((preference, newValue) -> {
- LauncherPrefs.get(getContext()).put(ALL_APPS_OVERVIEW_THRESHOLD, newValue);
- preference.setSummary(String.valueOf((int) newValue / 100f));
+ seekBarPref.setPersistent(false);
+ seekBarPref.setOnPreferenceChangeListener((preference, newValue) -> {
+ LauncherPrefs.get(getContext()).put(launcherPref, newValue);
+ preference.setSummary(String.valueOf(scale == 1 ? newValue
+ : (int) newValue / (float) scale));
return true;
});
- int value = LauncherPrefs.get(getContext()).get(ALL_APPS_OVERVIEW_THRESHOLD);
- thresholdPref.setValue(value);
+ int value = LauncherPrefs.get(getContext()).get(launcherPref);
+ seekBarPref.setValue(value);
// For some reason the initial value is not triggering the summary update, so call manually.
- thresholdPref.getOnPreferenceChangeListener().onPreferenceChange(thresholdPref, value);
-
- category.addPreference(thresholdPref);
+ seekBarPref.setSummary(String.valueOf(scale == 1 ? value
+ : value / (float) scale));
+ return seekBarPref;
}
private String toName(String action) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java
index 035beb4df1..915f4aeaf7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeviceFlag.java
@@ -29,6 +29,11 @@ class DeviceFlag extends DebugFlag {
}
@Override
+ boolean currentValueModified() {
+ return super.currentValueModified() || mDefaultValueInCode != get();
+ }
+
+ @Override
public String toString() {
return super.toString() + ", mDefaultValueInCode=" + mDefaultValueInCode;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
index 87c836bbac..ec0566ac5c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
@@ -19,24 +19,23 @@ package com.android.launcher3.uioverrides.flags;
import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
import static com.android.launcher3.uioverrides.flags.FlagsFactory.TEAMFOOD_FLAG;
+import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.os.Process;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
+import android.view.View;
import android.widget.Toast;
import androidx.preference.PreferenceDataStore;
-import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
-import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
import java.util.List;
import java.util.Set;
@@ -44,12 +43,13 @@ import java.util.Set;
/**
* Dev-build only UI allowing developers to toggle flag settings. See {@link FeatureFlags}.
*/
-public final class FlagTogglerPrefUi {
+public final class FlagTogglerPrefUi implements ActivityLifecycleCallbacksAdapter {
private static final String TAG = "FlagTogglerPrefFrag";
- private final PreferenceFragmentCompat mFragment;
+ private final View mFlagsApplyButton;
private final Context mContext;
+
private final PreferenceDataStore mDataStore = new PreferenceDataStore() {
@Override
@@ -64,9 +64,17 @@ public final class FlagTogglerPrefUi {
}
};
- public FlagTogglerPrefUi(PreferenceFragmentCompat fragment) {
- mFragment = fragment;
- mContext = fragment.getActivity();
+ public FlagTogglerPrefUi(Activity activity, View flagsApplyButton) {
+ mFlagsApplyButton = flagsApplyButton;
+ mContext = mFlagsApplyButton.getContext();
+ activity.registerActivityLifecycleCallbacks(this);
+
+ mFlagsApplyButton.setOnClickListener(v -> {
+ FlagsFactory.getSharedPreferences().edit().commit();
+ Log.e(TAG,
+ "Killing launcher process " + Process.myPid() + " to apply new flag values");
+ System.exit(0);
+ });
}
public void applyTo(PreferenceGroup parent) {
@@ -137,27 +145,11 @@ public final class FlagTogglerPrefUi {
}
private void updateMenu() {
- mFragment.setHasOptionsMenu(anyChanged());
- mFragment.getActivity().invalidateOptionsMenu();
- }
-
- public void onCreateOptionsMenu(Menu menu) {
- if (anyChanged()) {
- menu.add(0, R.id.menu_apply_flags, 0, "Apply")
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- }
-
- public void onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == R.id.menu_apply_flags) {
- FlagsFactory.getSharedPreferences().edit().commit();
- Log.e(TAG,
- "Killing launcher process " + Process.myPid() + " to apply new flag values");
- System.exit(0);
- }
+ mFlagsApplyButton.setVisibility(anyChanged() ? View.VISIBLE : View.INVISIBLE);
}
- public void onStop() {
+ @Override
+ public void onActivityStopped(Activity activity) {
if (anyChanged()) {
Toast.makeText(mContext, "Flag won't be applied until you restart launcher",
Toast.LENGTH_LONG).show();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index 6279f634d6..48d313e2d3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -57,6 +57,7 @@ public class FlagsFactory {
public static final String NAMESPACE_LAUNCHER = "launcher";
private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
+ private static final List<IntFlag> sIntFlags = new ArrayList<>();
private static SharedPreferences sSharedPreferences;
static final BooleanFlag TEAMFOOD_FLAG = getReleaseFlag(
@@ -132,7 +133,14 @@ public class FlagsFactory {
public static IntFlag getIntFlag(
int bugId, String key, int defaultValueInCode, String description) {
INSTANCE.mKeySet.add(key);
- return new IntFlag(DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, defaultValueInCode));
+ int defaultValue = DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, defaultValueInCode);
+ if (IS_DEBUG_DEVICE) {
+ IntDeviceFlag flag = new IntDeviceFlag(key, defaultValue, defaultValueInCode);
+ sIntFlags.add(flag);
+ return flag;
+ } else {
+ return new IntFlag(defaultValue);
+ }
}
static List<DebugFlag> getDebugFlags() {
@@ -163,18 +171,25 @@ public class FlagsFactory {
return;
}
pw.println("DeviceFlags:");
+ pw.println(" BooleanFlags:");
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
if (flag instanceof DeviceFlag) {
- pw.println(" " + flag);
+ pw.println((flag.currentValueModified() ? " ->" : " ") + flag);
}
}
}
- pw.println("DebugFlags:");
+ pw.println(" IntFlags:");
+ synchronized (sIntFlags) {
+ for (IntFlag flag : sIntFlags) {
+ pw.println(" " + flag);
+ }
+ }
+ pw.println(" DebugFlags:");
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
if (!(flag instanceof DeviceFlag)) {
- pw.println(" " + flag);
+ pw.println((flag.currentValueModified() ? " ->" : " ") + flag);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/IntDeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/IntDeviceFlag.java
new file mode 100644
index 0000000000..4f3b0ae137
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/IntDeviceFlag.java
@@ -0,0 +1,34 @@
+/*
+ * 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.uioverrides.flags;
+
+import com.android.launcher3.config.FeatureFlags.IntFlag;
+
+public class IntDeviceFlag extends IntFlag {
+ public final String key;
+ private final int mDefaultValueInCode;
+
+ public IntDeviceFlag(String key, int currentValue, int defaultValueInCode) {
+ super(currentValue);
+ this.key = key;
+ mDefaultValueInCode = defaultValueInCode;
+ }
+
+ @Override
+ public String toString() {
+ return key + ": mCurrentValue=" + get() + ", defaultValueInCode=" + mDefaultValueInCode;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index ed0a0d5172..3767cce67f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -121,13 +121,13 @@ public class AllAppsState extends LauncherState {
@Override
public int getFloatingSearchBarRestingMarginStart(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(launcher);
}
@Override
public int getFloatingSearchBarRestingMarginEnd(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
- return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin();
+ return dp.allAppsLeftRightMargin + dp.getAllAppsIconStartMargin(launcher);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index e5787209d2..d11a08bff5 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -17,6 +17,7 @@ package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import android.content.Context;
import android.graphics.Color;
@@ -26,7 +27,6 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
/**
@@ -90,7 +90,7 @@ public class BackgroundAppState extends OverviewState {
@Override
protected float getDepthUnchecked(Context context) {
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (isDesktopModeSupported()) {
if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
// Don't blur the background while freeform tasks are visible
return 0;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index b9221eef9d..1d55da325e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -21,9 +21,9 @@ import android.content.Context;
import android.graphics.Rect;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.views.RecentsView;
/**
@@ -72,7 +72,7 @@ public class OverviewModalTaskState extends OverviewState {
@Override
public boolean isTaskbarStashed(Launcher launcher) {
- if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+ if (Flags.enableGridOnlyOverview()) {
return true;
}
return super.isTaskbarStashed(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 739246992c..ba44d6a795 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import android.graphics.Color;
@@ -23,7 +24,6 @@ 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
@@ -46,7 +46,7 @@ public class QuickSwitchState extends BackgroundAppState {
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (isDesktopModeSupported()) {
if (launcher.areFreeformTasksVisible()) {
// No scrim while freeform tasks are visible
return Color.TRANSPARENT;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index b266bcd246..82a9c058df 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -45,6 +45,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
@@ -194,8 +195,21 @@ public class NavBarToHomeTouchController implements TouchController,
recentsView.switchToScreenshot(null,
() -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
if (mStartState.overviewUi) {
- new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState),
- FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()
+ Runnable onReachedHome = () -> {
+ StateManager.StateListener<LauncherState> listener =
+ new StateManager.StateListener<>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ mLauncher.onStateTransitionCompletedAfterSwipeToHome(
+ finalState);
+ mLauncher.getStateManager().removeStateListener(this);
+ }
+ };
+ mLauncher.getStateManager().addStateListener(listener);
+ onSwipeInteractionCompleted(mEndState);
+ };
+ new OverviewToHomeAnim(mLauncher, onReachedHome,
+ FeatureFlags.enableSplitContextually()
? mCancelSplitRunnable
: null)
.animateWithVelocity(velocity);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 6f421eb14a..968faf07c7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static android.view.MotionEvent.ACTION_DOWN;
-
import static com.android.app.animation.Interpolators.ACCELERATE_0_75;
import static com.android.app.animation.Interpolators.DECELERATE_3;
import static com.android.app.animation.Interpolators.LINEAR;
@@ -49,6 +48,7 @@ import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -83,7 +83,6 @@ import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.WorkspaceRevealAnim;
-import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
@@ -177,7 +176,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
return false;
}
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (isDesktopModeSupported()) {
// TODO(b/268075592): add support for quickswitch to/from desktop
return false;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index e30fe667ff..2c937b008e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -15,8 +15,7 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -84,7 +83,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return false;
}
}
- if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {
+ if (getTopOpenViewWithType(mLauncher, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) {
return false;
}
return true;
@@ -96,7 +95,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()
? mLauncher.getStateManager().getLastState()
: NORMAL;
- } else if (fromState == NORMAL && isDragTowardPositive) {
+ } else if (fromState == NORMAL && shouldOpenAllApps(isDragTowardPositive)) {
return ALL_APPS;
}
return fromState;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 9a35bb2ae7..ff142fe2ce 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -30,6 +30,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PR
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -48,7 +49,6 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
-import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -79,7 +79,7 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
return false;
}
- if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ if (isDesktopModeSupported()) {
// TODO(b/268075592): add support for quickswitch to/from desktop
return false;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 26ab3d6c52..cda785504b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
import android.graphics.PointF;
@@ -57,6 +58,8 @@ public class StatusBarTouchController implements TouchController {
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
private boolean mCanIntercept;
+ private boolean mIsTrackpadReverseScroll;
+
public StatusBarTouchController(Launcher l) {
mLauncher = l;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
@@ -92,6 +95,8 @@ public class StatusBarTouchController implements TouchController {
}
mDownEvents.clear();
mDownEvents.put(pid, new PointF(ev.getX(), ev.getY()));
+ mIsTrackpadReverseScroll = !mLauncher.isNaturalScrollingEnabled()
+ && isTrackpadScroll(ev);
} else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
// Check!! should only set it only when threshold is not entered.
mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx)));
@@ -102,6 +107,9 @@ public class StatusBarTouchController implements TouchController {
if (action == ACTION_MOVE) {
float dy = ev.getY(idx) - mDownEvents.get(pid).y;
float dx = ev.getX(idx) - mDownEvents.get(pid).x;
+ if (mIsTrackpadReverseScroll) {
+ dy = -dy;
+ }
// Currently input dispatcher will not do touch transfer if there are more than
// one touch pointer. Hence, even if slope passed, only set the slippery flag
// when there is single touch event. (context: InputDispatcher.cpp line 1445)
@@ -126,6 +134,7 @@ public class StatusBarTouchController implements TouchController {
mLauncher.getStatsLogManager().logger()
.log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
setWindowSlippery(false);
+ mIsTrackpadReverseScroll = false;
return true;
}
return true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 3d94857848..19bfe069c8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
-import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
@@ -112,7 +112,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
// If we are already animating from a previous state, we can intercept.
return true;
}
- if (AbstractFloatingView.getTopOpenViewWithType(mActivity, TYPE_ACCESSIBLE) != null) {
+ if (AbstractFloatingView.getTopOpenViewWithType(
+ mActivity, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) {
return false;
}
return isRecentsInteractive();