Merge "Fixing AppPredictionsUITests" into ub-launcher3-qt-dev
diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
index 6bc859a..af07aa3 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java
@@ -23,8 +23,10 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.Point;
+import android.graphics.Path;
+import android.graphics.PathMeasure;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.util.Log;
import android.view.ViewDebug;
@@ -36,33 +38,51 @@
private static final String TAG = "DotRenderer";
// The dot size is defined as a percentage of the app icon size.
- private static final float SIZE_PERCENTAGE = 0.38f;
+ private static final float SIZE_PERCENTAGE = 0.228f;
- // Extra scale down of the dot
- private static final float DOT_SCALE = 0.6f;
-
- // Offset the dot slightly away from the icon if there's space.
- private static final float OFFSET_PERCENTAGE = 0.02f;
-
- private final float mDotCenterOffset;
- private final int mOffset;
private final float mCircleRadius;
private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
private final Bitmap mBackgroundWithShadow;
private final float mBitmapOffset;
- public DotRenderer(int iconSizePx) {
- mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
- mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
+ // Stores the center x and y position as a percentage (0 to 1) of the icon size
+ private final float[] mRightDotPosition;
+ private final float[] mLeftDotPosition;
- int size = (int) (DOT_SCALE * mDotCenterOffset);
+ public DotRenderer(int iconSizePx, Path iconShapePath, int pathSize) {
+ int size = Math.round(SIZE_PERCENTAGE * iconSizePx);
ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
builder.ambientShadowAlpha = 88;
mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
mCircleRadius = builder.radius;
mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
+
+ // Find the points on the path that are closest to the top left and right corners.
+ mLeftDotPosition = getPathPoint(iconShapePath, pathSize, -1);
+ mRightDotPosition = getPathPoint(iconShapePath, pathSize, 1);
+ }
+
+ private static float[] getPathPoint(Path path, float size, float direction) {
+ float halfSize = size / 2;
+ // Small delta so that we don't get a zero size triangle
+ float delta = 1;
+
+ float x = halfSize + direction * halfSize;
+ Path trianglePath = new Path();
+ trianglePath.moveTo(halfSize, halfSize);
+ trianglePath.lineTo(x + delta * direction, 0);
+ trianglePath.lineTo(x, -delta);
+ trianglePath.close();
+
+ trianglePath.op(path, Path.Op.INTERSECT);
+ float[] pos = new float[2];
+ new PathMeasure(trianglePath, false).getPosTan(0, pos, null);
+
+ pos[0] = pos[0] / size;
+ pos[1] = pos[1] / size;
+ return pos;
}
/**
@@ -74,15 +94,21 @@
return;
}
canvas.save();
- // We draw the dot relative to its center.
- float dotCenterX = params.leftAlign
- ? params.iconBounds.left + mDotCenterOffset / 2
- : params.iconBounds.right - mDotCenterOffset / 2;
- float dotCenterY = params.iconBounds.top + mDotCenterOffset / 2;
- int offsetX = Math.min(mOffset, params.spaceForOffset.x);
- int offsetY = Math.min(mOffset, params.spaceForOffset.y);
- canvas.translate(dotCenterX + offsetX, dotCenterY - offsetY);
+ Rect iconBounds = params.iconBounds;
+ float[] dotPosition = params.leftAlign ? mLeftDotPosition : mRightDotPosition;
+ float dotCenterX = iconBounds.left + iconBounds.width() * dotPosition[0];
+ float dotCenterY = iconBounds.top + iconBounds.height() * dotPosition[1];
+
+ // Ensure dot fits entirely in canvas clip bounds.
+ Rect canvasBounds = canvas.getClipBounds();
+ float offsetX = params.leftAlign
+ ? Math.max(0, canvasBounds.left - (dotCenterX + mBitmapOffset))
+ : Math.min(0, canvasBounds.right - (dotCenterX - mBitmapOffset));
+ float offsetY = Math.max(0, canvasBounds.top - (dotCenterY + mBitmapOffset));
+
+ // We draw the dot relative to its center.
+ canvas.translate(dotCenterX + offsetX, dotCenterY + offsetY);
canvas.scale(params.scale, params.scale);
mCirclePaint.setColor(Color.BLACK);
@@ -102,9 +128,6 @@
/** The progress of the animation, from 0 to 1. */
@ViewDebug.ExportedProperty(category = "notification dot")
public float scale;
- /** Overrides internally calculated offset if specified value is smaller. */
- @ViewDebug.ExportedProperty(category = "notification dot")
- public Point spaceForOffset = new Point();
/** Whether the dot should align to the top left of the icon rather than the top right. */
@ViewDebug.ExportedProperty(category = "notification dot")
public boolean leftAlign;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 81090c1..3b664b7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -28,9 +28,13 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.view.MotionEvent;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
@@ -45,8 +49,6 @@
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import androidx.annotation.Nullable;
-
/**
* Handles quick switching to a recent task from the home screen.
*/
@@ -128,6 +130,10 @@
private void updateFullscreenProgress(float progress) {
if (mTaskToLaunch != null) {
mTaskToLaunch.setFullscreenProgress(progress);
+ int sysuiFlags = progress > UPDATE_SYSUI_FLAGS_THRESHOLD
+ ? mTaskToLaunch.getThumbnail().getSysUiStatusNavFlags()
+ : 0;
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 50f25fb..e932452 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -270,9 +270,9 @@
playScaleDownAnim(anim, activity, endState);
anim.setDuration(transitionLength * 2);
- activity.getStateManager().setCurrentAnimation(anim);
AnimatorPlaybackController controller =
AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+ activity.getStateManager().setCurrentUserControlledAnimation(controller);
// Since we are changing the start position of the UI, reapply the state, at the end
controller.setEndAction(() -> {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 61ae880..a343a36 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -429,8 +429,7 @@
MotionEvent event = (MotionEvent) ev;
TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked());
if (event.getAction() == ACTION_DOWN) {
- if (isInValidSystemUiState()
- && mSwipeTouchRegion.contains(event.getX(), event.getY())) {
+ if (mSwipeTouchRegion.contains(event.getX(), event.getY())) {
boolean useSharedState = mConsumer.useSharedSwipeState();
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(useSharedState, event);
@@ -443,15 +442,45 @@
mUncheckedConsumer.onMotionEvent(event);
}
- private boolean isInValidSystemUiState() {
- return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
- && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
- && !ActivityManagerWrapper.getInstance().isLockToAppActive();
- }
private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
- // TODO: this makes a binder call every touch down. we should move to a listener pattern.
- if (!mIsUserUnlocked || mKM.isDeviceLocked()) {
+ boolean validSystemUIFlags = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+ && (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0;
+ boolean topTaskLocked = ActivityManagerWrapper.getInstance().isLockToAppActive();
+ boolean isInValidSystemUiState = validSystemUIFlags && !topTaskLocked;
+
+ if (!mIsUserUnlocked) {
+ if (isInValidSystemUiState) {
+ // This handles apps launched in direct boot mode (e.g. dialer) as well as apps
+ // launched while device is locked even after exiting direct boot mode (e.g. camera).
+ return new DeviceLockedInputConsumer(this);
+ } else {
+ return InputConsumer.NO_OP;
+ }
+ }
+
+ InputConsumer base = isInValidSystemUiState
+ ? newBaseConsumer(useSharedState, event) : InputConsumer.NO_OP;
+ if (mMode == Mode.NO_BUTTON) {
+ final ActivityControlHelper activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
+ if (mAssistantAvailable && !topTaskLocked
+ && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+ base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
+ mInputMonitorCompat);
+ }
+
+ if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
+ base = new AccessibilityInputConsumer(this, mISystemUiProxy,
+ (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
+ mInputMonitorCompat);
+ }
+ }
+ return base;
+ }
+
+ private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
+ if (mKM.isDeviceLocked()) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps launched
// while device is locked even after exiting direct boot mode (e.g. camera).
return new DeviceLockedInputConsumer(this);
@@ -465,42 +494,26 @@
final ActivityControlHelper activityControl =
mOverviewComponentObserver.getActivityControlHelper();
- InputConsumer base;
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher
&& !mSwipeSharedState.recentsAnimationFinishInterrupted) {
- base = InputConsumer.NO_OP;
+ return InputConsumer.NO_OP;
} else if (mSwipeSharedState.recentsAnimationFinishInterrupted) {
// If the finish animation was interrupted, then continue using the other activity input
// consumer but with the next task as the running task
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.id = mSwipeSharedState.nextRunningTaskId;
- base = createOtherActivityInputConsumer(event, info);
+ return createOtherActivityInputConsumer(event, info);
} else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
- base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
+ return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
activityControl.isInLiveTileMode()) {
- base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
+ return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else if (mGestureBlockingActivity != null && runningTaskInfo != null
&& mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
- base = InputConsumer.NO_OP;
+ return InputConsumer.NO_OP;
} else {
- base = createOtherActivityInputConsumer(event, runningTaskInfo);
+ return createOtherActivityInputConsumer(event, runningTaskInfo);
}
-
- if (mMode == Mode.NO_BUTTON) {
- if (mAssistantAvailable && AssistantTouchConsumer.withinTouchRegion(this, event)) {
- base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
- mInputMonitorCompat);
- }
-
- if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
- base = new AccessibilityInputConsumer(this, mISystemUiProxy,
- (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
- mInputMonitorCompat);
- }
- }
-
- return base;
}
private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 936f0aa..58ae5eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
@@ -654,8 +655,7 @@
mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
mTransformParams);
- mRecentsAnimationWrapper.setWindowThresholdCrossed(
- shift > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD);
+ updateSysUiFlags(shift);
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
@@ -700,6 +700,18 @@
? 0 : (progress - mShiftAtGestureStart) / (1 - mShiftAtGestureStart));
}
+ private void updateSysUiFlags(float windowProgress) {
+ if (mRecentsView != null) {
+ // We will handle the sysui flags based on the centermost task view.
+ mRecentsAnimationWrapper.setWindowThresholdCrossed(true);
+ int sysuiFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD
+ ? 0
+ : mRecentsView.getTaskViewAt(mRecentsView.getPageNearestToCenterOfScreen())
+ .getThumbnail().getSysUiStatusNavFlags();
+ mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
+ }
+ }
+
@Override
public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
@@ -1091,6 +1103,7 @@
windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
}
+ updateSysUiFlags(Math.max(progress, mCurrentShift.value));
});
anim.addAnimatorListener(new AnimationSuccessListener() {
@Override
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8291acc..bff7f42 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -46,10 +46,12 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.views.ActivityContext;
@@ -388,7 +390,7 @@
protected void drawDotIfNecessary(Canvas canvas) {
if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
getIconBounds(mDotParams.iconBounds);
- mDotParams.spaceForOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
+ Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale());
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c0affb9..3d2d7cf 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -24,15 +24,13 @@
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Surface;
-import android.view.View;
import android.view.WindowManager;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
public class DeviceProfile {
public final InvariantDeviceProfile inv;
@@ -240,7 +238,8 @@
updateWorkspacePadding();
// This is done last, after iconSizePx is calculated above.
- mDotRenderer = new DotRenderer(iconSizePx);
+ mDotRenderer = new DotRenderer(iconSizePx, IconShape.getShapePath(),
+ IconShape.DEFAULT_PATH_SIZE);
}
public DeviceProfile copy(Context context) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6fa9ba9..8d9c520 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -36,6 +36,8 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
@@ -50,11 +52,11 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.BaseItemDragListener;
@@ -68,8 +70,6 @@
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.NonNull;
-
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
@@ -510,10 +510,11 @@
Rect iconBounds = mDotParams.iconBounds;
BubbleTextView.getIconBounds(this, iconBounds,
mLauncher.getWallpaperDeviceProfile().iconSizePx);
+ float iconScale = (float) mBackground.previewSize / iconBounds.width();
+ Utilities.scaleRectAboutCenter(iconBounds, iconScale);
// If we are animating to the accepting state, animate the dot out.
mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
- mDotParams.spaceForOffset.set(getWidth() - iconBounds.right, iconBounds.top);
mDotParams.color = mBackground.getDotColor();
mDotRenderer.draw(canvas, mDotParams);
}