Merging ub-launcher3-qt-dev, build 5508646

Test: Manual

Bug:112934365 [STOP SHIP BUG] Sysui Navigation Bar Prototype Tracking Bug:114136250 Have a more spartan RecentsActivity on android go Bug:118758133 Add logging to track undo box usage Bug:119992316 Widget screen won't close when tapping back or home button Bug:128857973 With dark display and black accent device theme, App list header not visible on all apps screen. Bug:129279637 [Logging] Long press|tap on app icon from Task should be logged Bug:129497226 Remove setInteractionState from the contract Bug:129723135 Broken tests: swipe from killed launcher doesn't open overview Bug:129806241 [a11y][Qt] Remove an app icon in Home screen, the popup "Item removed" with UNDO doesn't follow the settings of Time to take action. Bug:129985827 [Fully Gestural Navigation] Delay Recents animation when swiping up Bug:130851537 Recents and notification shelf both became nonresponsive after I "palmed" the screen while it was on. Bug:131095241 Recents Go should support landscape app => landscape thumbnail transition Bug:131303610 Backedup shortcut icons are not badged Bug:131339235 Don't layout empty task views if we have the real content Bug:131364673 [Failing test] FallbackRecentsTest
Change-Id: I04018e14bd6d9ba77005e72fe20f4a3ede886abf
This commit is contained in:
Matthew Ng
2019-04-26 11:59:39 -07:00
30 changed files with 351 additions and 119 deletions
@@ -18,5 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/darker_gray"/>
<corners android:radius="2dp"/>
<corners android:radius="@dimen/task_thumbnail_corner_radius"/>
</shape>
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 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.
-->
<resources>
<dimen name="task_thumbnail_corner_radius">3dp</dimen>
</resources>
@@ -161,6 +161,13 @@ public final class ContentFillItemAnimator extends SimpleItemAnimator {
private void animateChangeImpl(ViewHolder viewHolder, long startDelay) {
TaskItemView itemView = (TaskItemView) viewHolder.itemView;
if (itemView.getAlpha() == 0) {
// View is still not visible, so we can finish the change immediately.
CONTENT_TRANSITION_PROGRESS.set(itemView, 1.0f);
dispatchChangeFinished(viewHolder, true /* oldItem */);
dispatchFinishedWhenDone();
return;
}
final ObjectAnimator anim =
ObjectAnimator.ofFloat(itemView, CONTENT_TRANSITION_PROGRESS, 0.0f, 1.0f);
anim.setDuration(ITEM_CHANGE_DURATION).setStartDelay(startDelay);
@@ -16,17 +16,23 @@
package com.android.quickstep;
import static android.graphics.Shader.TileMode.CLAMP;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import com.android.launcher3.R;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
@@ -39,11 +45,18 @@ public final class ThumbnailDrawable extends Drawable {
private final Paint mPaint = new Paint();
private final Matrix mMatrix = new Matrix();
private final ThumbnailData mThumbnailData;
private final BitmapShader mShader;
private final RectF mDestRect = new RectF();
private final int mCornerRadius;
private int mRequestedOrientation;
public ThumbnailDrawable(@NonNull ThumbnailData thumbnailData, int requestedOrientation) {
public ThumbnailDrawable(Resources res, @NonNull ThumbnailData thumbnailData,
int requestedOrientation) {
mThumbnailData = thumbnailData;
mRequestedOrientation = requestedOrientation;
mCornerRadius = (int) res.getDimension(R.dimen.task_thumbnail_corner_radius);
mShader = new BitmapShader(mThumbnailData.thumbnail, CLAMP, CLAMP);
mPaint.setShader(mShader);
updateMatrix();
}
@@ -64,12 +77,13 @@ public final class ThumbnailDrawable extends Drawable {
if (mThumbnailData.thumbnail == null) {
return;
}
canvas.drawBitmap(mThumbnailData.thumbnail, mMatrix, mPaint);
canvas.drawRoundRect(mDestRect, mCornerRadius, mCornerRadius, mPaint);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mDestRect.set(bounds);
updateMatrix();
}
@@ -125,5 +139,6 @@ public final class ThumbnailDrawable extends Drawable {
}
// Scale to fill.
mMatrix.postScale(scaleX, scaleY);
mShader.setLocalMatrix(mMatrix);
}
}
@@ -177,7 +177,8 @@ public final class TaskItemView extends LinearLayout {
return mDefaultThumbnail;
}
int orientation = getResources().getConfiguration().orientation;
return new ThumbnailDrawable(thumbnailData, orientation /* requestedOrientation */);
return new ThumbnailDrawable(getResources(), thumbnailData,
orientation /* requestedOrientation */);
}
private @NonNull String getSafeLabel(@Nullable String label) {
@@ -16,6 +16,9 @@
package com.android.quickstep.views;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -53,15 +56,20 @@ final class TaskThumbnailIconView extends ViewGroup {
int width = height;
setMeasuredDimension(width, height);
int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
if (mThumbnailView.getVisibility() != GONE) {
int thumbnailHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
int thumbnailWidthSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
boolean isPortrait =
(getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT);
int thumbnailHeightSpec =
makeMeasureSpec(isPortrait ? height : subItemSize, MeasureSpec.EXACTLY);
int thumbnailWidthSpec =
makeMeasureSpec(isPortrait ? subItemSize : width, MeasureSpec.EXACTLY);
measureChild(mThumbnailView, thumbnailWidthSpec, thumbnailHeightSpec);
}
if (mIconView.getVisibility() != GONE) {
int iconHeightSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
int iconWidthSpec = MeasureSpec.makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
int iconHeightSpec = makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
int iconWidthSpec = makeMeasureSpec(subItemSize, MeasureSpec.EXACTLY);
measureChild(mIconView, iconWidthSpec, iconHeightSpec);
}
}
+2
View File
@@ -71,6 +71,7 @@ enum ItemType {
NOTIFICATION = 8;
TASK = 9; // Each page of Recents UI (QuickStep)
WEB_APP = 10;
TASK_ICON = 11;
}
// Used to define what type of container a Target would represent.
@@ -115,6 +116,7 @@ enum ControlType {
REMOTE_ACTION_SHORTCUT = 17;
APP_USAGE_SETTINGS = 18;
BACK_GESTURE = 19;
UNDO = 20;
}
enum TipType {
@@ -45,7 +45,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.QuickStepContract;
/**
* Touch consumer for handling events to launch assistant from launcher
@@ -103,7 +103,7 @@ public class AssistantTouchConsumer implements InputConsumer {
mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
mSlop = NavigationBarCompat.getQuickStepDragSlopPx();
mSlop = QuickStepContract.getQuickStepDragSlopPx();
mInputMonitorCompat = inputMonitorCompat;
mActivityControlHelper = activityControlHelper;
mState = STATE_INACTIVE;
@@ -16,14 +16,15 @@
package com.android.quickstep;
import static android.view.View.TRANSLATION_Y;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO;
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -177,6 +178,8 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
return new AnimationFactory() {
private Animator mShelfAnim;
private ShelfAnimState mShelfState;
private Animator mAttachToWindowAnim;
private boolean mIsAttachedToWindow;
@Override
public void createActivityController(long transitionLength) {
@@ -221,6 +224,28 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
mShelfAnim.setDuration(duration);
mShelfAnim.start();
}
@Override
public void setRecentsAttachedToAppWindow(boolean attached, boolean animate) {
if (mIsAttachedToWindow == attached && animate) {
return;
}
mIsAttachedToWindow = attached;
if (mAttachToWindowAnim != null) {
mAttachToWindowAnim.cancel();
}
mAttachToWindowAnim = ObjectAnimator.ofFloat(activity.getOverviewPanel(),
RecentsView.CONTENT_ALPHA, attached ? 1 : 0);
mAttachToWindowAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAttachToWindowAnim = null;
}
});
mAttachToWindowAnim.setInterpolator(ACCEL_DEACCEL);
mAttachToWindowAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0);
mAttachToWindowAnim.start();
}
};
}
@@ -60,7 +60,7 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.NavigationBarCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.function.Consumer;
@@ -152,8 +152,8 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mDisplayRotation = display.getRotation();
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
mDragSlop = QuickStepContract.getQuickStepDragSlopPx();
mTouchSlop = QuickStepContract.getQuickStepTouchSlopPx();
mPassedTouchSlop = mPassedDragSlop = continuingPreviousGesture;
}
@@ -267,6 +267,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
}
}
break;
@@ -43,6 +43,7 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -111,6 +112,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
implements SwipeAnimationListener, OnApplyWindowInsetsListener {
private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
private static final Rect TEMP_RECT = new Rect();
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
private static int getFlagForIndex(int index, String name) {
@@ -162,22 +165,23 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
enum GestureEndTarget {
HOME(1, STATE_SCALED_CONTROLLER_HOME, true, false, ContainerType.WORKSPACE),
HOME(1, STATE_SCALED_CONTROLLER_HOME, true, false, ContainerType.WORKSPACE, false),
RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
| STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER),
| STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER, true),
NEW_TASK(0, STATE_START_NEW_TASK, false, true, ContainerType.APP),
NEW_TASK(0, STATE_START_NEW_TASK, false, true, ContainerType.APP, true),
LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP);
LAST_TASK(0, STATE_RESUME_LAST_TASK, false, true, ContainerType.APP, false);
GestureEndTarget(float endShift, int endState, boolean isLauncher, boolean canBeContinued,
int containerType) {
int containerType, boolean recentsAttachedToAppWindow) {
this.endShift = endShift;
this.endState = endState;
this.isLauncher = isLauncher;
this.canBeContinued = canBeContinued;
this.containerType = containerType;
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
}
/** 0 is app, 1 is overview */
@@ -190,6 +194,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
public final boolean canBeContinued;
/** Used to log where the user ended up after the gesture ends */
public final int containerType;
/** Whether RecentsView should be attached to the window as we animate to this target */
public final boolean recentsAttachedToAppWindow;
}
public static final long MAX_SWIPE_DURATION = 350;
@@ -202,6 +208,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
private static final String SCREENSHOT_CAPTURED_EVT = "ScreenshotCaptured";
private static final long SHELF_ANIM_DURATION = 120;
public static final long RECENTS_ATTACH_DURATION = 300;
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
@@ -254,6 +261,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
private int mLogAction = Touch.SWIPE;
private int mLogDirection = Direction.UP;
private PointF mDownPos;
private boolean mIsLikelyToStartNewTask;
private final RecentsAnimationWrapper mRecentsAnimationWrapper;
@@ -277,6 +285,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mMode = SysUINavigationMode.getMode(context);
initStateCallbacks();
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
initTransitionEndpoints(dp);
}
private void initStateCallbacks() {
@@ -434,6 +445,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
mWasLauncherAlreadyVisible, true,
this::onAnimatorPlaybackControllerCreated);
maybeUpdateRecentsAttachedState(false /* animate */);
};
if (mWasLauncherAlreadyVisible) {
// Launcher is visible, but might be about to stop. Thus, if we prepare recents
@@ -538,10 +550,65 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
setShelfState(isPaused ? PEEK : HIDE, FAST_OUT_SLOW_IN, SHELF_ANIM_DURATION);
}
public void maybeUpdateRecentsAttachedState() {
maybeUpdateRecentsAttachedState(true /* animate */);
}
/**
* Determines whether to show or hide RecentsView. The window is always
* synchronized with its corresponding TaskView in RecentsView, so if
* RecentsView is shown, it will appear to be attached to the window.
*
* Note this method has no effect unless the navigation mode is NO_BUTTON.
*/
private void maybeUpdateRecentsAttachedState(boolean animate) {
if (mMode != Mode.NO_BUTTON || mRecentsView == null) {
return;
}
RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationWrapper.targetSet == null
? null
: mRecentsAnimationWrapper.targetSet.findTask(mRunningTaskId);
final boolean recentsAttachedToAppWindow;
int runningTaskIndex = mRecentsView.getRunningTaskIndex();
if (mContinuingLastGesture) {
recentsAttachedToAppWindow = true;
animate = false;
} else if (runningTaskTarget != null && isNotInRecents(runningTaskTarget)) {
// The window is going away so make sure recents is always visible in this case.
recentsAttachedToAppWindow = true;
animate = false;
} else {
if (mGestureEndTarget != null) {
recentsAttachedToAppWindow = mGestureEndTarget.recentsAttachedToAppWindow;
} else {
recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
}
if (animate) {
// Only animate if an adjacent task view is visible on screen.
TaskView adjacentTask1 = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
TaskView adjacentTask2 = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
animate = (adjacentTask1 != null && adjacentTask1.getGlobalVisibleRect(TEMP_RECT))
|| (adjacentTask2 != null && adjacentTask2.getGlobalVisibleRect(TEMP_RECT));
}
}
mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
}
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
mIsLikelyToStartNewTask = isLikelyToStartNewTask;
maybeUpdateRecentsAttachedState();
}
}
@UiThread
public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
mAnimationFactory.setShelfState(shelfState, interpolator, duration);
boolean wasShelfPeeking = mIsShelfPeeking;
mIsShelfPeeking = shelfState == PEEK;
if (mIsShelfPeeking != wasShelfPeeking) {
maybeUpdateRecentsAttachedState();
}
if (mRecentsView != null && shelfState.shouldPreformHaptic) {
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
@@ -869,6 +936,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
mGestureEndTarget = target;
maybeUpdateRecentsAttachedState();
if (mGestureEndTarget == HOME) {
HomeAnimationFactory homeAnimFactory;
if (mActivity != null) {
@@ -902,8 +971,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
windowAnim.start(velocityPxPerMs);
mLauncherTransitionController = null;
} else {
Animator windowAnim = mCurrentShift.animateToValue(start, end);
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
windowAnim.setDuration(duration).setInterpolator(interpolator);
windowAnim.addUpdateListener(valueAnimator -> {
if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
// Views typically don't compute scroll when invisible as an optimization,
// but in our case we need to since the window offset depends on the scroll.
mRecentsView.computeScroll();
}
});
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
@@ -1177,10 +1253,14 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
if (!(app.isNotInRecents
|| app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
if (!isNotInRecents(app)) {
return 0;
}
return expectedAlpha;
}
private static boolean isNotInRecents(RemoteAnimationTargetCompat app) {
return app.isNotInRecents
|| app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
}
}
@@ -48,6 +48,8 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
@@ -365,9 +367,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
private boolean showTaskMenu() {
private boolean showTaskMenu(int action) {
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
mMenuView = TaskMenuView.showForTask(this);
UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
LauncherLogProto.ItemType.TASK_ICON);
if (mMenuView != null) {
mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
}
@@ -377,10 +381,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private void setIcon(Drawable icon) {
if (icon != null) {
mIconView.setDrawable(icon);
mIconView.setOnClickListener(v -> showTaskMenu());
mIconView.setOnClickListener(v -> showTaskMenu(Touch.TAP));
mIconView.setOnLongClickListener(v -> {
requestDisallowInterceptTouchEvent(true);
return showTaskMenu();
return showTaskMenu(Touch.LONGPRESS);
});
} else {
mIconView.setDrawable(null);
@@ -32,6 +32,8 @@ import com.android.launcher3.util.TouchController;
import com.android.quickstep.RecentsModel;
import com.android.systemui.shared.recents.ISystemUiProxy;
import java.io.PrintWriter;
/**
* TouchController for handling touch events that get sent to the StatusBar. Once the
* Once the event delta y passes the touch slop, the events start getting forwarded.
@@ -45,6 +47,7 @@ public class StatusBarTouchController implements TouchController {
protected final TouchEventTranslator mTranslator;
private final float mTouchSlop;
private ISystemUiProxy mSysUiProxy;
private int mLastAction;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
private boolean mCanIntercept;
@@ -56,9 +59,18 @@ public class StatusBarTouchController implements TouchController {
mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
}
@Override
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "mCanIntercept:" + mCanIntercept);
writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
}
private void dispatchTouchEvent(MotionEvent ev) {
try {
if (mSysUiProxy != null) {
mLastAction = ev.getActionMasked();
mSysUiProxy.onStatusBarMotionEvent(ev);
}
} catch (RemoteException e) {
@@ -27,6 +27,10 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -37,10 +41,6 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
/**
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@@ -122,6 +122,13 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
default void setShelfState(ShelfAnimState animState, Interpolator interpolator,
long duration) { }
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
*/
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
}
interface HomeAnimationFactory {
@@ -15,10 +15,6 @@
*/
package com.android.quickstep;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
@@ -34,13 +30,7 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import androidx.annotation.WorkerThread;
/**
* Sets overview interaction flags, such as:
*
* - FLAG_DISABLE_QUICK_SCRUB
* - FLAG_DISABLE_SWIPE_UP
* - FLAG_SHOW_OVERVIEW_BUTTON
*
* @see com.android.systemui.shared.system.NavigationBarCompat.InteractionType and associated flags.
* Sets alpha for the back button
*/
public class OverviewInteractionState {
@@ -50,11 +40,10 @@ public class OverviewInteractionState {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<OverviewInteractionState> INSTANCE =
new MainThreadInitializedObject<>((c) -> new OverviewInteractionState(c));
new MainThreadInitializedObject<>(OverviewInteractionState::new);
private static final int MSG_SET_PROXY = 200;
private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
private static final int MSG_APPLY_FLAGS = 202;
private final Context mContext;
private final Handler mUiHandler;
@@ -62,7 +51,6 @@ public class OverviewInteractionState {
// These are updated on the background thread
private ISystemUiProxy mISystemUiProxy;
private boolean mSwipeUpEnabled;
private float mBackButtonAlpha = 1;
private OverviewInteractionState(Context context) {
@@ -83,7 +71,7 @@ public class OverviewInteractionState {
}
public void setBackButtonAlpha(float alpha, boolean animate) {
if (!mSwipeUpEnabled) {
if (!modeSupportsGestures()) {
alpha = 1;
}
mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_ALPHA);
@@ -111,30 +99,10 @@ public class OverviewInteractionState {
case MSG_SET_BACK_BUTTON_ALPHA:
applyBackButtonAlpha((float) msg.obj, msg.arg1 == 1);
return true;
case MSG_APPLY_FLAGS:
break;
}
applyFlags();
return true;
}
@WorkerThread
private void applyFlags() {
if (mISystemUiProxy == null) {
return;
}
int flags = FLAG_DISABLE_QUICK_SCRUB;
if (!mSwipeUpEnabled) {
flags = FLAG_DISABLE_SWIPE_UP | FLAG_DISABLE_QUICK_SCRUB | FLAG_SHOW_OVERVIEW_BUTTON;
}
try {
mISystemUiProxy.setInteractionState(flags);
} catch (RemoteException e) {
Log.w(TAG, "Unable to update overview interaction flags", e);
}
}
@WorkerThread
private void applyBackButtonAlpha(float alpha, boolean animate) {
if (mISystemUiProxy == null) {
@@ -148,18 +116,20 @@ public class OverviewInteractionState {
}
private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
mSwipeUpEnabled = mode.hasGestures;
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
mBgHandler.obtainMessage(MSG_APPLY_FLAGS).sendToTarget();
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
if (mSwipeUpEnabled && !Utilities.getPrefs(mContext).getBoolean(
if (modeSupportsGestures() && !Utilities.getPrefs(mContext).getBoolean(
HAS_ENABLED_QUICKSTEP_ONCE, true)) {
Utilities.getPrefs(mContext).edit()
.putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
.putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
.apply();
.putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
.putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
.apply();
}
}
private boolean modeSupportsGestures() {
return SysUINavigationMode.getMode(mContext).hasGestures;
}
}
@@ -12,21 +12,29 @@ import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
import android.content.Intent;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
import com.android.quickstep.views.DigitalWellBeingToast;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Duration;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class DigitalWellBeingToastTest extends AbstractQuickStepTest {
private static final String CALCULATOR_PACKAGE =
resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
@Test
public void testToast() throws Exception {
startAppFast(CALCULATOR_PACKAGE);
final UsageStatsManager usageStatsManager =
mTargetContext.getSystemService(UsageStatsManager.class);
final int observerId = 0;
@@ -32,6 +32,7 @@ import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.RemoteException;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -65,10 +66,11 @@ public class FallbackRecentsTest {
@Rule public final TestRule mSetLauncherCommand;
public FallbackRecentsTest() {
public FallbackRecentsTest() throws RemoteException {
Instrumentation instrumentation = getInstrumentation();
Context context = instrumentation.getContext();
mDevice = UiDevice.getInstance(instrumentation);
mDevice.setOrientationNatural();
mLauncher = new LauncherInstrumentation(instrumentation);
mQuickstepOnOffExecutor = new NavigationModeSwitchRule(mLauncher);
@@ -88,7 +88,6 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
}
@Test
@Ignore // b/129723135
@NavigationModeSwitch
public void testStressSwipeToOverview() {
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
+5 -4
View File
@@ -19,12 +19,13 @@
<resources>
<!-- Launcher theme -->
<style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
<item name="android:windowBackground">@android:color/transparent</item>
<style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorEdgeEffect">#FF757575</item>
<item name="android:windowActionBar">false</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowShowWallpaper">true</item>
</style>
<style name="LauncherTheme" parent="@style/BaseLauncherTheme">
@@ -16,6 +16,9 @@
package com.android.launcher3;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -121,8 +124,12 @@ public class DeleteDropTarget extends ButtonDropTarget {
int itemPage = mLauncher.getWorkspace().getCurrentPage();
onAccessibilityDrop(null, item);
ModelWriter modelWriter = mLauncher.getModelWriter();
Runnable onUndoClicked = () -> {
modelWriter.abortDelete(itemPage);
mLauncher.getUserEventDispatcher().logActionOnControl(TAP, UNDO);
};
Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
modelWriter::commitDelete, () -> modelWriter.abortDelete(itemPage));
modelWriter::commitDelete, onUndoClicked);
}
}
+7 -5
View File
@@ -75,6 +75,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -150,9 +152,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
/**
* Default launcher application.
*/
@@ -2198,7 +2197,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
// When undoing the removal of the last item on a page, return to that page.
mWorkspace.setCurrentPage(pageBoundFirst);
// Since we are just resetting the current page without user interaction,
// override the previous page so we don't log the page switch.
mWorkspace.setCurrentPage(pageBoundFirst, pageBoundFirst /* overridePrevPage */);
TraceHelper.endSection("finishBindingItems");
}
@@ -2345,7 +2346,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
writer.println(" mPendingActivityResult=" + mPendingActivityResult);
writer.println(" mRotationHelper: " + mRotationHelper);
// Extra logging for b/116853349
mDragLayer.dumpAlpha(writer);
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
dumpMisc(writer);
try {
@@ -50,6 +50,7 @@ import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
import com.android.launcher3.uioverrides.UiFactory;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -144,6 +145,15 @@ public class LauncherStateManager {
return mCurrentStableState;
}
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "LauncherState");
writer.println(prefix + "\tmLastStableState:" + mLastStableState);
writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
writer.println(prefix + "\tmState:" + mState);
writer.println(prefix + "\tmRestState:" + mRestState);
writer.println(prefix + "\tisInTransition:" + (mConfig.mCurrentAnimation != null));
}
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
mStateHandlers = UiFactory.getStateHandler(mLauncher);
+6 -2
View File
@@ -300,10 +300,14 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
return page;
}
public void setCurrentPage(int currentPage) {
setCurrentPage(currentPage, INVALID_PAGE);
}
/**
* Sets the current page.
*/
public void setCurrentPage(int currentPage) {
public void setCurrentPage(int currentPage, int overridePrevPage) {
if (!mScroller.isFinished()) {
abortScrollerAnimation(true);
}
@@ -312,7 +316,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
if (getChildCount() == 0) {
return;
}
int prevPage = mCurrentPage;
int prevPage = overridePrevPage != INVALID_PAGE ? overridePrevPage : mCurrentPage;
mCurrentPage = validateNewPage(currentPage);
updateCurrentPageScroll();
notifyPageSwitchListener(prevPage);
@@ -106,4 +106,11 @@ public class AccessibilityManagerCompat {
}
return false;
}
public static int getRecommendedTimeoutMillis(Context context, int originalTimeout, int flags) {
if (Utilities.ATLEAST_Q) {
return getManager(context).getRecommendedTimeoutMillis(originalTimeout, flags);
}
return originalTimeout;
}
}
@@ -696,5 +696,4 @@ public class DragController implements DragDriver.EventListener, TouchController
public void removeDropTarget(DropTarget target) {
mDropTargets.remove(target);
}
}
@@ -47,6 +47,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
* Class for handling model updates.
@@ -151,15 +152,13 @@ public class ModelWriter {
public void moveItemInDatabase(final ItemInfo item,
int container, int screenId, int cellX, int cellY) {
updateItemInfoProps(item, container, screenId, cellX, cellY);
final ContentWriter writer = new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
.put(Favorites.CELLY, item.cellY)
.put(Favorites.RANK, item.rank)
.put(Favorites.SCREEN, item.screenId);
enqueueDeleteRunnable(new UpdateItemRunnable(item, writer));
enqueueDeleteRunnable(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
.put(Favorites.CELLY, item.cellY)
.put(Favorites.RANK, item.rank)
.put(Favorites.SCREEN, item.screenId)));
}
/**
@@ -195,25 +194,26 @@ public class ModelWriter {
item.spanX = spanX;
item.spanY = spanY;
final ContentWriter writer = new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
.put(Favorites.CELLY, item.cellY)
.put(Favorites.RANK, item.rank)
.put(Favorites.SPANX, item.spanX)
.put(Favorites.SPANY, item.spanY)
.put(Favorites.SCREEN, item.screenId);
mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
mWorkerExecutor.execute(new UpdateItemRunnable(item, () ->
new ContentWriter(mContext)
.put(Favorites.CONTAINER, item.container)
.put(Favorites.CELLX, item.cellX)
.put(Favorites.CELLY, item.cellY)
.put(Favorites.RANK, item.rank)
.put(Favorites.SPANX, item.spanX)
.put(Favorites.SPANY, item.spanY)
.put(Favorites.SCREEN, item.screenId)));
}
/**
* Update an item to the database in a specified container.
*/
public void updateItemInDatabase(ItemInfo item) {
ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
mWorkerExecutor.execute(new UpdateItemRunnable(item, () -> {
ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
return writer;
}));
}
/**
@@ -224,17 +224,18 @@ public class ModelWriter {
int container, int screenId, int cellX, int cellY) {
updateItemInfoProps(item, container, screenId, cellX, cellY);
final ContentWriter writer = new ContentWriter(mContext);
final ContentResolver cr = mContext.getContentResolver();
item.onAddToDatabase(writer);
item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getInt(Settings.EXTRA_VALUE);
writer.put(Favorites._ID, item.id);
ModelVerifier verifier = new ModelVerifier();
final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
mWorkerExecutor.execute(() -> {
// Write the item on background thread, as some properties might have been updated in
// the background.
final ContentWriter writer = new ContentWriter(mContext);
item.onAddToDatabase(writer);
writer.put(Favorites._ID, item.id);
cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
synchronized (mBgDataModel) {
@@ -354,10 +355,10 @@ public class ModelWriter {
private class UpdateItemRunnable extends UpdateItemBaseRunnable {
private final ItemInfo mItem;
private final ContentWriter mWriter;
private final Supplier<ContentWriter> mWriter;
private final int mItemId;
UpdateItemRunnable(ItemInfo item, ContentWriter writer) {
UpdateItemRunnable(ItemInfo item, Supplier<ContentWriter> writer) {
mItem = item;
mWriter = writer;
mItemId = item.id;
@@ -366,7 +367,8 @@ public class ModelWriter {
@Override
public void run() {
Uri uri = Favorites.getContentUri(mItemId);
mContext.getContentResolver().update(uri, mWriter.getValues(mContext), null, null);
mContext.getContentResolver().update(uri, mWriter.get().getValues(mContext),
null, null);
updateItemArrays(mItem, mItemId);
}
}
@@ -16,12 +16,18 @@
package com.android.launcher3.util;
import android.content.Context;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* Supports various IO utility functions
@@ -52,4 +58,23 @@ public class IOUtils {
}
return total;
}
/**
* Utility method to debug binary data
*/
public static String createTempFile(Context context, byte[] data) {
if (!FeatureFlags.IS_DOGFOOD_BUILD) {
throw new IllegalStateException("Method only allowed in development mode");
}
String name = UUID.randomUUID().toString();
File file = new File(context.getCacheDir(), name);
try (FileOutputStream fo = new FileOutputStream(file)) {
fo.write(data);
fo.flush();
} catch (Exception e) {
throw new RuntimeException(e);
}
return file.getAbsolutePath();
}
}
@@ -18,6 +18,8 @@ package com.android.launcher3.util;
import android.view.MotionEvent;
import java.io.PrintWriter;
public interface TouchController {
/**
@@ -29,4 +31,6 @@ public interface TouchController {
* Called when the draglayer receives a intercept touch event.
*/
boolean onControllerInterceptTouchEvent(MotionEvent ev);
default void dump(String prefix, PrintWriter writer) { }
}
@@ -383,8 +383,13 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
return mMultiValueAlpha.getProperty(index);
}
public void dumpAlpha(PrintWriter writer) {
writer.println(" dragLayerAlpha : " + mMultiValueAlpha );
public void dump(String prefix, PrintWriter writer) {
writer.println(prefix + "DragLayer");
if (mActiveController != null) {
writer.println(prefix + "\tactiveController: " + mActiveController);
mActiveController.dump(prefix + "\t", writer);
}
writer.println(prefix + "\tdragLayerAlpha : " + mMultiValueAlpha );
}
public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
@@ -16,6 +16,9 @@
package com.android.launcher3.views;
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -29,6 +32,7 @@ import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.dragndrop.DragLayer;
/**
@@ -38,7 +42,7 @@ public class Snackbar extends AbstractFloatingView {
private static final long SHOW_DURATION_MS = 180;
private static final long HIDE_DURATION_MS = 180;
private static final long TIMEOUT_DURATION_MS = 4000;
private static final int TIMEOUT_DURATION_MS = 4000;
private final Launcher mLauncher;
private Runnable mOnDismissed;
@@ -131,7 +135,9 @@ public class Snackbar extends AbstractFloatingView {
.setDuration(SHOW_DURATION_MS)
.setInterpolator(Interpolators.ACCEL_DEACCEL)
.start();
snackbar.postDelayed(() -> snackbar.close(true), TIMEOUT_DURATION_MS);
int timeout = AccessibilityManagerCompat.getRecommendedTimeoutMillis(launcher,
TIMEOUT_DURATION_MS, FLAG_CONTENT_TEXT | FLAG_CONTENT_CONTROLS);
snackbar.postDelayed(() -> snackbar.close(true), timeout);
}
@Override