Merge "Simplifying some scrollbar binding logic in all-apps" into tm-qpr-dev
diff --git a/quickstep/res/drawable/close_icon.xml b/quickstep/res/drawable/close_icon.xml
deleted file mode 100644
index 07f4336..0000000
--- a/quickstep/res/drawable/close_icon.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?attr/colorControlNormal">
- <path
- android:fillColor="#909090"
- android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
-</vector>
diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
index 6d778ef..ff7c138 100644
--- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
-import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
import android.animation.Animator;
@@ -93,7 +92,8 @@
}
private void animateToRecentsState(RecentsState toState) {
- Animator anim = createAnimToRecentsState(toState, TASKBAR_STASH_DURATION);
+ Animator anim = createAnimToRecentsState(toState,
+ mControllers.taskbarStashController.getStashDuration());
if (anim != null) {
anim.start();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 94d62b2..745defc 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -425,7 +425,7 @@
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- return (mViewCallbacks != null && mViewCallbacks.onKeyUp(keyCode, event))
+ return (mViewCallbacks != null && mViewCallbacks.onKeyUp(keyCode, event, mIsRtl))
|| super.onKeyUp(keyCode, event);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index f0f361e..c1f764f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.view.KeyEvent;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -125,18 +126,26 @@
}
private int launchTaskAt(int index) {
- KeyboardQuickSwitchTaskView taskView = mKeyboardQuickSwitchView.getTaskAt(index);
+ if (mCloseAnimation != null) {
+ // Ignore taps on task views and alt key unpresses while the close animation is running.
+ return -1;
+ }
+ // Even with a valid index, this can be null if the user tries to quick switch before the
+ // views have been added in the KeyboardQuickSwitchView.
+ View taskView = mKeyboardQuickSwitchView.getTaskAt(index);
GroupTask task = mControllerCallbacks.getTaskAt(index);
- if (taskView == null || task == null) {
+ if (task == null) {
return Math.max(0, index);
} else if (task.task2 == null) {
UI_HELPER_EXECUTOR.execute(() ->
ActivityManagerWrapper.getInstance().startActivityFromRecents(
task.task1.key,
mControllers.taskbarActivityContext.getActivityLaunchOptions(
- taskView, null).options));
+ taskView == null ? mKeyboardQuickSwitchView : taskView, null)
+ .options));
} else {
- mControllers.uiController.launchSplitTasks(taskView, task);
+ mControllers.uiController.launchSplitTasks(
+ taskView == null ? mKeyboardQuickSwitchView : taskView, task);
}
return -1;
}
@@ -160,15 +169,26 @@
class ViewCallbacks {
- boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode != KeyEvent.KEYCODE_TAB) {
+ boolean onKeyUp(int keyCode, KeyEvent event, boolean isRTL) {
+ if (keyCode != KeyEvent.KEYCODE_TAB
+ && keyCode != KeyEvent.KEYCODE_DPAD_RIGHT
+ && keyCode != KeyEvent.KEYCODE_DPAD_LEFT
+ && keyCode != KeyEvent.KEYCODE_GRAVE
+ && keyCode != KeyEvent.KEYCODE_ESCAPE) {
return false;
}
+ if (keyCode == KeyEvent.KEYCODE_GRAVE || keyCode == KeyEvent.KEYCODE_ESCAPE) {
+ closeQuickSwitchView(true);
+ return true;
+ }
+ boolean traverseBackwards = (keyCode == KeyEvent.KEYCODE_TAB && event.isShiftPressed())
+ || (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT && !isRTL)
+ || (keyCode == KeyEvent.KEYCODE_DPAD_LEFT && isRTL);
int taskCount = mControllerCallbacks.getTaskCount();
int toIndex = mCurrentFocusIndex == -1
// Focus the second-most recent app if possible
? (taskCount > 1 ? 1 : 0)
- : (event.isShiftPressed()
+ : (traverseBackwards
// focus a more recent task or loop back to the opposite end
? Math.max(0, mCurrentFocusIndex == 0
? taskCount - 1 : mCurrentFocusIndex - 1)
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index ac584bf..3046076 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -388,12 +388,16 @@
}
@Override
- public void launchSplitTasks(View taskView, GroupTask groupTask) {
- super.launchSplitTasks(taskView, groupTask);
+ public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
mLauncher.launchSplitTasks(taskView, groupTask);
}
@Override
+ protected void onIconLayoutBoundsChanged() {
+ mTaskbarLauncherStateController.resetIconAlignment();
+ }
+
+ @Override
public void dumpLogs(String prefix, PrintWriter pw) {
super.dumpLogs(prefix, pw);
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 0f25ba1..f082fc6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -38,7 +38,6 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
-import com.android.systemui.shared.system.QuickStepContract;
import java.io.PrintWriter;
@@ -152,6 +151,14 @@
}
}
+ /**
+ * Returns the stashed handle bounds.
+ * @param out The destination rect.
+ */
+ public void getStashedHandleBounds(Rect out) {
+ out.set(mStashedHandleBounds);
+ }
+
private void initRegionSampler() {
mRegionSamplingHelper = new RegionSamplingHelper(mStashedHandleView,
new RegionSamplingHelper.SamplingCallback() {
@@ -194,16 +201,19 @@
*/
public Animator createRevealAnimToIsStashed(boolean isStashed) {
Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds());
+ float startRadius = mStashedHandleRadius;
if (DisplayController.isTransientTaskbar(mActivity)) {
// Account for the full visual height of the transient taskbar.
int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
visualBounds.top -= heightDiff;
visualBounds.bottom += heightDiff;
+
+ startRadius = visualBounds.height() / 2f;
}
final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider(
- mStashedHandleRadius, mStashedHandleRadius, visualBounds, mStashedHandleBounds);
+ startRadius, mStashedHandleRadius, visualBounds, mStashedHandleBounds);
boolean isReversed = !isStashed;
boolean changingDirection = mWasLastRevealAnimReversed != isReversed;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9b0f8c4..ab52adb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -560,8 +560,6 @@
public void updateSysuiStateFlags(int systemUiStateFlags, boolean fromInit) {
mControllers.navbarButtonsViewController.updateStateForSysuiFlags(systemUiStateFlags,
fromInit);
- mControllers.taskbarViewController.setImeIsVisible(
- mControllers.navbarButtonsViewController.isImeVisible());
int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
@@ -879,9 +877,9 @@
* (potentially breaking a split pair).
*/
private void launchFromTaskbarPreservingSplitIfVisible(RecentsView recents, ItemInfo info) {
- recents.findLastActiveTaskAndRunCallback(
+ recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
info.getTargetComponent(),
- (Consumer<Task>) foundTask -> {
+ foundTask -> {
if (foundTask != null) {
TaskView foundTaskView =
recents.getTaskViewByTaskId(foundTask.key.id);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 82f27ae..37d9090 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -20,7 +20,9 @@
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
+import android.graphics.RectF
import com.android.launcher3.R
+import com.android.launcher3.Utilities.mapRange
import com.android.launcher3.Utilities.mapToRange
import com.android.launcher3.anim.Interpolators
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
@@ -29,7 +31,8 @@
/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
class TaskbarBackgroundRenderer(context: TaskbarActivityContext) {
- val paint: Paint = Paint()
+ val paint = Paint()
+ val lastDrawnTransientRect = RectF()
var backgroundHeight = context.deviceProfile.taskbarSize.toFloat()
var translationYForSwipe = 0f
@@ -51,6 +54,12 @@
private val invertedLeftCornerPath: Path = Path()
private val invertedRightCornerPath: Path = Path()
+ private val stashedHandleWidth =
+ context.resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_width)
+
+ private val stashedHandleHeight =
+ context.resources.getDimensionPixelSize(R.dimen.taskbar_stashed_handle_height)
+
init {
paint.color = context.getColor(R.color.taskbar_background)
paint.flags = Paint.ANTI_ALIAS_FLAG
@@ -98,8 +107,8 @@
/** Draws the background with the given paint and height, on the provided canvas. */
fun draw(canvas: Canvas) {
canvas.save()
- canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
if (!isTransientTaskbar || transientBackgroundBounds.isEmpty) {
+ canvas.translate(0f, canvas.height - backgroundHeight - bottomMargin)
// Draw the background behind taskbar content.
canvas.drawRect(0f, 0f, canvas.width.toFloat(), backgroundHeight, paint)
@@ -110,13 +119,25 @@
canvas.translate(canvas.width - rightCornerRadius, -rightCornerRadius)
canvas.drawPath(invertedRightCornerPath, paint)
} else {
- // Approximates the stash/unstash animation to transform the background.
- val scaleFactor = backgroundHeight / maxBackgroundHeight
- val width = transientBackgroundBounds.width()
- val widthScale = mapToRange(scaleFactor, 0f, 1f, 0.2f, 1f, Interpolators.LINEAR)
- val newWidth = widthScale * width
- val delta = width - newWidth
- canvas.translate(0f, bottomMargin * ((1f - scaleFactor) / 2f))
+ // 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 +
+ (-mapRange(1f - progress, 0f, stashedHandleHeight / 2f) + translationYForSwipe)
// Draw shadow.
val shadowAlpha =
@@ -128,20 +149,15 @@
setColorAlphaBound(Color.BLACK, Math.round(shadowAlpha))
)
- // Draw background.
- val radius = backgroundHeight / 2f
-
- canvas.drawRoundRect(
- transientBackgroundBounds.left + (delta / 2f),
- translationYForSwipe,
- transientBackgroundBounds.right - (delta / 2f),
- backgroundHeight + translationYForSwipe,
- radius,
- radius,
- paint
+ lastDrawnTransientRect.set(
+ transientBackgroundBounds.left + halfWidthDelta,
+ bottom - newBackgroundHeight,
+ transientBackgroundBounds.right - halfWidthDelta,
+ bottom
)
- }
+ canvas.drawRoundRect(lastDrawnTransientRect, radius, radius, paint)
+ }
canvas.restore()
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index d1fea7b..4e79011 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -33,6 +33,7 @@
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.util.Pair;
@@ -69,11 +70,13 @@
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.quickstep.util.LogUtils;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.systemui.shared.recents.model.Task;
+import com.android.wm.shell.draganddrop.DragAndDropConstants;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -310,9 +313,6 @@
if (mDisallowGlobalDrag) {
AbstractFloatingView.closeAllOpenViewsExcept(mActivity, TYPE_TASKBAR_ALL_APPS);
} else {
- // stash the transient taskbar
- mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
-
AbstractFloatingView.closeAllOpenViews(mActivity);
}
@@ -340,7 +340,7 @@
if (DEBUG_DRAG_SHADOW_SURFACE) {
canvas.drawColor(0xffff0000);
}
- float scale = mDragObject.dragView.getScaleX();
+ float scale = mDragObject.dragView.getEndScale();
canvas.scale(scale, scale);
mDragObject.dragView.draw(canvas);
canvas.restore();
@@ -395,6 +395,15 @@
com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
+ if (DisplayController.isTransientTaskbar(mActivity)) {
+ // Tell WM Shell to ignore drag events in the provided transient taskbar region.
+ TaskbarDragLayer dragLayer = mControllers.taskbarActivityContext.getDragLayer();
+ int[] locationOnScreen = dragLayer.getLocationOnScreen();
+ RectF disallowExternalDropRegion = new RectF(dragLayer.getLastDrawnTransientRect());
+ disallowExternalDropRegion.offset(locationOnScreen[0], locationOnScreen[1]);
+ intent.putExtra(DragAndDropConstants.EXTRA_DISALLOW_HIT_REGION,
+ disallowExternalDropRegion);
+ }
ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent));
if (btv.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
@@ -421,9 +430,6 @@
if (dragEvent.getResult()) {
maybeOnDragEnd();
} else {
- // un-stash the transient taskbar in case drag and drop was canceled
- mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
-
// This will take care of calling maybeOnDragEnd() after the animation
animateGlobalDragViewToOriginalPosition(btv, dragEvent);
}
@@ -451,6 +457,9 @@
mControllers.taskbarAutohideSuspendController.updateFlag(
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, false);
mActivity.onDragEnd();
+ // Note, this must be done last to ensure no AutohideSuspendFlags are active, as that
+ // will prevent us from stashing until the timeout.
+ mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
}
}
@@ -592,7 +601,15 @@
View target = findTaskbarTargetForIconView(originalView);
int[] toPosition = target.getLocationOnScreen();
- float toScale = (float) target.getWidth() / mDragIconSize;
+ float iconSize = target.getWidth();
+ if (target instanceof BubbleTextView) {
+ Rect bounds = new Rect();
+ ((BubbleTextView) target).getSourceVisualDragBounds(bounds);
+ toPosition[0] += bounds.left;
+ toPosition[1] += bounds.top;
+ iconSize = bounds.width();
+ }
+ float toScale = iconSize / mDragIconSize;
float toAlpha = (target == originalView) ? 1f : 0f;
MultiValueUpdateListener listener = new MultiValueUpdateListener() {
final FloatProp mDx = new FloatProp(fromX, toPosition[0], 0,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
index 7114849..58d6244 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -183,6 +184,11 @@
invalidate();
}
+ /** Returns the bounds in DragLayer coordinates of where the transient background was drawn. */
+ protected RectF getLastDrawnTransientRect() {
+ return mBackgroundRenderer.getLastDrawnTransientRect();
+ }
+
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 6432119..0b86155 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
-import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION;
import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
import static com.android.systemui.animation.Interpolators.EMPHASIZED;
@@ -39,8 +38,8 @@
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
+import com.android.launcher3.util.window.RefreshRateTracker;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.views.RecentsView;
@@ -119,11 +118,10 @@
mLauncherState = finalState;
updateStateForFlag(FLAG_TRANSITION_STATE_RUNNING, false);
applyState();
- boolean finalStateOverview = finalState instanceof OverviewState;
boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
com.android.launcher3.taskbar.Utilities.setOverviewDragState(
- mControllers, finalStateOverview /*disallowGlobalDrag*/,
- disallowLongClick, finalStateOverview /*allowInitialSplitSelection*/);
+ mControllers, finalState.disallowTaskbarGlobalDrag(),
+ disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
}
};
@@ -139,8 +137,7 @@
mIconAlphaForHome = mControllers.taskbarViewController
.getTaskbarIconAlpha().get(ALPHA_INDEX_HOME);
- mIconAlignment.finishAnimation();
- onIconAlignmentRatioChanged();
+ resetIconAlignment();
mLauncher.getStateManager().addStateListener(mStateListener);
@@ -234,7 +231,7 @@
}
public void applyState() {
- applyState(TASKBAR_STASH_DURATION);
+ applyState(mControllers.taskbarStashController.getStashDuration());
}
public void applyState(long duration) {
@@ -242,7 +239,7 @@
}
public Animator applyState(boolean start) {
- return applyState(TASKBAR_STASH_DURATION, start);
+ return applyState(mControllers.taskbarStashController.getStashDuration(), start);
}
public Animator applyState(long duration, boolean start) {
@@ -329,8 +326,17 @@
+ mTaskbarBackgroundAlpha.value
+ " -> " + backgroundAlpha + ": " + duration);
}
- animatorSet.play(mTaskbarBackgroundAlpha.animateToValue(backgroundAlpha)
- .setDuration(duration));
+
+ Animator taskbarBackgroundAlpha = mTaskbarBackgroundAlpha
+ .animateToValue(backgroundAlpha)
+ .setDuration(duration);
+ // Add a single frame delay to the taskbar bg to avoid too many moving parts during the
+ // app launch animation.
+ taskbarBackgroundAlpha.setStartDelay(
+ (hasAnyFlag(changedFlags, FLAG_RESUMED) && !goingToLauncher)
+ ? RefreshRateTracker.getSingleFrameMs(mLauncher)
+ : 0);
+ animatorSet.play(taskbarBackgroundAlpha);
}
float cornerRoundness = goingToLauncher ? 0 : 1;
@@ -433,6 +439,14 @@
return (mState & FLAGS_LAUNCHER) != 0;
}
+ /**
+ * Resets and updates the icon alignment.
+ */
+ protected void resetIconAlignment() {
+ mIconAlignment.finishAnimation();
+ onIconAlignmentRatioChanged();
+ }
+
private void onIconAlignmentRatioChanged() {
float currentValue = mIconAlphaForHome.getValue();
boolean taskbarWillBeVisible = mIconAlignment.value < 1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 2f69b07..50f5d9e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,8 +17,10 @@
import static android.view.HapticFeedbackConstants.LONG_PRESS;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
@@ -41,6 +43,7 @@
import android.view.InsetsController;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -101,11 +104,20 @@
/**
* How long to stash/unstash when manually invoked via long press.
+ *
+ * Use {@link #getStashDuration()} to query duration
*/
- public static final long TASKBAR_STASH_DURATION =
+ private static final long TASKBAR_STASH_DURATION =
InsetsController.ANIMATION_DURATION_RESIZE;
/**
+ * How long to stash/unstash transient taskbar.
+ *
+ * Use {@link #getStashDuration()} to query duration.
+ */
+ private static final long TRANSIENT_TASKBAR_STASH_DURATION = 417;
+
+ /**
* How long to stash/unstash when keyboard is appearing/disappearing.
*/
private static final long TASKBAR_STASH_DURATION_FOR_IME = 80;
@@ -113,7 +125,7 @@
/**
* The scale TaskbarView animates to when being stashed.
*/
- private static final float STASHED_TASKBAR_SCALE = 0.3f;
+ protected static final float STASHED_TASKBAR_SCALE = 0.5f;
/**
* How long the hint animation plays, starting on motion down.
@@ -122,6 +134,21 @@
ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
/**
+ * How long to delay the icon/stash handle alpha.
+ */
+ private static final long TASKBAR_STASH_ALPHA_START_DELAY = 33;
+
+ /**
+ * How long the icon/stash handle alpha animation plays.
+ */
+ private static final long TASKBAR_STASH_ALPHA_DURATION = 50;
+
+ /**
+ * How long to delay the icon/stash handle alpha for the home to app taskbar animation.
+ */
+ 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;
@@ -299,7 +326,16 @@
boolean hideTaskbar = isVisible || !mActivity.isUserSetupComplete();
updateStateForFlag(FLAG_IN_SETUP, hideTaskbar);
updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, hideTaskbar);
- applyState(hideTaskbar ? 0 : TASKBAR_STASH_DURATION);
+ applyState(hideTaskbar ? 0 : getStashDuration());
+ }
+
+ /**
+ * Returns how long the stash/unstash animation should play.
+ */
+ public long getStashDuration() {
+ return DisplayController.isTransientTaskbar(mActivity)
+ ? TRANSIENT_TASKBAR_STASH_DURATION
+ : TASKBAR_STASH_DURATION;
}
/**
@@ -514,7 +550,10 @@
}
mAnimator = new AnimatorSet();
addJankMonitorListener(mAnimator, /* appearing= */ !mIsStashed);
- final float stashTranslation = isPhoneMode() ? 0 : (mUnstashedHeight - mStashedHeight);
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ final float stashTranslation = isPhoneMode() || isTransientTaskbar
+ ? 0
+ : (mUnstashedHeight - mStashedHeight);
if (!supportsVisualStashing()) {
// Just hide/show the icons and background instead of stashing into a handle.
@@ -522,8 +561,8 @@
.setDuration(duration));
mAnimator.playTogether(mTaskbarBackgroundOffset.animateToValue(isStashed ? 1 : 0)
.setDuration(duration));
- mAnimator.playTogether(mIconTranslationYForStash.animateToValue(isStashed ?
- stashTranslation : 0)
+ mAnimator.playTogether(mIconTranslationYForStash.animateToValue(isStashed
+ ? stashTranslation : 0)
.setDuration(duration));
mAnimator.play(mTaskbarImeBgAlpha.animateToValue(
hasAnyFlag(FLAG_STASHED_IN_APP_IME) ? 0 : 1).setDuration(duration));
@@ -531,6 +570,40 @@
return;
}
+ // If Hotseat is not the top element during animation to/from Launcher, fade in/out a
+ // already stashed Taskbar.
+ boolean skipStashAnimation = !mControllers.uiController.isHotseatIconOnTopWhenAligned()
+ && hasAnyFlag(changedFlags, FLAG_IN_APP);
+ if (isTransientTaskbar) {
+ createTransientAnimToIsStashed(mAnimator, isStashed, duration, animateBg, changedFlags,
+ skipStashAnimation);
+ } else {
+ createAnimToIsStashed(mAnimator, isStashed, duration, animateBg, skipStashAnimation,
+ stashTranslation);
+ }
+
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsStashed = isStashed;
+ onIsStashedChanged(mIsStashed);
+
+ cancelTimeoutIfExists();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimator = null;
+
+ if (!mIsStashed) {
+ tryStartTaskbarTimeout();
+ }
+ }
+ });
+ }
+
+ private void createAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
+ boolean animateBg, boolean skipStashAnimation, float stashTranslation) {
AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
// Not exactly half and may overlap. See [first|second]HalfDurationScale below.
AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
@@ -539,10 +612,6 @@
final float firstHalfDurationScale;
final float secondHalfDurationScale;
- // If Hotseat is not the top element during animation to/from Launcher, fade in/out a
- // already stashed Taskbar.
- boolean skipStashAnimation = !mControllers.uiController.isHotseatIconOnTopWhenAligned()
- && hasAnyFlag(changedFlags, FLAG_IN_APP);
if (isStashed) {
firstHalfDurationScale = 0.75f;
secondHalfDurationScale = 0.5f;
@@ -595,10 +664,6 @@
}
}
- if (DisplayController.isTransientTaskbar(mActivity)) {
- fullLengthAnimatorSet.play(mControllers.taskbarViewController
- .createRevealAnimToIsStashed(isStashed));
- }
fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
.createRevealAnimToIsStashed(isStashed));
// Return the stashed handle to its default scale in case it was changed as part of the
@@ -610,26 +675,73 @@
secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
- mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
+ as.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
secondHalfAnimatorSet);
- mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mIsStashed = isStashed;
- onIsStashedChanged(mIsStashed);
- cancelTimeoutIfExists();
+ }
+
+ private void createTransientAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration,
+ boolean animateBg, int changedFlags, boolean skipStashAnimation) {
+ Interpolator skipInterpolator = null;
+
+ if (isStashed) {
+ if (animateBg) {
+ play(as, mTaskbarBackgroundOffset.animateToValue(1), 0, duration, EMPHASIZED);
+ } else {
+ as.addListener(AnimatorListeners.forEndCallback(
+ () -> mTaskbarBackgroundOffset.updateValue(1)));
}
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimator = null;
+ long alphaStartDelay = duration == 0 ? 0 : (changedFlags == FLAG_IN_APP)
+ ? TASKBAR_STASH_ICON_ALPHA_HOME_TO_APP_START_DELAY
+ : TASKBAR_STASH_ALPHA_START_DELAY;
+ long alphaDuration = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_DURATION;
+ play(as, mIconAlphaForStash.animateToValue(0), alphaStartDelay, alphaDuration, LINEAR);
+ play(as, mTaskbarStashedHandleAlpha.animateToValue(1), alphaStartDelay,
+ Math.max(0, duration - alphaStartDelay), LINEAR);
- if (!mIsStashed) {
- tryStartTaskbarTimeout();
- }
+ if (skipStashAnimation) {
+ skipInterpolator = INSTANT;
}
- });
+ } else {
+ if (animateBg) {
+ play(as, mTaskbarBackgroundOffset.animateToValue(0), 0, duration, EMPHASIZED);
+ } else {
+ as.addListener(AnimatorListeners.forEndCallback(
+ () -> mTaskbarBackgroundOffset.updateValue(0)));
+ }
+
+ long alphaStartDelay = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_START_DELAY;
+ long alphaDuration = duration == 0 ? 0 : TASKBAR_STASH_ALPHA_DURATION;
+ play(as, mIconAlphaForStash.animateToValue(1), alphaStartDelay, alphaDuration, LINEAR);
+ play(as, mTaskbarStashedHandleAlpha.animateToValue(0), 0, alphaDuration, LINEAR);
+
+ if (skipStashAnimation) {
+ skipInterpolator = FINAL_FRAME;
+ }
+ }
+ play(as, mControllers.taskbarViewController
+ .createRevealAnimToIsStashed(isStashed, isInApp()), 0, duration, EMPHASIZED);
+
+ if (skipInterpolator != null) {
+ as.setInterpolator(skipInterpolator);
+ }
+
+ play(as, mControllers.stashedHandleViewController
+ .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED);
+
+ // Return the stashed handle to its default scale in case it was changed as part of the
+ // feedforward hint. Note that the reveal animation above also visually scales it.
+ as.play(mTaskbarStashedHandleHintScale.animateToValue(1f)
+ .setDuration(isStashed ? duration / 2 : duration));
+ }
+
+ private static void play(AnimatorSet as, Animator a, long startDelay, long duration,
+ Interpolator interpolator) {
+ a.setDuration(duration);
+ a.setStartDelay(startDelay);
+ a.setInterpolator(interpolator);
+ as.play(a);
}
private void addJankMonitorListener(AnimatorSet animator, boolean expanding) {
@@ -711,7 +823,6 @@
}
}
-
/**
* Returns an animator which applies the latest state if mIsStashed is changed, or {@code null}
* otherwise.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 76b8b6d..3479255 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -25,6 +25,7 @@
import android.view.View;
import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.model.data.ItemInfo;
@@ -75,6 +76,11 @@
protected void onStashedInAppChanged() { }
+ /**
+ * Called when taskbar icon layout bounds change.
+ */
+ protected void onIconLayoutBoundsChanged() { }
+
/** Called when an icon is launched. */
@CallSuper
public void onTaskbarIconLaunched(ItemInfo item) {
@@ -183,9 +189,9 @@
if (recentsView == null) {
return;
}
- recentsView.findLastActiveTaskAndRunCallback(
+ recentsView.getSplitSelectController().findLastActiveTaskAndRunCallback(
splitSelectSource.intent.getComponent(),
- (Consumer<Task>) foundTask -> {
+ foundTask -> {
splitSelectSource.alreadyRunningTaskId = foundTask == null
? INVALID_TASK_ID
: foundTask.key.id;
@@ -200,9 +206,9 @@
*/
public void triggerSecondAppForSplit(ItemInfoWithIcon info, Intent intent, View startingView) {
RecentsView recents = getRecentsView();
- recents.findLastActiveTaskAndRunCallback(
+ recents.getSplitSelectController().findLastActiveTaskAndRunCallback(
info.getTargetComponent(),
- (Consumer<Task>) foundTask -> {
+ foundTask -> {
if (foundTask != null) {
TaskView foundTaskView = recents.getTaskViewByTaskId(foundTask.key.id);
// TODO (b/266482558): This additional null check is needed because there
@@ -248,15 +254,6 @@
}
/**
- * Closes the Keyboard Quick Switch View.
- *
- * No-op if the view is already closed
- */
- public void closeQuickSwitchView() {
- mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
- }
-
- /**
* Launches the focused task and closes the Keyboard Quick Switch View.
*
* If the overlay or view are closed, or the overview task is focused, then Overview is
@@ -275,5 +272,5 @@
*
* No-op if the view is not yet open.
*/
- public void launchSplitTasks(View taskview, GroupTask groupTask) { }
+ public void launchSplitTasks(@NonNull View taskview, @NonNull GroupTask groupTask) { }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index eddc278..c1e85aa 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -58,6 +58,8 @@
private static final String TAG = TaskbarView.class.getSimpleName();
private static final float TASKBAR_BACKGROUND_LUMINANCE = 0.30f;
+ private static final Rect sTmpRect = new Rect();
+
public int mThemeIconsBackground;
private final int[] mTempOutLocation = new int[2];
@@ -74,9 +76,6 @@
private View.OnClickListener mIconClickListener;
private View.OnLongClickListener mIconLongClickListener;
- // Prevents dispatching touches to children if true
- private boolean mTouchEnabled = true;
-
// Only non-null when the corresponding Folder is open.
private @Nullable FolderIcon mLeaveBehindFolderIcon;
@@ -339,6 +338,9 @@
(right - navSpaceNeeded) - iconEnd;
iconEnd += offset;
}
+
+ sTmpRect.set(mIconLayoutBounds);
+
// Layout the children
mIconLayoutBounds.right = iconEnd;
mIconLayoutBounds.top = (bottom - top - mIconTouchSize) / 2;
@@ -379,14 +381,10 @@
mIconLayoutBounds.right = center + distanceFromCenter;
mIconLayoutBounds.left = center - distanceFromCenter;
}
- }
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- if (!mTouchEnabled) {
- return true;
+ if (!sTmpRect.equals(mIconLayoutBounds)) {
+ mControllerCallbacks.notifyIconLayoutBoundsChanged();
}
- return super.dispatchTouchEvent(ev);
}
@Override
@@ -397,9 +395,6 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (!mTouchEnabled) {
- return true;
- }
if (mIconLayoutBounds.left <= event.getX()
&& event.getX() <= mIconLayoutBounds.right
&& !DisplayController.isTransientTaskbar(mActivityContext)) {
@@ -420,11 +415,6 @@
return super.onTouchEvent(event);
}
- public void setTouchesEnabled(boolean touchEnabled) {
- this.mTouchEnabled = touchEnabled;
- mControllerCallbacks.clearTouchInProgress();
- }
-
/**
* Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
* touch bounds.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6252e60..c708838 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -17,9 +17,9 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.AnimatedFloat.VALUE;
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
@@ -28,6 +28,7 @@
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.Rect;
@@ -199,14 +200,6 @@
}
/**
- * Should be called when the IME visibility changes, so we can make Taskbar not steal touches.
- */
- public void setImeIsVisible(boolean isImeVisible) {
- mTaskbarView.setTouchesEnabled(!isImeVisible
- || DisplayController.isTransientTaskbar(mActivity));
- }
-
- /**
* Should be called when the recents button is disabled, so we can hide taskbar icons as well.
*/
public void setRecentsButtonDisabled(boolean isDisabled) {
@@ -310,13 +303,48 @@
* @param isStashed When true, the icon crops vertically to the size of the stashed handle.
* When false, the reverse happens.
*/
- public AnimatorSet createRevealAnimToIsStashed(boolean isStashed) {
+ public AnimatorSet createRevealAnimToIsStashed(boolean isStashed, boolean isInApp) {
AnimatorSet as = new AnimatorSet();
- for (int i = mTaskbarView.getChildCount() - 1; i >= 0; i--) {
+
+ Rect stashedBounds = new Rect();
+ mControllers.stashedHandleViewController.getStashedHandleBounds(stashedBounds);
+
+ int numChildren = mTaskbarView.getChildCount();
+ // We do not actually modify the width of the icons, but we will use this width to position
+ // the children to overlay the nav handle.
+ float virtualChildWidth = stashedBounds.width() / (float) numChildren;
+
+ for (int i = numChildren - 1; i >= 0; i--) {
View child = mTaskbarView.getChildAt(i);
- if (child instanceof BubbleTextView) {
- as.play(createRevealAnimForView(child, isStashed));
+
+ // Crop the icons to/from the nav handle shape.
+ as.play(createRevealAnimForView(child, isStashed));
+
+ // Translate the icons to/from their locations as the "nav handle."
+ // We look at 'left' and 'right' values to ensure that the children stay within the
+ // bounds of the stashed handle.
+ float iconLeft = child.getLeft();
+ float newLeft = stashedBounds.left + (virtualChildWidth * i);
+ final float croppedTransX;
+ if (iconLeft > newLeft) {
+ float newRight = stashedBounds.right - (virtualChildWidth * (numChildren - 1 - i));
+ croppedTransX = -(child.getLeft() + child.getWidth() - newRight);
+ } else {
+ croppedTransX = newLeft - iconLeft;
}
+
+ as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_X, isStashed
+ ? new float[] {croppedTransX}
+ : new float[] {croppedTransX, 0}));
+
+ float croppedTransY = child.getHeight() - stashedBounds.height();
+ as.play(ObjectAnimator.ofFloat(child, ICON_REVEAL_TRANSLATE_Y, isStashed
+ ? new float[] {croppedTransY}
+ : new float[] {croppedTransY, 0}));
+ as.addListener(forEndCallback(() -> {
+ ICON_REVEAL_TRANSLATE_X.set(child, 0f);
+ ICON_REVEAL_TRANSLATE_Y.set(child, 0f);
+ }));
}
return as;
}
@@ -409,7 +437,7 @@
float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
- setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+ setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
if (mIsHotseatIconOnTopWhenAligned) {
setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
@@ -448,7 +476,7 @@
float childCenter = (child.getLeft() + child.getRight()) / 2f;
setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
- setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
+ setter.setFloat(child, ICON_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
}
@@ -626,6 +654,13 @@
public void clearTouchInProgress() {
mTouchInProgress = false;
}
+
+ /**
+ * Notifies launcher to update icon alignment.
+ */
+ public void notifyIconLayoutBoundsChanged() {
+ mControllers.uiController.onIconLayoutBoundsChanged();
+ }
}
public static final FloatProperty<View> ICON_TRANSLATE_X =
@@ -636,7 +671,7 @@
if (view instanceof BubbleTextView) {
((BubbleTextView) view).setTranslationXForTaskbarAlignmentAnimation(v);
} else if (view instanceof FolderIcon) {
- ((FolderIcon) view).setTranslationForTaskbarAlignmentAnimation(v);
+ ((FolderIcon) view).setTranslationXForTaskbarAlignmentAnimation(v);
} else {
view.setTranslationX(v);
}
@@ -653,4 +688,81 @@
return view.getTranslationX();
}
};
+
+ public static final FloatProperty<View> ICON_TRANSLATE_Y =
+ new FloatProperty<View>("taskbarAlignmentTranslateY") {
+
+ @Override
+ public void setValue(View view, float v) {
+ if (view instanceof BubbleTextView) {
+ ((BubbleTextView) view).setTranslationYForTaskbarAlignmentAnimation(v);
+ } else if (view instanceof FolderIcon) {
+ ((FolderIcon) view).setTranslationYForTaskbarAlignmentAnimation(v);
+ } else {
+ view.setTranslationY(v);
+ }
+ }
+
+ @Override
+ public Float get(View view) {
+ if (view instanceof BubbleTextView) {
+ return ((BubbleTextView) view)
+ .getTranslationYForTaskbarAlignmentAnimation();
+ } else if (view instanceof FolderIcon) {
+ return ((FolderIcon) view).getTranslationYForTaskbarAlignmentAnimation();
+ }
+ return view.getTranslationY();
+ }
+ };
+
+ public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_X =
+ new FloatProperty<View>("taskbarRevealTranslateX") {
+
+ @Override
+ public void setValue(View view, float v) {
+ if (view instanceof BubbleTextView) {
+ ((BubbleTextView) view).setTranslationXForTaskbarRevealAnimation(v);
+ } else if (view instanceof FolderIcon) {
+ ((FolderIcon) view).setTranslationXForTaskbarRevealAnimation(v);
+ } else {
+ view.setTranslationX(v);
+ }
+ }
+
+ @Override
+ public Float get(View view) {
+ if (view instanceof BubbleTextView) {
+ return ((BubbleTextView) view).getTranslationXForTaskbarRevealAnimation();
+ } else if (view instanceof FolderIcon) {
+ return ((FolderIcon) view).getTranslationXForTaskbarRevealAnimation();
+ }
+ return view.getTranslationX();
+ }
+ };
+
+ public static final FloatProperty<View> ICON_REVEAL_TRANSLATE_Y =
+ new FloatProperty<View>("taskbarRevealTranslateY") {
+
+ @Override
+ public void setValue(View view, float v) {
+ if (view instanceof BubbleTextView) {
+ ((BubbleTextView) view).setTranslationYForTaskbarRevealAnimation(v);
+ } else if (view instanceof FolderIcon) {
+ ((FolderIcon) view).setTranslationYForTaskbarRevealAnimation(v);
+ } else {
+ view.setTranslationY(v);
+ }
+ }
+
+ @Override
+ public Float get(View view) {
+ if (view instanceof BubbleTextView) {
+ return ((BubbleTextView) view).getTranslationYForTaskbarRevealAnimation();
+ } else if (view instanceof FolderIcon) {
+ return ((FolderIcon) view).getTranslationYForTaskbarRevealAnimation();
+ }
+ return view.getTranslationY();
+ }
+ };
+
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 80ce369..3dbe6c8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -579,9 +579,9 @@
RecentsView recentsView = getOverviewPanel();
// Check if there is already an instance of this app running, if so, initiate the split
// using that.
- recentsView.findLastActiveTaskAndRunCallback(
+ mSplitSelectStateController.findLastActiveTaskAndRunCallback(
splitSelectSource.intent.getComponent(),
- (Consumer<Task>) foundTask -> {
+ foundTask -> {
splitSelectSource.alreadyRunningTaskId = foundTask == null
? INVALID_TASK_ID
: foundTask.key.id;
@@ -595,8 +595,7 @@
}
/** TODO(b/266482558) Migrate into SplitSelectStateController or someplace split specific. */
- private void startSplitToHome(
- SplitSelectSource source) {
+ private void startSplitToHome(SplitSelectSource source) {
AbstractFloatingView.closeAllOpenViews(this);
int splitPlaceholderSize = getResources().getDimensionPixelSize(
R.dimen.split_placeholder_size);
@@ -604,14 +603,14 @@
R.dimen.split_placeholder_inset);
Rect tempRect = new Rect();
- SplitSelectStateController controller = getSplitSelectStateController();
- controller.setInitialTaskSelect(source.intent, source.position.stagePosition,
- source.itemInfo, source.splitEvent, source.alreadyRunningTaskId);
+ mSplitSelectStateController.setInitialTaskSelect(source.intent,
+ source.position.stagePosition, source.itemInfo, source.splitEvent,
+ source.alreadyRunningTaskId);
RecentsView recentsView = getOverviewPanel();
recentsView.getPagedOrientationHandler().getInitialSplitPlaceholderBounds(
splitPlaceholderSize, splitPlaceholderInset, getDeviceProfile(),
- controller.getActiveSplitStagePosition(), tempRect);
+ mSplitSelectStateController.getActiveSplitStagePosition(), tempRect);
PendingAnimation anim = new PendingAnimation(TABLET_HOME_TO_SPLIT.getDuration());
RectF startingTaskRect = new RectF();
@@ -620,12 +619,12 @@
floatingTaskView.setAlpha(1);
floatingTaskView.addStagingAnimation(anim, startingTaskRect, tempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
- controller.setFirstFloatingTaskView(floatingTaskView);
+ mSplitSelectStateController.setFirstFloatingTaskView(floatingTaskView);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationCancel(Animator animation) {
getDragLayer().removeView(floatingTaskView);
- controller.resetState();
+ mSplitSelectStateController.resetState();
}
});
anim.buildAnim().start();
@@ -1156,14 +1155,12 @@
// Launcher to first restore into Overview state, wait for the relevant tasks and icons to
// load in, and then proceed to OverviewSplitSelect.
if (isInState(OVERVIEW_SPLIT_SELECT)) {
- SplitSelectStateController splitSelectStateController =
- ((RecentsView) getOverviewPanel()).getSplitSelectController();
// Launcher will restart in Overview and then transition to OverviewSplitSelect.
outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
new PendingSplitSelectInfo(
- splitSelectStateController.getInitialTaskId(),
- splitSelectStateController.getActiveSplitStagePosition(),
- splitSelectStateController.getSplitEvent())
+ mSplitSelectStateController.getInitialTaskId(),
+ mSplitSelectStateController.getActiveSplitStagePosition(),
+ mSplitSelectStateController.getSplitEvent())
));
outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
}
@@ -1214,7 +1211,7 @@
*
* If the second split task is missing, launches the first task normally.
*/
- public void launchSplitTasks(View taskView, GroupTask groupTask) {
+ public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
if (groupTask.task2 == null) {
UI_HELPER_EXECUTOR.execute(() ->
ActivityManagerWrapper.getInstance().startActivityFromRecents(
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 95eb128..8125fc8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -116,6 +116,18 @@
return super.isTaskbarAlignedWithHotseat(launcher);
}
+ @Override
+ public boolean disallowTaskbarGlobalDrag() {
+ // Enable global drag in overview
+ return false;
+ }
+
+ @Override
+ public boolean allowTaskbarInitialSplitSelection() {
+ // Disallow split select from taskbar items in overview
+ return false;
+ }
+
public static float[] getOverviewScaleAndOffsetForBackgroundState(
BaseDraggingActivity activity) {
return new float[] {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 0c49e5f..b9221ee 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -18,12 +18,12 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Rect;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.views.RecentsView;
/**
@@ -70,13 +70,22 @@
}
}
- public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
- Point taskSize = activity.<RecentsView>getOverviewPanel().getSelectedTaskSize();
- Rect modalTaskSize = new Rect();
- activity.<RecentsView>getOverviewPanel().getModalTaskSize(modalTaskSize);
+ @Override
+ public boolean isTaskbarStashed(Launcher launcher) {
+ if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+ return true;
+ }
+ return super.isTaskbarStashed(launcher);
+ }
- float scale = Math.min((float) modalTaskSize.height() / taskSize.y,
- (float) modalTaskSize.width() / taskSize.x);
+ public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
+ RecentsView recentsView = activity.<RecentsView>getOverviewPanel();
+ Rect taskSize = recentsView.getSelectedTaskBounds();
+ Rect modalTaskSize = new Rect();
+ recentsView.getModalTaskSize(modalTaskSize);
+
+ float scale = Math.min((float) modalTaskSize.height() / taskSize.height(),
+ (float) modalTaskSize.width() / taskSize.width());
return new float[] {scale, NO_OFFSET};
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d075750..233ed46 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -124,6 +124,18 @@
}
@Override
+ public boolean disallowTaskbarGlobalDrag() {
+ // Disable global drag in overview
+ return true;
+ }
+
+ @Override
+ public boolean allowTaskbarInitialSplitSelection() {
+ // Allow split select from taskbar items in overview
+ return true;
+ }
+
+ @Override
public String getDescription(Launcher launcher) {
return launcher.getString(R.string.accessibility_recent_apps);
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index f9ad749..998439e 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -52,6 +52,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
@@ -339,12 +340,21 @@
*/
public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
calculateTaskSize(context, dp, outRect);
- float maxScale = context.getResources().getFloat(R.dimen.overview_modal_max_scale);
+ boolean isGridOnlyOverview = dp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get();
+ int claimedSpaceBelow = isGridOnlyOverview
+ ? dp.overviewActionsTopMarginPx + dp.overviewActionsHeight + dp.stashedTaskbarSize
+ : (dp.heightPx - outRect.bottom - dp.getInsets().bottom);
+ int minimumHorizontalPadding = 0;
+ if (!isGridOnlyOverview) {
+ float maxScale = context.getResources().getFloat(R.dimen.overview_modal_max_scale);
+ minimumHorizontalPadding =
+ Math.round((dp.availableWidthPx - outRect.width() * maxScale) / 2);
+ }
calculateTaskSizeInternal(
context, dp,
dp.overviewTaskMarginPx,
- dp.heightPx - outRect.bottom - dp.getInsets().bottom,
- Math.round((dp.availableWidthPx - outRect.width() * maxScale) / 2),
+ claimedSpaceBelow,
+ minimumHorizontalPadding,
1f /*maxScale*/,
Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
outRect);
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index d0fd65f..ac5b2f2 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -37,6 +37,7 @@
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.RunnableList;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -185,6 +186,11 @@
&& dp != null
&& (dp.isTablet || dp.isTwoPanels);
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ // TODO(b/268075592): add support for quickswitch to/from desktop
+ allowQuickSwitch = false;
+ }
+
if (cmd.type == TYPE_HIDE) {
if (!allowQuickSwitch) {
return true;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 1f522c1..9a23557 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,6 +17,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -51,8 +52,10 @@
import android.graphics.Region;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserManager;
import android.provider.Settings;
import android.view.MotionEvent;
@@ -62,9 +65,9 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -108,6 +111,15 @@
private final boolean mIsOneHandedModeSupported;
private boolean mPipIsActive;
+ private boolean mIsUserUnlocked;
+ private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
+ private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
+ if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
+ mIsUserUnlocked = true;
+ notifyUserUnlocked();
+ }
+ });
+
private int mGestureBlockingTaskId = -1;
private @NonNull Region mExclusionRegion = new Region();
private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -133,6 +145,14 @@
runOnDestroy(mRotationTouchHelper::destroy);
}
+ // Register for user unlocked if necessary
+ mIsUserUnlocked = context.getSystemService(UserManager.class)
+ .isUserUnlocked(Process.myUserHandle());
+ if (!mIsUserUnlocked) {
+ mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
+ }
+ runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));
+
// Register for exclusion updates
mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
@Override
@@ -292,12 +312,39 @@
}
/**
+ * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
+ * will be called back immediately.
+ */
+ public void runOnUserUnlocked(Runnable action) {
+ if (mIsUserUnlocked) {
+ action.run();
+ } else {
+ mUserUnlockedActions.add(action);
+ }
+ }
+
+ /**
+ * @return whether the user is unlocked.
+ */
+ public boolean isUserUnlocked() {
+ return mIsUserUnlocked;
+ }
+
+ /**
* @return whether the user has completed setup wizard
*/
public boolean isUserSetupComplete() {
return mIsUserSetupComplete;
}
+ private void notifyUserUnlocked() {
+ for (Runnable action : mUserUnlockedActions) {
+ action.run();
+ }
+ mUserUnlockedActions.clear();
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
+ }
+
/**
* Sets the task id where gestures should be blocked
*/
@@ -542,7 +589,7 @@
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
- pw.println(" isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked());
+ pw.println(" isUserUnlocked=" + mIsUserUnlocked);
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 0dcd723..72330ef 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -80,9 +80,9 @@
boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
boolean isTablet = activity.getDeviceProfile().isTablet;
+ boolean isGridOnlyOverview = isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get();
// Add overview actions to the menu when in in-place rotate landscape mode.
- if ((!canLauncherRotate && isInLandscape)
- || (isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get())) {
+ if ((!canLauncherRotate && isInLandscape) || isGridOnlyOverview) {
// Add screenshot action to task menu.
List<SystemShortcut> screenshotShortcuts = TaskShortcutFactory.SCREENSHOT
.getShortcuts(activity, taskContainer);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1b8a93c..61caef2 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -88,7 +88,6 @@
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -412,8 +411,8 @@
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
// Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
- LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
- LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
+ mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
+ mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
ProtoTracer.INSTANCE.get(this).add(this);
@@ -483,7 +482,7 @@
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
- if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
+ if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
// Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
// mode doesn't have gestures
return;
@@ -526,7 +525,7 @@
@UiThread
private void onSystemUiFlagsChanged(int lastSysUIFlags) {
- if (LockedUserState.get(this).isUserUnlocked()) {
+ if (mDeviceState.isUserUnlocked()) {
int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
@@ -571,7 +570,7 @@
@UiThread
private void onAssistantVisibilityChanged() {
- if (LockedUserState.get(this).isUserUnlocked()) {
+ if (mDeviceState.isUserUnlocked()) {
mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
mDeviceState.getAssistantVisibility());
}
@@ -581,7 +580,7 @@
public void onDestroy() {
Log.d(TAG, "Touch service destroyed: user=" + getUserId());
sIsInitialized = false;
- if (LockedUserState.get(this).isUserUnlocked()) {
+ if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
}
@@ -615,7 +614,7 @@
TestLogging.recordMotionEvent(
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
- if (!LockedUserState.get(this).isUserUnlocked()) {
+ if (!mDeviceState.isUserUnlocked()) {
return;
}
@@ -637,8 +636,7 @@
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
mUncheckedConsumer = mConsumer;
- } else if (LockedUserState.get(this).isUserUnlocked()
- && mDeviceState.isFullyGesturalNavMode()
+ } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
mGestureState = createGestureState(mGestureState);
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
@@ -758,7 +756,7 @@
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
- if (!LockedUserState.get(this).isUserUnlocked()) {
+ if (!mDeviceState.isUserUnlocked()) {
CompoundString reasonString = newCompoundString("device locked");
InputConsumer consumer;
if (canStartSystemGesture) {
@@ -1105,7 +1103,7 @@
}
private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
- if (!LockedUserState.get(this).isUserUnlocked()) {
+ if (!mDeviceState.isUserUnlocked()) {
return;
}
@@ -1137,7 +1135,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
- if (!LockedUserState.get(this).isUserUnlocked()) {
+ if (!mDeviceState.isUserUnlocked()) {
return;
}
final BaseActivityInterface activityInterface =
@@ -1178,7 +1176,7 @@
} else {
// Dump everything
FeatureFlags.dump(pw);
- if (LockedUserState.get(this).isUserUnlocked()) {
+ if (mDeviceState.isUserUnlocked()) {
PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
}
mDeviceState.dump(pw);
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index db6d56b..4b1dd43 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.fallback;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -200,8 +202,9 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState, boolean animate) {
- if (isModalState) {
+ public void setModalStateEnabled(int taskId, boolean animate) {
+ if (taskId != INVALID_TASK_ID) {
+ setSelectedTask(taskId);
mActivity.getStateManager().goToState(RecentsState.MODAL_TASK, animate);
} else {
if (mActivity.isInState(RecentsState.MODAL_TASK)) {
diff --git a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
index 543ca89..2a8bfa2 100644
--- a/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/BaseUnfoldMoveFromCenterAnimator.java
@@ -100,22 +100,34 @@
mMoveFromCenterAnimation.registerViewForAnimation(view);
}
- protected void disableClipping(ViewGroup view) {
+ /**
+ * Sets clipToPadding for the view which then could be restored to the original value
+ * using {@link BaseUnfoldMoveFromCenterAnimator#restoreClippings} method call
+ * @param view view to set the property
+ * @param clipToPadding value of the property
+ */
+ protected void setClipToPadding(ViewGroup view, boolean clipToPadding) {
mOriginalClipToPadding.put(view, view.getClipToPadding());
- mOriginalClipChildren.put(view, view.getClipChildren());
- view.setClipToPadding(false);
- view.setClipChildren(false);
+ view.setClipToPadding(clipToPadding);
}
- protected void restoreClipping(ViewGroup view) {
- final Boolean originalClipToPadding = mOriginalClipToPadding.get(view);
- if (originalClipToPadding != null) {
- view.setClipToPadding(originalClipToPadding);
- }
- final Boolean originalClipChildren = mOriginalClipChildren.get(view);
- if (originalClipChildren != null) {
- view.setClipChildren(originalClipChildren);
- }
+ /**
+ * Sets clipChildren for the view which then could be restored to the original value
+ * using {@link BaseUnfoldMoveFromCenterAnimator#restoreClippings} method call
+ * @param view view to set the property
+ * @param clipChildren value of the property
+ */
+ protected void setClipChildren(ViewGroup view, boolean clipChildren) {
+ mOriginalClipChildren.put(view, view.getClipChildren());
+ view.setClipChildren(clipChildren);
+ }
+
+ /**
+ * Restores original clip properties after their modifications
+ */
+ protected void restoreClippings() {
+ mOriginalClipToPadding.forEach(ViewGroup::setClipToPadding);
+ mOriginalClipChildren.forEach(ViewGroup::setClipChildren);
}
private class UnfoldMoveFromCenterRotationListener implements
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 1b2bfc9..51afb5b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -29,6 +29,7 @@
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -59,6 +60,7 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+import com.android.quickstep.RecentsModel;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskAnimationManager;
import com.android.quickstep.TaskViewUtils;
@@ -79,10 +81,12 @@
private final Context mContext;
private final Handler mHandler;
+ private final RecentsModel mRecentTasksModel;
private StatsLogManager mStatsLogManager;
private final SystemUiProxy mSystemUiProxy;
private final StateManager mStateManager;
- private final DepthController mDepthController;
+ @Nullable
+ private DepthController mDepthController;
private @StagePosition int mStagePosition;
private ItemInfo mItemInfo;
private Intent mInitialTaskIntent;
@@ -110,6 +114,7 @@
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
mStateManager = stateManager;
mDepthController = depthController;
+ mRecentTasksModel = RecentsModel.INSTANCE.get(context);
}
/**
@@ -149,6 +154,50 @@
}
/**
+ * Pulls the list of active Tasks from RecentsModel, and finds the most recently active Task
+ * matching a given ComponentName. Then uses that Task (which could be null) with the given
+ * callback.
+ *
+ * Used in various task-switching or splitscreen operations when we need to check if there is a
+ * currently running Task of a certain type and use the most recent one.
+ */
+ public void findLastActiveTaskAndRunCallback(ComponentName componentName,
+ Consumer<Task> callback) {
+ mRecentTasksModel.getTasks(taskGroups -> {
+ Task lastActiveTask = null;
+ // Loop through tasks in reverse, since they are ordered with most-recent tasks last.
+ for (int i = taskGroups.size() - 1; i >= 0; i--) {
+ GroupTask groupTask = taskGroups.get(i);
+ Task task1 = groupTask.task1;
+ if (isInstanceOfComponent(task1, componentName)) {
+ lastActiveTask = task1;
+ break;
+ }
+ Task task2 = groupTask.task2;
+ if (isInstanceOfComponent(task2, componentName)) {
+ lastActiveTask = task2;
+ break;
+ }
+ }
+
+ callback.accept(lastActiveTask);
+ });
+ }
+
+ /**
+ * Checks if a given Task is the most recently-active Task of type componentName. Used for
+ * selecting already-running Tasks for splitscreen.
+ */
+ public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
+ // Exclude the task that is already staged
+ if (task == null || task.key.id == mInitialTaskId) {
+ return false;
+ }
+
+ return task.key.baseIntent.getComponent().equals(componentName);
+ }
+
+ /**
* To be called when the actual tasks ({@link #mInitialTaskId}, {@link #mSecondTaskId}) are
* to be launched. Call after launcher side animations are complete.
*/
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
index 01a997a..70a12d6 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterHotseatAnimator.java
@@ -41,7 +41,8 @@
Hotseat hotseat = mLauncher.getHotseat();
ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
- disableClipping(hotseat);
+ setClipChildren(hotseat, false);
+ setClipToPadding(hotseat, false);
for (int i = 0; i < hotseatIcons.getChildCount(); i++) {
View child = hotseatIcons.getChildAt(i);
@@ -51,7 +52,7 @@
@Override
public void onTransitionFinished() {
- restoreClipping(mLauncher.getHotseat());
+ restoreClippings();
super.onTransitionFinished();
}
}
diff --git a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
index 95a4b8f..7da103e 100644
--- a/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/UnfoldMoveFromCenterWorkspaceAnimator.java
@@ -47,7 +47,8 @@
final CellLayout cellLayout = (CellLayout) page;
ShortcutAndWidgetContainer itemsContainer = cellLayout
.getShortcutsAndWidgets();
- disableClipping(cellLayout);
+ setClipChildren(cellLayout, false);
+ setClipToPadding(cellLayout, false);
for (int i = 0; i < itemsContainer.getChildCount(); i++) {
View child = itemsContainer.getChildAt(i);
@@ -55,13 +56,13 @@
}
});
- disableClipping(workspace);
+ setClipChildren(workspace, false);
+ setClipToPadding(workspace, true);
}
@Override
public void onTransitionFinished() {
- restoreClipping(mLauncher.getWorkspace());
- mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page));
+ restoreClippings();
super.onTransitionFinished();
}
}
diff --git a/quickstep/src/com/android/quickstep/views/LaunchableConstraintLayout.kt b/quickstep/src/com/android/quickstep/views/LaunchableConstraintLayout.kt
new file mode 100644
index 0000000..537eca1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/LaunchableConstraintLayout.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.quickstep.views
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
+
+/** A [ConstraintLayout] that also implements [LaunchableView]. */
+open class LaunchableConstraintLayout : ConstraintLayout, LaunchableView {
+ private val delegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ )
+
+ constructor(context: Context) : super(context)
+ constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ ) : super(context, attrs, defStyleAttr)
+
+ constructor(
+ context: Context,
+ attrs: AttributeSet?,
+ defStyleAttr: Int,
+ defStyleRes: Int,
+ ) : super(context, attrs, defStyleAttr, defStyleRes)
+
+ override fun setShouldBlockVisibilityChanges(block: Boolean) {
+ delegate.setShouldBlockVisibilityChanges(block)
+ }
+
+ override fun setVisibility(visibility: Int) {
+ // Note that super.setVisibility() is passed to the delegate upon creation and called by it.
+ // This method is just a passthrough if no animation is in progress, whereas otherwise it
+ // caches the passed value and restores it at the end of the animation.
+ delegate.setVisibility(visibility)
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index ff26129..c165acc 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.views;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -171,8 +173,9 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState, boolean animate) {
- if (isModalState) {
+ public void setModalStateEnabled(int taskId, boolean animate) {
+ if (taskId != INVALID_TASK_ID) {
+ setSelectedTask(taskId);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK, animate);
} else {
if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 0d21e60..f578b87 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -303,6 +303,10 @@
return mDp.getOverviewActionsClaimedSpaceBelow();
}
+ if (mDp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+ return mDp.stashedTaskbarSize;
+ }
+
// Align to bottom of task Rect.
return mDp.heightPx - mTaskSize.bottom - mDp.overviewActionsTopMarginPx
- mDp.overviewActionsHeight;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5ecc05a..e529b04 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -453,6 +453,7 @@
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
+ private TaskView mSelectedTask = null;
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
@Nullable
protected Float mLastComputedTaskStartPushOutDistance = null;
@@ -1084,6 +1085,16 @@
super.draw(canvas);
}
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ if (isModal()) {
+ // Do not scroll when clicking on a modal grid task, as it will already be centered
+ // on screen.
+ return false;
+ }
+ return super.requestChildRectangleOnScreen(child, rectangle, immediate);
+ }
+
public void addSideTaskLaunchCallback(RunnableList callback) {
if (mSideTaskLaunchCallback == null) {
mSideTaskLaunchCallback = new RunnableList();
@@ -1272,7 +1283,7 @@
*/
@Nullable
public TaskView getTaskViewByTaskId(int taskId) {
- if (taskId == -1) {
+ if (taskId == INVALID_TASK_ID) {
return null;
}
@@ -1285,53 +1296,6 @@
return null;
}
- /**
- * Pulls the list of active Tasks from RecentsModel, and finds the most recently active Task
- * matching a given ComponentName. Then uses that Task (which could be null) with the given
- * callback.
- *
- * Used in various task-switching or splitscreen operations when we need to check if there is a
- * currently running Task of a certain type and use the most recent one.
- */
- public void findLastActiveTaskAndRunCallback(ComponentName componentName,
- Consumer<Task> callback) {
- mModel.getTasks(taskGroups -> {
- Task lastActiveTask = null;
- // Loop through tasks in reverse, since they are ordered with most-recent tasks last.
- for (int i = taskGroups.size() - 1; i >= 0; i--) {
- GroupTask groupTask = taskGroups.get(i);
- Task task1 = groupTask.task1;
- if (isInstanceOfComponent(task1, componentName)) {
- lastActiveTask = task1;
- break;
- }
- Task task2 = groupTask.task2;
- if (isInstanceOfComponent(task2, componentName)) {
- lastActiveTask = task2;
- break;
- }
- }
-
- callback.accept(lastActiveTask);
- });
- }
-
- /**
- * Checks if a given Task is the most recently-active Task of type componentName. Used for
- * selecting already-running Tasks for splitscreen.
- */
- public boolean isInstanceOfComponent(@Nullable Task task, ComponentName componentName) {
- if (task == null) {
- return false;
- }
- // Exclude the task that is already staged
- if (mSplitHiddenTaskView != null && mSplitHiddenTaskView.getTask().equals(task)) {
- return false;
- }
-
- return task.key.baseIntent.getComponent().equals(componentName);
- }
-
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
@@ -1967,7 +1931,7 @@
private void onOrientationChanged() {
// If overview is in modal state when rotate, reset it to overview state without running
// animation.
- setModalStateEnabled(/* isModalState= */ false, /* animate= */ false);
+ setModalStateEnabled(/* taskId= */ INVALID_TASK_ID, /* animate= */ false);
if (isSplitSelectionActive()) {
onRotateInSplitSelectionState();
}
@@ -2040,12 +2004,33 @@
}
/**
- * Returns the size of task selected to enter modal state.
+ * Sets the last TaskView selected.
*/
- public Point getSelectedTaskSize() {
- mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(),
- mTempRect);
- return new Point(mTempRect.width(), mTempRect.height());
+ public void setSelectedTask(int lastSelectedTaskId) {
+ mSelectedTask = getTaskViewByTaskId(lastSelectedTaskId);
+ }
+
+ /**
+ * Returns the bounds of the task selected to enter modal state.
+ */
+ public Rect getSelectedTaskBounds() {
+ if (mSelectedTask == null) {
+ return mLastComputedTaskSize;
+ }
+ return getTaskBounds(mSelectedTask);
+ }
+
+ private Rect getTaskBounds(TaskView taskView) {
+ int selectedPage = indexOfChild(taskView);
+ int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+ int selectedPageScroll = getScrollForPage(selectedPage);
+ boolean isTopRow = taskView != null && mTopRowIdSet.contains(taskView.getTaskViewId());
+ Rect outRect = new Rect(mLastComputedTaskSize);
+ outRect.offset(
+ -(primaryScroll - (selectedPageScroll + getOffsetFromScrollPosition(selectedPage))),
+ (int) (showAsGrid() && ENABLE_GRID_ONLY_OVERVIEW.get() && !isTopRow
+ ? mTopBottomRowHeightDiff : 0));
+ return outRect;
}
/** Gets the last computed task size */
@@ -4169,8 +4154,13 @@
private void updatePivots() {
if (mOverviewSelectEnabled) {
- setPivotX(mLastComputedTaskSize.centerX());
- setPivotY(mLastComputedTaskSize.bottom);
+ getModalTaskSize(mTempRect);
+ Rect selectedTaskPosition = getSelectedTaskBounds();
+
+ Utilities.getPivotsForScalingRectToRect(mTempRect, selectedTaskPosition,
+ mTempPointF);
+ setPivotX(mTempPointF.x);
+ setPivotY(mTempPointF.y);
} else {
getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
mActivity.getDeviceProfile(), mTempPointF);
@@ -4183,11 +4173,17 @@
float offset = mAdjacentPageHorizontalOffset;
float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
int count = getChildCount();
+ boolean showAsGrid = showAsGrid();
TaskView runningTask = mRunningTaskViewId == -1 || !mRunningTaskTileHidden
? null : getRunningTaskView();
int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
int modalMidpoint = getCurrentPage();
+ boolean isModalGridWithoutFocusedTask =
+ showAsGrid && ENABLE_GRID_ONLY_OVERVIEW.get() && mTaskModalness > 0;
+ if (isModalGridWithoutFocusedTask) {
+ modalMidpoint = indexOfChild(mSelectedTask);
+ }
float midpointOffsetSize = 0;
float leftOffsetSize = midpoint - 1 >= 0
@@ -4197,7 +4193,6 @@
? getHorizontalOffsetSize(midpoint + 1, midpoint, offset)
: 0;
- boolean showAsGrid = showAsGrid();
float modalMidpointOffsetSize = 0;
float modalLeftOffsetSize = 0;
float modalRightOffsetSize = 0;
@@ -4225,23 +4220,34 @@
: i < midpoint
? leftOffsetSize
: rightOffsetSize;
+ if (isModalGridWithoutFocusedTask) {
+ gridOffsetSize = getHorizontalOffsetSize(i, modalMidpoint, modalOffset);
+ gridOffsetSize = Math.abs(gridOffsetSize) * (i <= modalMidpoint ? 1 : -1);
+ }
float modalTranslation = i == modalMidpoint
? modalMidpointOffsetSize
: showAsGrid
? gridOffsetSize
: i < modalMidpoint ? modalLeftOffsetSize : modalRightOffsetSize;
- float totalTranslation = translation + modalTranslation;
+ float totalTranslationX = translation + modalTranslation;
View child = getChildAt(i);
- FloatProperty translationProperty = child instanceof TaskView
+ FloatProperty translationPropertyX = child instanceof TaskView
? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
: mOrientationHandler.getPrimaryViewTranslate();
- translationProperty.set(child, totalTranslation);
+ translationPropertyX.set(child, totalTranslationX);
if (mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
- .taskPrimaryTranslation.value = totalTranslation);
+ .taskPrimaryTranslation.value = totalTranslationX);
redrawLiveTile();
}
+
+ if (showAsGrid && ENABLE_GRID_ONLY_OVERVIEW.get() && child instanceof TaskView) {
+ float totalTranslationY = getVerticalOffsetSize(i, modalOffset);
+ FloatProperty translationPropertyY =
+ ((TaskView) child).getSecondaryTaskOffsetTranslationProperty();
+ translationPropertyY.set(child, totalTranslationY);
+ }
}
updateCurveProperties();
}
@@ -4339,6 +4345,38 @@
return distanceToOffscreen * offsetProgress;
}
+ /**
+ * Computes the vertical distance to offset a given child such that it is completely offscreen.
+ *
+ * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
+ */
+ private float getVerticalOffsetSize(int childIndex, float offsetProgress) {
+ if (offsetProgress == 0 || !(showAsGrid() && ENABLE_GRID_ONLY_OVERVIEW.get())
+ || mSelectedTask == null) {
+ // Don't bother calculating everything below if we won't offset vertically.
+ return 0;
+ }
+
+ // First, get the position of the task relative to the top row.
+ TaskView child = getTaskViewAt(childIndex);
+ Rect taskPosition = getTaskBounds(child);
+
+ boolean isSelectedTaskTopRow = mTopRowIdSet.contains(mSelectedTask.getTaskViewId());
+ boolean isChildTopRow = mTopRowIdSet.contains(child.getTaskViewId());
+ // Whether the task should be shifted to the top.
+ boolean isTopShift = !isSelectedTaskTopRow && isChildTopRow;
+ boolean isBottomShift = isSelectedTaskTopRow && !isChildTopRow;
+
+ // Next, calculate the distance to move the task off screen at scale = 1.
+ float distanceToOffscreen = 0;
+ if (isTopShift) {
+ distanceToOffscreen = -taskPosition.bottom;
+ } else if (isBottomShift) {
+ distanceToOffscreen = mActivity.getDeviceProfile().heightPx - taskPosition.top;
+ }
+ return distanceToOffscreen * offsetProgress;
+ }
+
protected void setTaskViewsResistanceTranslation(float translation) {
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
@@ -4438,9 +4476,8 @@
* Resets the visuals when exit modal state.
*/
public void resetModalVisuals() {
- TaskView taskView = getCurrentPageTaskView();
- if (taskView != null) {
- taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
+ if (mSelectedTask != null) {
+ mSelectedTask.getThumbnail().getTaskOverlay().resetModalVisuals();
}
}
@@ -5442,6 +5479,9 @@
if (mOverviewSelectEnabled != overviewSelectEnabled) {
mOverviewSelectEnabled = overviewSelectEnabled;
updatePivots();
+ if (!mOverviewSelectEnabled) {
+ setSelectedTask(INVALID_TASK_ID);
+ }
}
}
@@ -5512,7 +5552,9 @@
private void setTaskModalness(float modalness) {
mTaskModalness = modalness;
updatePageOffsets();
- if (getCurrentPageTaskView() != null) {
+ if (mSelectedTask != null) {
+ mSelectedTask.setModalness(modalness);
+ } else if (getCurrentPageTaskView() != null) {
getCurrentPageTaskView().setModalness(modalness);
}
// Only show actions view when it's modal for in-place landscape mode.
@@ -5527,7 +5569,7 @@
}
/** Enables or disables modal state for RecentsView */
- public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
+ public abstract void setModalStateEnabled(int taskId, boolean animate);
public TaskOverlayFactory getTaskOverlayFactory() {
return mTaskOverlayFactory;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 0e2f020..fb85605 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -431,9 +431,12 @@
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
- setWillNotDraw(!FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get());
+ boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
+ || DesktopTaskView.DESKTOP_MODE_SUPPORTED;
- mBorderAnimator = !FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
+ setWillNotDraw(!keyboardFocusHighlightEnabled);
+
+ mBorderAnimator = !keyboardFocusHighlightEnabled
? null
: new BorderAnimator(
/* borderBoundsBuilder= */ this::updateBorderBounds,
@@ -1442,6 +1445,11 @@
TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
}
+ public FloatProperty<TaskView> getSecondaryTaskOffsetTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+ }
+
public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index edbce10..9e28608 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -104,6 +104,10 @@
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+ private float mTranslationYForTaskbarAlignmentAnimation = 0f;
+
+ private float mTranslationXForTaskbarRevealAnimation = 0f;
+ private float mTranslationYForTaskbarRevealAnimation = 0f;
private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
@@ -370,9 +374,7 @@
@UiThread
protected void applyIconAndLabel(ItemInfoWithIcon info) {
- boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
- || mDisplay == DISPLAY_TASKBAR;
- int flags = useTheme ? FLAG_THEMED : 0;
+ int flags = shouldUseTheme() ? FLAG_THEMED : 0;
if (mHideBadge) {
flags |= FLAG_NO_BADGE;
}
@@ -384,6 +386,11 @@
applyLabel(info);
}
+ protected boolean shouldUseTheme() {
+ return mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
+ || mDisplay == DISPLAY_TASKBAR;
+ }
+
@UiThread
private void applyLabel(ItemInfoWithIcon info) {
setText(info.title);
@@ -952,11 +959,17 @@
}
private void updateTranslation() {
- super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ super.setTranslationX(mTranslationForReorderBounce.x
+ + mTranslationForReorderPreview.x
+ mTranslationForMoveFromCenterAnimation.x
- + mTranslationXForTaskbarAlignmentAnimation);
- super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
- + mTranslationForMoveFromCenterAnimation.y);
+ + mTranslationXForTaskbarAlignmentAnimation
+ + mTranslationXForTaskbarRevealAnimation
+ );
+ super.setTranslationY(mTranslationForReorderBounce.y
+ + mTranslationForReorderPreview.y
+ + mTranslationForMoveFromCenterAnimation.y
+ + mTranslationYForTaskbarAlignmentAnimation
+ + mTranslationYForTaskbarRevealAnimation);
}
public void setReorderBounceOffset(float x, float y) {
@@ -1012,6 +1025,51 @@
return mTranslationXForTaskbarAlignmentAnimation;
}
+ /**
+ * Sets translationX for taskbar to launcher alignment animation
+ */
+ public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
+ mTranslationYForTaskbarAlignmentAnimation = translationY;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translationY value for taskbar to launcher alignment animation
+ */
+ public float getTranslationYForTaskbarAlignmentAnimation() {
+ return mTranslationYForTaskbarAlignmentAnimation;
+ }
+
+ /**
+ * Sets translationX value for taskbar reveal animation
+ */
+ public void setTranslationXForTaskbarRevealAnimation(float translationX) {
+ mTranslationXForTaskbarRevealAnimation = translationX;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translation values for taskbar reveal animation
+ */
+ public float getTranslationXForTaskbarRevealAnimation() {
+ return mTranslationXForTaskbarRevealAnimation;
+ }
+
+ /**
+ * Sets translationY value for taskbar reveal animation
+ */
+ public void setTranslationYForTaskbarRevealAnimation(float translationY) {
+ mTranslationYForTaskbarRevealAnimation = translationY;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translationY values for taskbar reveal animation
+ */
+ public float getTranslationYForTaskbarRevealAnimation() {
+ return mTranslationYForTaskbarRevealAnimation;
+ }
+
public View getView() {
return this;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e63b054..90b2374 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -190,10 +190,10 @@
public final int hotseatQsbVisualHeight;
private final int hotseatQsbShadowHeight;
public int hotseatBorderSpace;
- private int minHotseatIconSpacePx;
- private int minHotseatQsbWidthPx;
- private final int maxHotseatIconSpacePx;
- private int inlineNavButtonsEndSpacing;
+ private final int mMinHotseatIconSpacePx;
+ private final int mMinHotseatQsbWidthPx;
+ private final int mMaxHotseatIconSpacePx;
+ private final int mInlineNavButtonsEndSpacingPx;
// Bottom sheets
public int bottomSheetTopPadding;
@@ -490,7 +490,8 @@
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
if (areNavButtonsInline && !isPhone) {
- inlineNavButtonsEndSpacing = res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing);
+ mInlineNavButtonsEndSpacingPx =
+ res.getDimensionPixelSize(inv.inlineNavButtonsEndSpacing);
/*
* 3 nav buttons +
* Spacing between nav buttons +
@@ -498,9 +499,9 @@
*/
hotseatBarEndOffset = 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+ 2 * res.getDimensionPixelSize(R.dimen.taskbar_button_space_inbetween)
- + inlineNavButtonsEndSpacing;
+ + mInlineNavButtonsEndSpacingPx;
} else {
- inlineNavButtonsEndSpacing = 0;
+ mInlineNavButtonsEndSpacingPx = 0;
hotseatBarEndOffset = 0;
}
@@ -547,9 +548,9 @@
cellLayoutPadding);
updateWorkspacePadding();
- minHotseatIconSpacePx = res.getDimensionPixelSize(R.dimen.min_hotseat_icon_space);
- minHotseatQsbWidthPx = res.getDimensionPixelSize(R.dimen.min_hotseat_qsb_width);
- maxHotseatIconSpacePx = areNavButtonsInline
+ mMinHotseatIconSpacePx = res.getDimensionPixelSize(R.dimen.min_hotseat_icon_space);
+ mMinHotseatQsbWidthPx = res.getDimensionPixelSize(R.dimen.min_hotseat_qsb_width);
+ mMaxHotseatIconSpacePx = areNavButtonsInline
? res.getDimensionPixelSize(R.dimen.max_hotseat_icon_space) : Integer.MAX_VALUE;
// Hotseat and QSB width depends on updated cellSize and workspace padding
recalculateHotseatWidthAndBorderSpace();
@@ -661,39 +662,39 @@
}
// The side space with inline buttons should be what is defined in InvariantDeviceProfile
- int sideSpace = inlineNavButtonsEndSpacing;
- int maxHotseatWidth = availableWidthPx - sideSpace - hotseatBarEndOffset;
- int maxHotseatIconsWidth = maxHotseatWidth - (isQsbInline ? hotseatQsbWidth : 0);
- hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidth,
+ int sideSpacePx = mInlineNavButtonsEndSpacingPx;
+ int maxHotseatWidthPx = availableWidthPx - sideSpacePx - hotseatBarEndOffset;
+ int maxHotseatIconsWidthPx = maxHotseatWidthPx - (isQsbInline ? hotseatQsbWidth : 0);
+ hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidthPx,
(isQsbInline ? 1 : 0) + /* border between nav buttons and first icon */ 1);
- if (hotseatBorderSpace >= minHotseatIconSpacePx) {
+ if (hotseatBorderSpace >= mMinHotseatIconSpacePx) {
return;
}
// Border space can't be less than the minimum
- hotseatBorderSpace = minHotseatIconSpacePx;
+ hotseatBorderSpace = mMinHotseatIconSpacePx;
int requiredWidth = getHotseatRequiredWidth();
// If there is an inline qsb, change its size
if (isQsbInline) {
- hotseatQsbWidth -= requiredWidth - maxHotseatWidth;
- if (hotseatQsbWidth >= minHotseatQsbWidthPx) {
+ hotseatQsbWidth -= requiredWidth - maxHotseatWidthPx;
+ if (hotseatQsbWidth >= mMinHotseatQsbWidthPx) {
return;
}
// QSB can't be less than the minimum
- hotseatQsbWidth = minHotseatQsbWidthPx;
+ hotseatQsbWidth = mMinHotseatQsbWidthPx;
}
- maxHotseatIconsWidth = maxHotseatWidth - (isQsbInline ? hotseatQsbWidth : 0);
+ maxHotseatIconsWidthPx = maxHotseatWidthPx - (isQsbInline ? hotseatQsbWidth : 0);
// If it still doesn't fit, start removing icons
do {
numShownHotseatIcons--;
- hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidth,
+ hotseatBorderSpace = calculateHotseatBorderSpace(maxHotseatIconsWidthPx,
(isQsbInline ? 1 : 0) + /* border between nav buttons and first icon */ 1);
- } while (hotseatBorderSpace < minHotseatIconSpacePx && numShownHotseatIcons > 1);
+ } while (hotseatBorderSpace < mMinHotseatIconSpacePx && numShownHotseatIcons > 1);
}
@@ -993,10 +994,10 @@
*/
private int calculateHotseatBorderSpace(float hotseatWidthPx, int numExtraBorder) {
float hotseatIconsTotalPx = iconSizePx * numShownHotseatIcons;
- int hotseatBorderSpace =
+ int hotseatBorderSpacePx =
(int) (hotseatWidthPx - hotseatIconsTotalPx)
/ (numShownHotseatIcons - 1 + numExtraBorder);
- return Math.min(hotseatBorderSpace, maxHotseatIconSpacePx);
+ return Math.min(hotseatBorderSpacePx, mMaxHotseatIconSpacePx);
}
@@ -1319,7 +1320,7 @@
int endSpacing;
// Hotseat aligns to the left with nav buttons
if (hotseatBarEndOffset > 0) {
- startSpacing = inlineNavButtonsEndSpacing;
+ startSpacing = mInlineNavButtonsEndSpacingPx;
endSpacing = availableWidthPx - hotseatWidth - startSpacing + hotseatBorderSpace;
} else {
startSpacing = (availableWidthPx - hotseatWidth) / 2;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 2fb0fa6..604c1b8 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.LauncherPrefs.GRID_NAME;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.config.FeatureFlags.ENABLE_DEVICE_PROFILE_LOGGING;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
@@ -94,6 +93,8 @@
public static final int TYPE_MULTI_DISPLAY = 1;
public static final int TYPE_TABLET = 2;
+ private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
+
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
// Constants that affects the interpolation curve between statically defined device profile
@@ -206,7 +207,8 @@
String gridName = getCurrentGridName(context);
String newGridName = initGrid(context, gridName);
if (!newGridName.equals(gridName)) {
- LauncherPrefs.get(context).put(GRID_NAME, newGridName);
+ LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName)
+ .apply();
}
new DeviceGridState(this).writeToPrefs(context);
@@ -314,7 +316,7 @@
}
public static String getCurrentGridName(Context context) {
- return LauncherPrefs.get(context).get(GRID_NAME);
+ return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null);
}
private String initGrid(Context context, String gridName) {
@@ -456,8 +458,9 @@
public void setCurrentGrid(Context context, String gridName) {
- LauncherPrefs.get(context).put(GRID_NAME, gridName);
- MAIN_EXECUTOR.execute(() -> onConfigChanged(context.getApplicationContext()));
+ Context appContext = context.getApplicationContext();
+ LauncherPrefs.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
+ MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext));
}
private Object[] toModelState() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8097fd7..8e53101 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1947,7 +1947,7 @@
Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName);
setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info));
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: processShortcutFromDrop");
- if (!info.activityInfo.startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) {
+ if (!info.getActivityInfo(this).startConfigActivity(this, REQUEST_CREATE_SHORTCUT)) {
handleActivityResult(REQUEST_CREATE_SHORTCUT, RESULT_CANCELED, null);
}
}
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index befaa64..2e07e30 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -4,8 +4,6 @@
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.annotation.VisibleForTesting
-import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
-import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
import com.android.launcher3.allapps.WorkProfileManager
import com.android.launcher3.model.DeviceGridState
import com.android.launcher3.pm.InstallSessionHelper
@@ -22,10 +20,11 @@
class LauncherPrefs(private val context: Context) {
/** Wrapper around `getInner` for a `ContextualItem` */
- fun <T> get(item: ContextualItem<T>): T = getInner(item, item.defaultValueFromContext(context))
+ fun <T : Any> get(item: ContextualItem<T>): T =
+ getInner(item, item.defaultValueFromContext(context))
/** Wrapper around `getInner` for an `Item` */
- fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
+ fun <T : Any> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
/**
* Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
@@ -33,11 +32,11 @@
* `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
*/
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
- private fun <T> getInner(item: Item, default: T): T {
+ private fun <T : Any> getInner(item: Item, default: T): T {
val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
- return when (item.type) {
- String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
+ return when (default::class.java) {
+ String::class.java -> sp.getString(item.sharedPrefKey, default as String)
Boolean::class.java,
java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Int::class.java,
@@ -46,10 +45,11 @@
java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Long::class.java,
java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
- Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
+ Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as Set<String>)
else ->
throw IllegalArgumentException(
- "item type: ${item.type}" + " is not compatible with sharedPref methods"
+ "item type: ${default::class.java}" +
+ " is not compatible with sharedPref methods"
)
}
as T
@@ -224,36 +224,39 @@
backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
@JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
@JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
- @JvmField val GRID_NAME = ConstantItem("idp_grid_name", true, null, String::class.java)
@JvmField
val ALLOW_ROTATION =
- backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
+ backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY) {
RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
}
@VisibleForTesting
@JvmStatic
fun <T> backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem<T> =
- ConstantItem(sharedPrefKey, true, defaultValue)
+ ConstantItem(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue)
@JvmStatic
fun <T> backedUpItem(
sharedPrefKey: String,
- type: Class<out T>,
defaultValueFromContext: (c: Context) -> T
- ): ContextualItem<T> = ContextualItem(sharedPrefKey, true, defaultValueFromContext, type)
+ ): ContextualItem<T> =
+ ContextualItem(
+ sharedPrefKey,
+ LauncherFiles.SHARED_PREFERENCES_KEY,
+ defaultValueFromContext
+ )
@VisibleForTesting
@JvmStatic
fun <T> nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem<T> =
- ConstantItem(sharedPrefKey, false, defaultValue)
+ ConstantItem(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue)
@Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
@JvmStatic
fun getPrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so we use single cached instance
return context.applicationContext.getSharedPreferences(
- SHARED_PREFERENCES_KEY,
+ LauncherFiles.SHARED_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
}
@@ -263,7 +266,7 @@
fun getDevicePrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so we use a single cached instance
return context.applicationContext.getSharedPreferences(
- DEVICE_PREFERENCES_KEY,
+ LauncherFiles.DEVICE_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
}
@@ -272,26 +275,21 @@
abstract class Item {
abstract val sharedPrefKey: String
- abstract val isBackedUp: Boolean
- abstract val type: Class<*>
- val sharedPrefFile: String = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
+ abstract val sharedPrefFile: String
fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
}
data class ConstantItem<T>(
override val sharedPrefKey: String,
- override val isBackedUp: Boolean,
- val defaultValue: T,
- // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
- override val type: Class<out T> = defaultValue!!::class.java
+ override val sharedPrefFile: String,
+ val defaultValue: T
) : Item()
data class ContextualItem<T>(
override val sharedPrefKey: String,
- override val isBackedUp: Boolean,
- private val defaultSupplier: (c: Context) -> T,
- override val type: Class<out T>
+ override val sharedPrefFile: String,
+ private val defaultSupplier: (c: Context) -> T
) : Item() {
private var default: T? = null
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 81fbe79..b8d13ed 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -226,6 +226,20 @@
}
/**
+ * Returns whether taskbar global drag is disallowed in this state.
+ */
+ public boolean disallowTaskbarGlobalDrag() {
+ return false;
+ }
+
+ /**
+ * Returns whether the taskbar shortcut should trigger split selection mode.
+ */
+ public boolean allowTaskbarInitialSplitSelection() {
+ return false;
+ }
+
+ /**
* Fraction shift in the vertical translation UI and related properties
*
* @see com.android.launcher3.allapps.AllAppsTransitionController
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index b7a22fc..000ddd8 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -21,7 +21,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import java.util.Optional;
@@ -29,13 +29,20 @@
* Meta data that is used for deferred binding. e.g., this object is used to pass information on
* draggable targets when they are dropped onto the workspace from another container.
*/
-public class PendingAddItemInfo extends ItemInfo {
+public class PendingAddItemInfo extends ItemInfoWithIcon {
/**
* The component that will be created.
*/
public ComponentName componentName;
+ public PendingAddItemInfo() { }
+
+ public PendingAddItemInfo(PendingAddItemInfo info) {
+ super(info);
+ componentName = info.componentName;
+ }
+
@Override
protected String dumpProperties() {
return super.dumpProperties() + " componentName=" + componentName;
@@ -46,13 +53,18 @@
*/
@NonNull
@Override
- public ItemInfo makeShallowCopy() {
+ public PendingAddItemInfo makeShallowCopy() {
PendingAddItemInfo itemInfo = new PendingAddItemInfo();
itemInfo.copyFrom(this);
itemInfo.componentName = this.componentName;
return itemInfo;
}
+ @Override
+ public PendingAddItemInfo clone() {
+ return makeShallowCopy();
+ }
+
@Nullable
@Override
public ComponentName getTargetComponent() {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index bd9493b..67bc7fc 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -38,6 +38,7 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
@@ -343,6 +344,21 @@
}
/**
+ * Sets the x and y pivots for scaling from one Rect to another.
+ *
+ * @param src the source rectangle to scale from.
+ * @param dst the destination rectangle to scale to.
+ * @param outPivot the pivots set for scaling from src to dst.
+ */
+ public static void getPivotsForScalingRectToRect(Rect src, Rect dst, PointF outPivot) {
+ float pivotXPct = ((float) src.left - dst.left) / ((float) dst.width() - src.width());
+ outPivot.x = dst.left + dst.width() * pivotXPct;
+
+ float pivotYPct = ((float) src.top - dst.top) / ((float) dst.height() - src.height());
+ outPivot.y = dst.top + dst.height() * pivotYPct;
+ }
+
+ /**
* Maps t from one range to another range.
* @param t The value to map.
* @param fromMin The lower bound of the range that t is being mapped from.
@@ -557,6 +573,12 @@
int width, int height, Object[] outObj) {
ActivityContext activity = ActivityContext.lookupContext(context);
LauncherAppState appState = LauncherAppState.getInstance(context);
+ if (info instanceof PendingAddShortcutInfo) {
+ ShortcutConfigActivityInfo activityInfo =
+ ((PendingAddShortcutInfo) info).getActivityInfo(context);
+ outObj[0] = activityInfo;
+ return activityInfo.getFullResIcon(appState.getIconCache());
+ }
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
LauncherActivityInfo activityInfo = context.getSystemService(LauncherApps.class)
.resolveActivity(info.getIntent(), info.user);
@@ -565,12 +587,6 @@
.getIconProvider().getIcon(
activityInfo, activity.getDeviceProfile().inv.fillResIconDpi);
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- if (info instanceof PendingAddShortcutInfo) {
- ShortcutConfigActivityInfo activityInfo =
- ((PendingAddShortcutInfo) info).activityInfo;
- outObj[0] = activityInfo;
- return activityInfo.getFullResIcon(appState.getIconCache());
- }
List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
.buildRequest(context)
.query(ShortcutRequest.ALL);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index cfb8ca4..ba492d5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2690,7 +2690,7 @@
private void onDropExternal(final int[] touchXY, final CellLayout cellLayout, DragObject d) {
if (d.dragInfo instanceof PendingAddShortcutInfo) {
WorkspaceItemInfo si = ((PendingAddShortcutInfo) d.dragInfo)
- .activityInfo.createWorkspaceItemInfo();
+ .getActivityInfo(mLauncher).createWorkspaceItemInfo();
if (si != null) {
d.dragInfo = si;
}
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 3e98cd9..8fbe997 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -365,6 +365,9 @@
mAH.get(i).mRecyclerView.scrollToTop();
}
}
+ if (mTouchHandler != null) {
+ mTouchHandler.endFastScrolling();
+ }
if (mHeader != null && mHeader.getVisibility() == VISIBLE) {
mHeader.reset(animate);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 92c017c..4430a94 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -200,6 +200,7 @@
? FLAG_DARK_NAV : FLAG_LIGHT_NAV;
setShiftRange(dp.allAppsShiftRange);
+ mAllAppScale.value = 1;
mLauncher.addOnDeviceProfileChangeListener(this);
mVibratorWrapper = VibratorWrapper.INSTANCE.get(mLauncher.getApplicationContext());
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index c89a461..0ec036c 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -355,6 +355,9 @@
public static final BooleanFlag ENABLE_ICON_IN_TEXT_HEADER = getDebugFlag(
"ENABLE_ICON_IN_TEXT_HEADER", false, "Show icon in textheader");
+ public static final BooleanFlag ENABLE_APP_ICON_FOR_INLINE_SHORTCUTS = getDebugFlag(
+ "ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", false, "Show app icon for inline shortcut");
+
public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(
"SHOW_DOT_PAGINATION", false, "Enable showing dot pagination in workspace");
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index f54d05d..46c8e81 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -56,7 +56,6 @@
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -84,6 +83,7 @@
protected final int mRegistrationX;
protected final int mRegistrationY;
private final float mInitialScale;
+ private final float mEndScale;
protected final float mScaleOnDrop;
protected final int[] mTempLoc = new int[2];
@@ -159,7 +159,7 @@
setClipToPadding(false);
}
- final float scale = (width + finalScaleDps) / width;
+ mEndScale = (width + finalScaleDps) / width;
// Set the initial scale to avoid any jumps
setScaleX(initialScale);
@@ -170,8 +170,8 @@
mAnim.setDuration(VIEW_ZOOM_DURATION);
mAnim.addUpdateListener(animation -> {
final float value = (Float) animation.getAnimatedValue();
- setScaleX(initialScale + (value * (scale - initialScale)));
- setScaleY(initialScale + (value * (scale - initialScale)));
+ setScaleX(Utilities.mapRange(value, initialScale, mEndScale));
+ setScaleY(Utilities.mapRange(value, initialScale, mEndScale));
if (!isAttachedToWindow()) {
animation.cancel();
}
@@ -218,12 +218,6 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public void setItemInfo(final ItemInfo info) {
- if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
- && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
- && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- return;
- }
// Load the adaptive icon on a background thread and add the view in ui thread.
MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(() -> {
Object[] outObj = new Object[1];
@@ -515,6 +509,10 @@
return mInitialScale;
}
+ public float getEndScale() {
+ return mEndScale;
+ }
+
@Override
public boolean hasOverlappingRendering() {
return false;
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index f9916d0..6b21522 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -44,7 +44,7 @@
* request.
*/
@TargetApi(Build.VERSION_CODES.O)
-class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
+public class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
// Class name used in the target component, such that it will never represent an
// actual existing class.
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 9a5d77e..c9fe745 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1263,7 +1263,7 @@
PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo
? (PendingAddShortcutInfo) d.dragInfo : null;
WorkspaceItemInfo pasiSi =
- pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null;
+ pasi != null ? pasi.getActivityInfo(launcher).createWorkspaceItemInfo() : null;
if (pasi != null && pasiSi == null) {
// There is no WorkspaceItemInfo, so we have to go through a configuration activity.
pasi.container = mInfo.id;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index e69f781..ee1a060 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -135,6 +135,9 @@
private final PointF mTranslationForMoveFromCenterAnimation = new PointF(0, 0);
private float mTranslationXForTaskbarAlignmentAnimation = 0f;
+ private float mTranslationYForTaskbarAlignmentAnimation = 0f;
+ private float mTranslationXForTaskbarRevealAnimation = 0f;
+ private float mTranslationYForTaskbarRevealAnimation = 0f;
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@@ -416,7 +419,7 @@
() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
- },
+ },
DragLayer.ANIMATION_END_DISAPPEAR, null);
mFolder.hideItem(item);
@@ -768,11 +771,15 @@
}
private void updateTranslation() {
- super.setTranslationX(mTranslationForReorderBounce.x + mTranslationForReorderPreview.x
+ super.setTranslationX(mTranslationForReorderBounce.x
+ + mTranslationForReorderPreview.x
+ mTranslationForMoveFromCenterAnimation.x
- + mTranslationXForTaskbarAlignmentAnimation);
+ + mTranslationXForTaskbarAlignmentAnimation
+ + mTranslationXForTaskbarRevealAnimation);
super.setTranslationY(mTranslationForReorderBounce.y + mTranslationForReorderPreview.y
- + mTranslationForMoveFromCenterAnimation.y);
+ + mTranslationForMoveFromCenterAnimation.y
+ + mTranslationYForTaskbarAlignmentAnimation
+ + mTranslationYForTaskbarRevealAnimation);
}
public void setReorderBounceOffset(float x, float y) {
@@ -787,7 +794,7 @@
/**
* Sets translationX value for taskbar to launcher alignment animation
*/
- public void setTranslationForTaskbarAlignmentAnimation(float translationX) {
+ public void setTranslationXForTaskbarAlignmentAnimation(float translationX) {
mTranslationXForTaskbarAlignmentAnimation = translationX;
updateTranslation();
}
@@ -800,6 +807,51 @@
}
/**
+ * Sets translationY value for taskbar to launcher alignment animation
+ */
+ public void setTranslationYForTaskbarAlignmentAnimation(float translationY) {
+ mTranslationYForTaskbarAlignmentAnimation = translationY;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translation values for taskbar to launcher alignment animation
+ */
+ public float getTranslationYForTaskbarAlignmentAnimation() {
+ return mTranslationYForTaskbarAlignmentAnimation;
+ }
+
+ /**
+ * Sets translationX value for taskbar reveal animation
+ */
+ public void setTranslationXForTaskbarRevealAnimation(float translationX) {
+ mTranslationXForTaskbarRevealAnimation = translationX;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translation values for taskbar reveal animation
+ */
+ public float getTranslationXForTaskbarRevealAnimation() {
+ return mTranslationXForTaskbarRevealAnimation;
+ }
+
+ /**
+ * Sets translationY value for taskbar reveal animation
+ */
+ public void setTranslationYForTaskbarRevealAnimation(float translationY) {
+ mTranslationYForTaskbarRevealAnimation = translationY;
+ updateTranslation();
+ }
+
+ /**
+ * Returns translationY values for taskbar reveal animation
+ */
+ public float getTranslationYForTaskbarRevealAnimation() {
+ return mTranslationYForTaskbarRevealAnimation;
+ }
+
+ /**
* Sets translation values for move from center animation
*/
public void setTranslationForMoveFromCenterAnimation(float x, float y) {
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 9426c22..2c8f1f3 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -138,12 +138,14 @@
}
idp.setCurrentGrid(getContext(), gridName);
+ getContext().getContentResolver().notifyChange(uri, null);
return 1;
}
case ICON_THEMED:
case SET_ICON_THEMED: {
LauncherPrefs.get(getContext())
.put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE));
+ getContext().getContentResolver().notifyChange(uri, null);
return 1;
}
default:
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 0b4a4a5..3c63f26 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -60,6 +60,7 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.InstantAppResolver;
@@ -81,6 +82,11 @@
*/
public class IconCache extends BaseIconCache {
+ // Shortcut extra which can point to a packageName and can be used to indicate an alternate
+ // badge info. Launcher only reads this if the shortcut comes from a system app.
+ public static final String EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE =
+ "extra_shortcut_badge_override_package";
+
private static final String TAG = "Launcher.IconCache";
private final Predicate<ItemInfoWithIcon> mIsUsingFallbackOrNonDefaultIconCheck = w ->
@@ -260,8 +266,15 @@
getTitleAndIcon(appInfo, false);
return appInfo.bitmap;
} else {
- PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage(),
- shortcutInfo.getUserHandle());
+ String pkg = shortcutInfo.getPackage();
+ String override = shortcutInfo.getExtras() == null ? null
+ : shortcutInfo.getExtras().getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE);
+ if (!TextUtils.isEmpty(override)
+ && InstallSessionHelper.INSTANCE.get(mContext)
+ .isTrustedPackage(pkg, shortcutInfo.getUserHandle())) {
+ pkg = override;
+ }
+ PackageItemInfo pkgInfo = new PackageItemInfo(pkg, shortcutInfo.getUserHandle());
getTitleAndIconForApp(pkgInfo, false);
return pkgInfo.bitmap;
}
@@ -484,8 +497,7 @@
WidgetSection widgetSection = WidgetSections.getWidgetSections(mContext)
.get(infoInOut.widgetCategory);
infoInOut.title = mContext.getString(widgetSection.mSectionTitle);
- infoInOut.contentDescription = mPackageManager.getUserBadgedLabel(
- infoInOut.title, infoInOut.user);
+ infoInOut.contentDescription = getUserBadgedLabel(infoInOut.title, infoInOut.user);
final BitmapInfo cachedBitmap = mWidgetCategoryBitmapInfos.get(infoInOut.widgetCategory);
if (cachedBitmap != null) {
infoInOut.bitmap = getBadgedIcon(cachedBitmap, infoInOut.user);
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 0a6a7cd..855a69d 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -310,7 +310,7 @@
throw new InvalidParameterException("Invalid restoreType " + restoreFlag);
}
- info.contentDescription = mPM.getUserBadgedLabel(info.title, info.user);
+ info.contentDescription = mIconCache.getUserBadgedLabel(info.title, info.user);
info.itemType = itemType;
info.status = restoreFlag;
return info;
@@ -381,7 +381,7 @@
}
}
- info.contentDescription = mPM.getUserBadgedLabel(info.title, info.user);
+ info.contentDescription = mIconCache.getUserBadgedLabel(info.title, info.user);
return info;
}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index db23566..7ca3b11 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -171,15 +171,22 @@
}
return null;
}
- String pkg = sessionInfo.getInstallerPackageName();
+ return isTrustedPackage(sessionInfo.getInstallerPackageName(), getUserHandle(sessionInfo))
+ ? sessionInfo : null;
+ }
+
+ /**
+ * Returns true if the provided packageName can be trusted for user configurations
+ */
+ public boolean isTrustedPackage(String pkg, UserHandle user) {
synchronized (mSessionVerifiedMap) {
if (!mSessionVerifiedMap.containsKey(pkg)) {
boolean hasSystemFlag = new PackageManagerHelper(mAppContext).getApplicationInfo(
- pkg, getUserHandle(sessionInfo), ApplicationInfo.FLAG_SYSTEM) != null;
+ pkg, user, ApplicationInfo.FLAG_SYSTEM) != null;
mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
}
}
- return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
+ return mSessionVerifiedMap.get(pkg);
}
@NonNull
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index 7af14c6..14e67b2 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
@@ -112,26 +111,6 @@
return true;
}
- static class ShortcutConfigActivityInfoVL extends ShortcutConfigActivityInfo {
-
- private final ActivityInfo mInfo;
-
- ShortcutConfigActivityInfoVL(ActivityInfo info) {
- super(new ComponentName(info.packageName, info.name), Process.myUserHandle());
- mInfo = info;
- }
-
- @Override
- public CharSequence getLabel(PackageManager pm) {
- return mInfo.loadLabel(pm);
- }
-
- @Override
- public Drawable getFullResIcon(IconCache cache) {
- return cache.getFullResIcon(mInfo);
- }
- }
-
@TargetApi(26)
public static class ShortcutConfigActivityInfoVO extends ShortcutConfigActivityInfo {
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
deleted file mode 100644
index 7b49583..0000000
--- a/src/com/android/launcher3/util/LockedUserState.kt
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.android.launcher3.util
-
-import android.content.Context
-import android.content.Intent
-import android.os.Process
-import android.os.UserManager
-import androidx.annotation.VisibleForTesting
-
-class LockedUserState(private val mContext: Context) : SafeCloseable {
- var isUserUnlocked: Boolean
- private set
- private val mUserUnlockedActions: RunnableList = RunnableList()
-
- @VisibleForTesting
- val mUserUnlockedReceiver = SimpleBroadcastReceiver {
- if (Intent.ACTION_USER_UNLOCKED == it.action) {
- isUserUnlocked = true
- notifyUserUnlocked()
- }
- }
-
- init {
- isUserUnlocked =
- mContext
- .getSystemService(UserManager::class.java)!!
- .isUserUnlocked(Process.myUserHandle())
- if (isUserUnlocked) {
- notifyUserUnlocked()
- } else {
- mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
- }
- }
-
- private fun notifyUserUnlocked() {
- mUserUnlockedActions.executeAllAndDestroy()
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
- }
-
- /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
- override fun close() {
- mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
- }
-
- /**
- * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
- * this runnable will run immediately because RunnableList will already have been destroyed.
- */
- fun runOnUserUnlocked(action: Runnable) {
- mUserUnlockedActions.add(action)
- }
-
- companion object {
- @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
-
- @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
- }
-}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index f7837f5..b6f6223 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -313,7 +313,7 @@
*/
default boolean startActivitySafely(
View v, Intent intent, @Nullable ItemInfo item) {
-
+ Preconditions.assertUIThread();
Context context = (Context) this;
if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 1274294..a941833 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -281,15 +281,7 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mRv.onFastScrollCompleted();
- mTouchOffsetY = 0;
- mLastTouchY = 0;
- mIgnoreDragGesture = false;
- if (mIsDragging) {
- mIsDragging = false;
- animatePopupVisibility(false);
- showActiveScrollbar(false);
- }
+ endFastScrolling();
break;
}
if (DEBUG) {
@@ -328,6 +320,19 @@
setThumbOffsetY((int) mLastTouchY);
}
+ /** End any active fast scrolling touch handling, if applicable. */
+ public void endFastScrolling() {
+ mRv.onFastScrollCompleted();
+ mTouchOffsetY = 0;
+ mLastTouchY = 0;
+ mIgnoreDragGesture = false;
+ if (mIsDragging) {
+ mIsDragging = false;
+ animatePopupVisibility(false);
+ showActiveScrollbar(false);
+ }
+ }
+
public void onDraw(Canvas canvas) {
if (mThumbOffsetY < 0 || mRv == null) {
return;
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index 9601652..3935be5 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -17,6 +17,8 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+import android.content.Context;
+
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
@@ -27,13 +29,28 @@
*/
public class PendingAddShortcutInfo extends PendingAddItemInfo {
- public ShortcutConfigActivityInfo activityInfo;
+ // TODO: Make it @NonNull
+ protected ShortcutConfigActivityInfo mActivityInfo;
public PendingAddShortcutInfo(ShortcutConfigActivityInfo activityInfo) {
- this.activityInfo = activityInfo;
+ this.mActivityInfo = activityInfo;
componentName = activityInfo.getComponent();
user = activityInfo.getUser();
itemType = activityInfo.getItemType();
this.container = CONTAINER_WIDGETS_TRAY;
}
+
+ public PendingAddShortcutInfo(PendingAddShortcutInfo info) {
+ super(info);
+ mActivityInfo = info.mActivityInfo;
+ }
+
+ public PendingAddShortcutInfo() { }
+
+ /**
+ * Returns the info used for creating the shortcut
+ */
+ public ShortcutConfigActivityInfo getActivityInfo(Context context) {
+ return mActivityInfo;
+ }
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index bbbc329..2dedd12 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -180,7 +180,8 @@
draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_WIDGET);
} else {
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
- Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
+ Drawable icon = createShortcutInfo.getActivityInfo(launcher)
+ .getFullResIcon(app.getIconCache());
LauncherIcons li = LauncherIcons.obtain(launcher);
preview = new FastBitmapDrawable(
li.createScaledBitmap(icon, BaseIconFactory.MODE_DEFAULT));
diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
index d40a7bc..31e8d30 100644
--- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt
+++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -13,7 +13,7 @@
private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
-private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, Boolean::class.java)
+private val TEST_CONTEXTUAL_ITEM = LauncherPrefs.backedUpItem("4") { true }
@SmallTest
@RunWith(AndroidJUnit4::class)
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
deleted file mode 100644
index 84156e7..0000000
--- a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
+++ /dev/null
@@ -1,88 +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.util
-
-import android.content.Context
-import android.content.Intent
-import android.os.Process
-import android.os.UserManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-
-/** Unit tests for {@link LockedUserUtil} */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class LockedUserStateTest {
-
- @Mock lateinit var userManager: UserManager
- @Mock lateinit var context: Context
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
- }
-
- @Test
- fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
- `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
- val action: Runnable = mock()
-
- LockedUserState.get(context).runOnUserUnlocked(action)
- verify(action).run()
- }
-
- @Test
- fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
- `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
- val action: Runnable = mock()
-
- LockedUserState.get(context).runOnUserUnlocked(action)
- verifyZeroInteractions(action)
-
- LockedUserState.get(context)
- .mUserUnlockedReceiver
- .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
-
- verify(action).run()
- }
-
- @Test
- fun isUserUnlocked_returns_true_when_user_is_unlocked() {
- `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
- assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
- }
-
- @Test
- fun isUserUnlocked_returns_false_when_user_is_locked() {
- `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
- LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
- assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
- }
-}