Merging ub-launcher3-qt-dev, build 5633311
Test: Manual Bug:119330044 P1 [STOPSHIP] Add back shape detection in Launcher icons Bug:123900446 P1 App to home animation should zoom into the app icon Bug:132298752 P1 Adding widget locks home screen page Bug:132757019 P1 Swipe up twice quickly from app in 2 button mode; can't access all apps Bug:133518678 P1 In 2 button mode, flinging up strongly favours quickswitch Bug:133806285 P1 All apps causes overview to peek in Bug:133844237 P1 Respect limited width Exclusion Rects in bottom area? Bug:133867119 P2 Lab-only flake: want to switch from workspace to all apps; Swipe failed to receive an event for the swipe end Bug:134062513 P1 Quickswitch sends lifecycle events to -1 Bug:134094839 P1 Launcher kept crashing on OTA update Bug:134096917 P1 Launcher animation component gets stuck during swipe up Bug:134164918 P1 STOPSHIP: Disable chips hard-coded value in qt_dev Launcher Bug:134172603 P1 Tracing for lab-only flakes Bug:134442147 P1 Sometimes All Apps won't update on app install/uninstall/update Change-Id: Ia530fbf9bb5fa67b602ce44aef0cc4a1d1494e13
This commit is contained in:
commit
a3ed49d566
|
@ -29,7 +29,7 @@ public abstract class GoActivityControlHelper<T extends BaseDraggingActivity> im
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpComplete(T activity) {
|
||||
public void onSwipeUpToRecentsComplete(T activity) {
|
||||
// Go does not support swipe up gesture.
|
||||
}
|
||||
|
||||
|
|
|
@ -46,13 +46,15 @@ public class BaseIconFactory implements AutoCloseable {
|
|||
|
||||
private IconNormalizer mNormalizer;
|
||||
private ShadowGenerator mShadowGenerator;
|
||||
private final boolean mShapeDetection;
|
||||
|
||||
private Drawable mWrapperIcon;
|
||||
private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
|
||||
|
||||
protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
|
||||
protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize,
|
||||
boolean shapeDetection) {
|
||||
mContext = context.getApplicationContext();
|
||||
|
||||
mShapeDetection = shapeDetection;
|
||||
mFillResIconDpi = fillResIconDpi;
|
||||
mIconBitmapSize = iconBitmapSize;
|
||||
|
||||
|
@ -64,6 +66,10 @@ public class BaseIconFactory implements AutoCloseable {
|
|||
clear();
|
||||
}
|
||||
|
||||
protected BaseIconFactory(Context context, int fillResIconDpi, int iconBitmapSize) {
|
||||
this(context, fillResIconDpi, iconBitmapSize, false);
|
||||
}
|
||||
|
||||
protected void clear() {
|
||||
mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
|
||||
mDisableColorExtractor = false;
|
||||
|
@ -78,7 +84,7 @@ public class BaseIconFactory implements AutoCloseable {
|
|||
|
||||
public IconNormalizer getNormalizer() {
|
||||
if (mNormalizer == null) {
|
||||
mNormalizer = new IconNormalizer(mIconBitmapSize);
|
||||
mNormalizer = new IconNormalizer(mContext, mIconBitmapSize, mShapeDetection);
|
||||
}
|
||||
return mNormalizer;
|
||||
}
|
||||
|
@ -209,18 +215,19 @@ public class BaseIconFactory implements AutoCloseable {
|
|||
}
|
||||
AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
|
||||
dr.setBounds(0, 0, 1, 1);
|
||||
scale = getNormalizer().getScale(icon, outIconBounds);
|
||||
if (!(icon instanceof AdaptiveIconDrawable)) {
|
||||
boolean[] outShape = new boolean[1];
|
||||
scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
|
||||
if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) {
|
||||
FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
|
||||
fsd.setDrawable(icon);
|
||||
fsd.setScale(scale);
|
||||
icon = dr;
|
||||
scale = getNormalizer().getScale(icon, outIconBounds);
|
||||
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
|
||||
|
||||
((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
|
||||
}
|
||||
} else {
|
||||
scale = getNormalizer().getScale(icon, outIconBounds);
|
||||
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
|
||||
}
|
||||
|
||||
outScale[0] = scale;
|
||||
|
|
|
@ -18,16 +18,22 @@ package com.android.launcher3.icons;
|
|||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.AdaptiveIconDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
|
@ -51,6 +57,9 @@ public class IconNormalizer {
|
|||
|
||||
private static final int MIN_VISIBLE_ALPHA = 40;
|
||||
|
||||
// Shape detection related constants
|
||||
private static final float BOUND_RATIO_MARGIN = .05f;
|
||||
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
|
||||
private static final float SCALE_NOT_INITIALIZED = 0;
|
||||
|
||||
// Ratio of the diameter of an normalized circular icon to the actual icon size.
|
||||
|
@ -59,18 +68,24 @@ public class IconNormalizer {
|
|||
private final int mMaxSize;
|
||||
private final Bitmap mBitmap;
|
||||
private final Canvas mCanvas;
|
||||
private final Paint mPaintMaskShape;
|
||||
private final Paint mPaintMaskShapeOutline;
|
||||
private final byte[] mPixels;
|
||||
|
||||
private final RectF mAdaptiveIconBounds;
|
||||
private float mAdaptiveIconScale;
|
||||
|
||||
private boolean mEnableShapeDetection;
|
||||
|
||||
// for each y, stores the position of the leftmost x and the rightmost x
|
||||
private final float[] mLeftBorder;
|
||||
private final float[] mRightBorder;
|
||||
private final Rect mBounds;
|
||||
private final Path mShapePath;
|
||||
private final Matrix mMatrix;
|
||||
|
||||
/** package private **/
|
||||
IconNormalizer(int iconBitmapSize) {
|
||||
IconNormalizer(Context context, int iconBitmapSize, boolean shapeDetection) {
|
||||
// Use twice the icon size as maximum size to avoid scaling down twice.
|
||||
mMaxSize = iconBitmapSize * 2;
|
||||
mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
|
||||
|
@ -81,7 +96,22 @@ public class IconNormalizer {
|
|||
mBounds = new Rect();
|
||||
mAdaptiveIconBounds = new RectF();
|
||||
|
||||
mPaintMaskShape = new Paint();
|
||||
mPaintMaskShape.setColor(Color.RED);
|
||||
mPaintMaskShape.setStyle(Paint.Style.FILL);
|
||||
mPaintMaskShape.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
|
||||
|
||||
mPaintMaskShapeOutline = new Paint();
|
||||
mPaintMaskShapeOutline.setStrokeWidth(
|
||||
2 * context.getResources().getDisplayMetrics().density);
|
||||
mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
|
||||
mPaintMaskShapeOutline.setColor(Color.BLACK);
|
||||
mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
|
||||
mShapePath = new Path();
|
||||
mMatrix = new Matrix();
|
||||
mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
|
||||
mEnableShapeDetection = shapeDetection;
|
||||
}
|
||||
|
||||
private static float getScale(float hullArea, float boundingArea, float fullArea) {
|
||||
|
@ -126,6 +156,72 @@ public class IconNormalizer {
|
|||
return getScale(hullArea, hullArea, size * size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the shape of the icon is same as the path.
|
||||
* For this method to work, the shape path bounds should be in [0,1]x[0,1] bounds.
|
||||
*/
|
||||
private boolean isShape(Path maskPath) {
|
||||
// Condition1:
|
||||
// If width and height of the path not close to a square, then the icon shape is
|
||||
// not same as the mask shape.
|
||||
float iconRatio = ((float) mBounds.width()) / mBounds.height();
|
||||
if (Math.abs(iconRatio - 1) > BOUND_RATIO_MARGIN) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Not same as mask shape because width != height. " + iconRatio);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Condition 2:
|
||||
// Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
|
||||
// should generate transparent image, if the actual icon is equivalent to the shape.
|
||||
|
||||
// Fit the shape within the icon's bounding box
|
||||
mMatrix.reset();
|
||||
mMatrix.setScale(mBounds.width(), mBounds.height());
|
||||
mMatrix.postTranslate(mBounds.left, mBounds.top);
|
||||
maskPath.transform(mMatrix, mShapePath);
|
||||
|
||||
// XOR operation
|
||||
mCanvas.drawPath(mShapePath, mPaintMaskShape);
|
||||
|
||||
// DST_OUT operation around the mask path outline
|
||||
mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
|
||||
|
||||
// Check if the result is almost transparent
|
||||
return isTransparentBitmap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine if certain the bitmap is transparent.
|
||||
*/
|
||||
private boolean isTransparentBitmap() {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(mPixels);
|
||||
buffer.rewind();
|
||||
mBitmap.copyPixelsToBuffer(buffer);
|
||||
|
||||
int y = mBounds.top;
|
||||
// buffer position
|
||||
int index = y * mMaxSize;
|
||||
// buffer shift after every row, width of buffer = mMaxSize
|
||||
int rowSizeDiff = mMaxSize - mBounds.right;
|
||||
|
||||
int sum = 0;
|
||||
for (; y < mBounds.bottom; y++) {
|
||||
index += mBounds.left;
|
||||
for (int x = mBounds.left; x < mBounds.right; x++) {
|
||||
if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
|
||||
sum++;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
index += rowSizeDiff;
|
||||
}
|
||||
|
||||
float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
|
||||
return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
|
||||
* matches the design guidelines for a launcher icon.
|
||||
|
@ -140,7 +236,8 @@ public class IconNormalizer {
|
|||
*
|
||||
* @param outBounds optional rect to receive the fraction distance from each edge.
|
||||
*/
|
||||
public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) {
|
||||
public synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds,
|
||||
@Nullable Path path, @Nullable boolean[] outMaskShape) {
|
||||
if (BaseIconFactory.ATLEAST_OREO && d instanceof AdaptiveIconDrawable) {
|
||||
if (mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
|
||||
mAdaptiveIconScale = normalizeAdaptiveIcon(d, mMaxSize, mAdaptiveIconBounds);
|
||||
|
@ -242,7 +339,9 @@ public class IconNormalizer {
|
|||
1 - ((float) mBounds.right) / width,
|
||||
1 - ((float) mBounds.bottom) / height);
|
||||
}
|
||||
|
||||
if (outMaskShape != null && mEnableShapeDetection && outMaskShape.length > 0) {
|
||||
outMaskShape[0] = isShape(path);
|
||||
}
|
||||
// Area of the rectangle required to fit the convex hull
|
||||
float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
|
||||
return getScale(area, rectArea, width * height);
|
||||
|
|
|
@ -27,5 +27,5 @@
|
|||
<!-- Swipe up to home related -->
|
||||
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
|
||||
<dimen name="swipe_up_y_overshoot">10dp</dimen>
|
||||
<dimen name="swipe_up_max_workspace_trans_y">-80dp</dimen>
|
||||
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
|
||||
</resources>
|
|
@ -164,13 +164,6 @@ public class PredictionRowView extends LinearLayout implements
|
|||
mParent = parent;
|
||||
}
|
||||
|
||||
private void setPredictionsEnabled(boolean predictionsEnabled) {
|
||||
if (predictionsEnabled != mPredictionsEnabled) {
|
||||
mPredictionsEnabled = predictionsEnabled;
|
||||
updateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVisibility() {
|
||||
setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
|
||||
}
|
||||
|
@ -220,8 +213,7 @@ public class PredictionRowView extends LinearLayout implements
|
|||
* If the number of predicted apps is the same as the previous list of predicted apps,
|
||||
* we can optimize by swapping them in place.
|
||||
*/
|
||||
public void setPredictedApps(boolean predictionsEnabled, List<ComponentKeyMapper> apps) {
|
||||
setPredictionsEnabled(predictionsEnabled);
|
||||
public void setPredictedApps(List<ComponentKeyMapper> apps) {
|
||||
mPredictedAppComponents.clear();
|
||||
mPredictedAppComponents.addAll(apps);
|
||||
|
||||
|
@ -237,11 +229,6 @@ public class PredictionRowView extends LinearLayout implements
|
|||
}
|
||||
|
||||
private void applyPredictionApps() {
|
||||
if (!mPredictionsEnabled) {
|
||||
mParent.onHeightUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
if (getChildCount() != mNumPredictedAppsPerRow) {
|
||||
while (getChildCount() > mNumPredictedAppsPerRow) {
|
||||
removeViewAt(0);
|
||||
|
@ -282,8 +269,11 @@ public class PredictionRowView extends LinearLayout implements
|
|||
}
|
||||
}
|
||||
|
||||
if (predictionCount == 0) {
|
||||
setPredictionsEnabled(false);
|
||||
boolean predictionsEnabled = predictionCount > 0;
|
||||
if (predictionsEnabled != mPredictionsEnabled) {
|
||||
mPredictionsEnabled = predictionsEnabled;
|
||||
mLauncher.reapplyUi();
|
||||
updateVisibility();
|
||||
}
|
||||
mParent.onHeightUpdated();
|
||||
}
|
||||
|
|
|
@ -177,16 +177,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
|
|||
}
|
||||
|
||||
private void applyState(PredictionState state) {
|
||||
boolean wasEnabled = mCurrentState.isEnabled;
|
||||
mCurrentState = state;
|
||||
if (mAppsView != null) {
|
||||
mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
|
||||
.setPredictedApps(mCurrentState.isEnabled, mCurrentState.apps);
|
||||
|
||||
if (wasEnabled != mCurrentState.isEnabled) {
|
||||
// Reapply state as the State UI might have changed.
|
||||
Launcher.getLauncher(mAppsView.getContext()).getStateManager().reapplyState(true);
|
||||
}
|
||||
.setPredictedApps(mCurrentState.apps);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,8 @@ import com.android.quickstep.views.TaskView;
|
|||
public class BackgroundAppState extends OverviewState {
|
||||
|
||||
private static final int STATE_FLAGS =
|
||||
FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
|
||||
FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY
|
||||
| FLAG_DISABLE_INTERACTION;
|
||||
|
||||
public BackgroundAppState(int id) {
|
||||
this(id, LauncherLogProto.ContainerType.TASKSWITCHER);
|
||||
|
|
|
@ -70,7 +70,7 @@ public final class FallbackActivityControllerHelper implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpComplete(RecentsActivity activity) {
|
||||
public void onSwipeUpToRecentsComplete(RecentsActivity activity) {
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
recentsView.getClearAllButton().setVisibilityAlpha(1);
|
||||
recentsView.setDisallowScrollToClearAll(false);
|
||||
|
|
|
@ -91,7 +91,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpComplete(Launcher activity) {
|
||||
public void onSwipeUpToRecentsComplete(Launcher activity) {
|
||||
// Re apply state in case we did something funky during the transition.
|
||||
activity.getStateManager().reapplyState();
|
||||
DiscoveryBounce.showForOverviewIfNeeded(activity);
|
||||
|
|
|
@ -58,6 +58,8 @@ import android.view.MotionEvent;
|
|||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
|
||||
import com.android.launcher3.MainThreadExecutor;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.ResourceUtils;
|
||||
|
@ -84,6 +86,7 @@ import com.android.systemui.shared.system.InputConsumerController;
|
|||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
|
||||
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
|
@ -268,6 +271,9 @@ public class TouchInteractionService extends Service implements
|
|||
private final RectF mSwipeTouchRegion = new RectF();
|
||||
private ComponentName mGestureBlockingActivity;
|
||||
|
||||
private Region mExclusionRegion;
|
||||
private SystemGestureExclusionListenerCompat mExclusionListener;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
@ -284,14 +290,23 @@ public class TouchInteractionService extends Service implements
|
|||
mIsUserUnlocked = false;
|
||||
registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
|
||||
}
|
||||
onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
|
||||
|
||||
mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay()
|
||||
.getDisplayId();
|
||||
|
||||
String blockingActivity = getString(R.string.gesture_blocking_activity);
|
||||
mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null :
|
||||
ComponentName.unflattenFromString(blockingActivity);
|
||||
|
||||
mExclusionListener = new SystemGestureExclusionListenerCompat(mDefaultDisplayId) {
|
||||
@Override
|
||||
@BinderThread
|
||||
public void onExclusionChanged(Region region) {
|
||||
// Assignments are atomic, it should be safe on binder thread
|
||||
mExclusionRegion = region;
|
||||
}
|
||||
};
|
||||
|
||||
onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this));
|
||||
sConnected = true;
|
||||
}
|
||||
|
||||
|
@ -370,6 +385,12 @@ public class TouchInteractionService extends Service implements
|
|||
|
||||
disposeEventHandlers();
|
||||
initInputMonitor();
|
||||
|
||||
if (mMode == Mode.NO_BUTTON) {
|
||||
mExclusionListener.register();
|
||||
} else {
|
||||
mExclusionListener.unregister();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -437,6 +458,7 @@ public class TouchInteractionService extends Service implements
|
|||
sConnected = false;
|
||||
Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
|
||||
SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
|
||||
mExclusionListener.unregister();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
@ -557,10 +579,15 @@ public class TouchInteractionService extends Service implements
|
|||
final ActivityControlHelper activityControl =
|
||||
mOverviewComponentObserver.getActivityControlHelper();
|
||||
boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
|
||||
|
||||
// mExclusionRegion can change on binder thread, use a local instance here.
|
||||
Region exclusionRegion = mExclusionRegion;
|
||||
boolean disableHorizontalSwipe = mMode == Mode.NO_BUTTON && exclusionRegion != null
|
||||
&& exclusionRegion.contains((int) event.getX(), (int) event.getY());
|
||||
return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
|
||||
mOverviewComponentObserver.getOverviewIntent(), activityControl,
|
||||
shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
|
||||
mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion);
|
||||
mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, disableHorizontalSwipe);
|
||||
}
|
||||
|
||||
private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
|
|||
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
|
||||
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
|
||||
import static com.android.launcher3.Utilities.postAsyncCallback;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
@ -106,7 +107,6 @@ import com.android.quickstep.views.TaskView;
|
|||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.LatencyTrackerCompat;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.WindowCallbacksCompat;
|
||||
|
@ -353,7 +353,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
| STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
|
||||
| STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
|
||||
| STATE_GESTURE_STARTED,
|
||||
this::setupLauncherUiAfterSwipeUpAnimation);
|
||||
this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
|
||||
|
||||
mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
|
||||
mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
|
||||
|
@ -647,6 +647,12 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
}
|
||||
|
||||
private void buildAnimationController() {
|
||||
if (mGestureEndTarget == HOME || (mLauncherTransitionController != null
|
||||
&& mLauncherTransitionController.getAnimationPlayer().isStarted())) {
|
||||
// We don't want a new mLauncherTransitionController if mGestureEndTarget == HOME (it
|
||||
// has its own animation) or if we're already animating the current controller.
|
||||
return;
|
||||
}
|
||||
initTransitionEndpoints(mActivity.getDeviceProfile());
|
||||
mAnimationFactory.createActivityController(mTransitionDragLength);
|
||||
}
|
||||
|
@ -902,10 +908,14 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
// If swiping at a diagonal, base end target on the faster velocity.
|
||||
endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
|
||||
? NEW_TASK : HOME;
|
||||
} else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) {
|
||||
// If user scrolled to a new task, only go to recents if they already passed
|
||||
// the overview threshold. Otherwise, we'll snap to the new task and launch it.
|
||||
endTarget = RECENTS;
|
||||
} else if (endVelocity < 0) {
|
||||
if (reachedOverviewThreshold) {
|
||||
endTarget = RECENTS;
|
||||
} else {
|
||||
// If swiping at a diagonal, base end target on the faster velocity.
|
||||
endTarget = goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity)
|
||||
? NEW_TASK : RECENTS;
|
||||
}
|
||||
} else {
|
||||
endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
|
||||
}
|
||||
|
@ -976,6 +986,12 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
} else if (endTarget == RECENTS) {
|
||||
mLiveTileOverlay.startIconAnimation();
|
||||
if (mRecentsView != null) {
|
||||
int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
|
||||
if (mRecentsView.getNextPage() != nearestPage) {
|
||||
// We shouldn't really scroll to the next page when swiping up to recents.
|
||||
// Only allow settling on the next page if it's nearest to the center.
|
||||
mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration));
|
||||
}
|
||||
if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) {
|
||||
mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION);
|
||||
}
|
||||
|
@ -1057,8 +1073,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
setStateOnUiThread(target.endState);
|
||||
}
|
||||
});
|
||||
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
|
||||
windowAnim.start(velocityPxPerMs);
|
||||
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
|
||||
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
|
||||
mLauncherTransitionController = null;
|
||||
} else {
|
||||
|
@ -1131,16 +1147,25 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
|
||||
AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
|
||||
|
||||
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
|
||||
// rounding at the end of the animation.
|
||||
float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
|
||||
float endRadius = startRect.width() / 6f;
|
||||
// We want the window alpha to be 0 once this threshold is met, so that the
|
||||
// FolderIconView can be seen morphing into the icon shape.
|
||||
final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
|
||||
anim.addOnUpdateListener((currentRect, progress) -> {
|
||||
homeAnim.setPlayFraction(progress);
|
||||
|
||||
float windowAlpha = Math.max(0, Utilities.mapToRange(progress, 0,
|
||||
windowAlphaThreshold, 1f, 0f, Interpolators.LINEAR));
|
||||
float alphaProgress = ACCEL_1_5.getInterpolation(progress);
|
||||
float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
|
||||
windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
|
||||
mTransformParams.setProgress(progress)
|
||||
.setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
|
||||
if (isFloatingIconView) {
|
||||
mTransformParams.setCornerRadius(endRadius * progress + startRadius
|
||||
* (1f - progress));
|
||||
}
|
||||
mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
|
||||
false /* launcherOnTop */);
|
||||
|
||||
|
@ -1254,12 +1279,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
}
|
||||
|
||||
private void invalidateHandlerWithLauncher() {
|
||||
if (mLauncherTransitionController != null) {
|
||||
if (mLauncherTransitionController.getAnimationPlayer().isStarted()) {
|
||||
mLauncherTransitionController.getAnimationPlayer().cancel();
|
||||
}
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
endLauncherTransitionController();
|
||||
|
||||
mRecentsView.onGestureAnimationEnd();
|
||||
|
||||
|
@ -1267,6 +1287,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
|
||||
}
|
||||
|
||||
private void endLauncherTransitionController() {
|
||||
if (mLauncherTransitionController != null) {
|
||||
mLauncherTransitionController.getAnimationPlayer().end();
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyTransitionCancelled() {
|
||||
mAnimationFactory.onTransitionCancelled();
|
||||
}
|
||||
|
@ -1368,12 +1395,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
|
|||
doLogGesture(HOME);
|
||||
}
|
||||
|
||||
private void setupLauncherUiAfterSwipeUpAnimation() {
|
||||
if (mLauncherTransitionController != null) {
|
||||
mLauncherTransitionController.getAnimationPlayer().end();
|
||||
mLauncherTransitionController = null;
|
||||
}
|
||||
mActivityControlHelper.onSwipeUpComplete(mActivity);
|
||||
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
|
||||
endLauncherTransitionController();
|
||||
mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
|
||||
mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
|
||||
mRecentsView.onSwipeUpAnimationSuccess();
|
||||
|
||||
|
|
|
@ -62,6 +62,10 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
|||
private static final String TAG = "AssistantTouchConsumer";
|
||||
private static final long RETRACT_ANIMATION_DURATION_MS = 300;
|
||||
|
||||
// From //java/com/google/android/apps/gsa/search/shared/util/OpaContract.java.
|
||||
private static final String OPA_BUNDLE_TRIGGER = "triggered_by";
|
||||
// From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto.
|
||||
private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
|
||||
private static final String INVOCATION_TYPE_KEY = "invocation_type";
|
||||
private static final int INVOCATION_TYPE_GESTURE = 1;
|
||||
private static final int INVOCATION_TYPE_FLING = 6;
|
||||
|
@ -230,6 +234,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer
|
|||
startAssistantInternal(SWIPE);
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
|
||||
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
|
||||
mSysUiProxy.startAssistant(args);
|
||||
mLaunchedAssistant = true;
|
||||
|
|
|
@ -111,6 +111,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
|
||||
private final float mDragSlop;
|
||||
private final float mSquaredTouchSlop;
|
||||
private final boolean mDisableHorizontalSwipe;
|
||||
|
||||
// Slop used to check when we start moving window.
|
||||
private boolean mPassedDragSlop;
|
||||
|
@ -132,7 +133,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
InputConsumerController inputConsumer,
|
||||
Consumer<OtherActivityInputConsumer> onCompleteCallback,
|
||||
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
|
||||
RectF swipeTouchRegion) {
|
||||
RectF swipeTouchRegion, boolean disableHorizontalSwipe) {
|
||||
super(base);
|
||||
|
||||
mMainThreadHandler = new Handler(Looper.getMainLooper());
|
||||
|
@ -162,6 +163,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
mSquaredTouchSlop = slop * slop;
|
||||
|
||||
mPassedTouchSlop = mPassedDragSlop = continuingPreviousGesture;
|
||||
mDisableHorizontalSwipe = !mPassedTouchSlop && disableHorizontalSwipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,6 +171,13 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
return TYPE_OTHER_ACTIVITY;
|
||||
}
|
||||
|
||||
private void forceCancelGesture(MotionEvent ev) {
|
||||
int action = ev.getAction();
|
||||
ev.setAction(ACTION_CANCEL);
|
||||
finishTouchTracking(ev);
|
||||
ev.setAction(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
if (mVelocityTracker == null) {
|
||||
|
@ -216,10 +225,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
// Cancel interaction in case of multi-touch interaction
|
||||
int ptrIdx = ev.getActionIndex();
|
||||
if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
|
||||
int action = ev.getAction();
|
||||
ev.setAction(ACTION_CANCEL);
|
||||
finishTouchTracking(ev);
|
||||
ev.setAction(action);
|
||||
forceCancelGesture(ev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -258,7 +264,15 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
}
|
||||
|
||||
if (!mPassedTouchSlop) {
|
||||
if (squaredHypot(displacementX, mLastPos.y - mDownPos.y) >= mSquaredTouchSlop) {
|
||||
float displacementY = mLastPos.y - mDownPos.y;
|
||||
if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) {
|
||||
if (mDisableHorizontalSwipe
|
||||
&& Math.abs(displacementX) > Math.abs(displacementY)) {
|
||||
// Horizontal gesture is not allowed in this region
|
||||
forceCancelGesture(ev);
|
||||
break;
|
||||
}
|
||||
|
||||
mPassedTouchSlop = true;
|
||||
|
||||
if (mIsDeferredDownTarget) {
|
||||
|
|
|
@ -197,10 +197,15 @@ public class ClipAnimationHelper {
|
|||
mTmpMatrix.postTranslate(app.position.x, app.position.y);
|
||||
mClipRectF.roundOut(crop);
|
||||
if (mSupportsRoundedCornersOnWindows) {
|
||||
float windowCornerRadius = mUseRoundedCornersOnWindows
|
||||
? mWindowCornerRadius : 0;
|
||||
cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
|
||||
mTaskCornerRadius);
|
||||
if (params.cornerRadius > -1) {
|
||||
cornerRadius = params.cornerRadius;
|
||||
scale = params.currentRect.width() / crop.width();
|
||||
} else {
|
||||
float windowCornerRadius = mUseRoundedCornersOnWindows
|
||||
? mWindowCornerRadius : 0;
|
||||
cornerRadius = Utilities.mapRange(progress, windowCornerRadius,
|
||||
mTaskCornerRadius);
|
||||
}
|
||||
mCurrentCornerRadius = cornerRadius;
|
||||
}
|
||||
}
|
||||
|
@ -355,6 +360,7 @@ public class ClipAnimationHelper {
|
|||
@Nullable RectF currentRect;
|
||||
float targetAlpha;
|
||||
boolean forLiveTile;
|
||||
float cornerRadius;
|
||||
|
||||
SyncRtSurfaceTransactionApplierCompat syncTransactionApplier;
|
||||
|
||||
|
@ -365,6 +371,7 @@ public class ClipAnimationHelper {
|
|||
currentRect = null;
|
||||
targetAlpha = 0;
|
||||
forLiveTile = false;
|
||||
cornerRadius = -1;
|
||||
}
|
||||
|
||||
public TransformParams setProgress(float progress) {
|
||||
|
@ -373,6 +380,11 @@ public class ClipAnimationHelper {
|
|||
return this;
|
||||
}
|
||||
|
||||
public TransformParams setCornerRadius(float cornerRadius) {
|
||||
this.cornerRadius = cornerRadius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public TransformParams setCurrentRectAndTargetAlpha(RectF currentRect, float targetAlpha) {
|
||||
this.currentRect = currentRect;
|
||||
this.targetAlpha = targetAlpha;
|
||||
|
|
|
@ -44,14 +44,13 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
|
|||
*/
|
||||
public class StaggeredWorkspaceAnim {
|
||||
|
||||
private static final int APP_CLOSE_ROW_START_DELAY_MS = 16;
|
||||
private static final int ALPHA_DURATION_MS = 200;
|
||||
private static final int APP_CLOSE_ROW_START_DELAY_MS = 10;
|
||||
private static final int ALPHA_DURATION_MS = 250;
|
||||
|
||||
private static final float MAX_VELOCITY_PX_PER_S = 22f;
|
||||
|
||||
private static final float DAMPING_RATIO =
|
||||
(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY + SpringForce.DAMPING_RATIO_LOW_BOUNCY) / 2f;
|
||||
private static final float STIFFNESS = SpringForce.STIFFNESS_LOW;
|
||||
private static final float DAMPING_RATIO = 0.7f;
|
||||
private static final float STIFFNESS = 150f;
|
||||
|
||||
private final float mVelocity;
|
||||
private final float mSpringTransY;
|
||||
|
@ -71,9 +70,9 @@ public class StaggeredWorkspaceAnim {
|
|||
|
||||
// Scale the translationY based on the initial velocity to better sync the workspace items
|
||||
// with the floating view.
|
||||
float transFactor = 0.1f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S;
|
||||
float transFactor = 0.2f + 0.9f * Math.abs(velocity) / MAX_VELOCITY_PX_PER_S;
|
||||
mSpringTransY = transFactor * launcher.getResources()
|
||||
.getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);;
|
||||
.getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
|
||||
|
||||
DeviceProfile grid = launcher.getDeviceProfile();
|
||||
ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace()
|
||||
|
|
|
@ -119,6 +119,9 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
|
||||
(long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
|
||||
|
||||
private static final long CROP_DURATION = 375;
|
||||
private static final long RADIUS_DURATION = 375;
|
||||
|
||||
public static final int RECENTS_LAUNCH_DURATION = 336;
|
||||
private static final int LAUNCHER_RESUME_START_DELAY = 100;
|
||||
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
|
||||
|
@ -494,10 +497,10 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
EXAGGERATED_EASE);
|
||||
FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
|
||||
alphaDuration, LINEAR);
|
||||
FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, APP_LAUNCH_DURATION,
|
||||
FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION,
|
||||
EXAGGERATED_EASE);
|
||||
FloatProp mWindowRadius = new FloatProp(startCrop / 2f, windowRadius, 0,
|
||||
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
|
||||
RADIUS_DURATION, EXAGGERATED_EASE);
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
|
|
|
@ -51,7 +51,7 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
|
|||
|
||||
int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
|
||||
|
||||
void onSwipeUpComplete(T activity);
|
||||
void onSwipeUpToRecentsComplete(T activity);
|
||||
|
||||
void onAssistantVisibilityChanged(float visibility);
|
||||
|
||||
|
|
|
@ -277,6 +277,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
|||
|
||||
final Handler mHandler = new Handler();
|
||||
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
|
||||
private boolean mDeferredResumePending;
|
||||
|
||||
private float mCurrentAssistantVisibility = 0f;
|
||||
|
||||
|
@ -889,26 +890,40 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
|||
}
|
||||
|
||||
private void handleDeferredResume() {
|
||||
if (hasBeenResumed()) {
|
||||
if (hasBeenResumed() && !mStateManager.getState().disableInteraction) {
|
||||
getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
|
||||
mStateManager.getState().containerType, -1);
|
||||
getUserEventDispatcher().startSession();
|
||||
|
||||
UiFactory.onLauncherStateOrResumeChanged(this);
|
||||
AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
|
||||
resetPendingActivityResultIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetPendingActivityResultIfNeeded() {
|
||||
if (hasBeenResumed() && mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
|
||||
UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
|
||||
// Process any items that were added while Launcher was away.
|
||||
InstallShortcutReceiver.disableAndFlushInstallQueue(
|
||||
InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
|
||||
|
||||
// Refresh shortcuts if the permission changed.
|
||||
mModel.refreshShortcutsIfRequired();
|
||||
|
||||
DiscoveryBounce.showForHomeIfNeeded(this);
|
||||
|
||||
if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
|
||||
UiFactory.resetPendingActivityResults(this, mPendingActivityRequestCode);
|
||||
}
|
||||
mDeferredResumePending = false;
|
||||
} else {
|
||||
mDeferredResumePending = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onStateSet(LauncherState state) {
|
||||
getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
|
||||
resetPendingActivityResultIfNeeded();
|
||||
if (mDeferredResumePending) {
|
||||
handleDeferredResume();
|
||||
}
|
||||
if (mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -922,14 +937,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
|
|||
Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
|
||||
|
||||
setOnResumeCallback(null);
|
||||
// Process any items that were added while Launcher was away.
|
||||
InstallShortcutReceiver.disableAndFlushInstallQueue(
|
||||
InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
|
||||
|
||||
// Refresh shortcuts if the permission changed.
|
||||
mModel.refreshShortcutsIfRequired();
|
||||
|
||||
DiscoveryBounce.showForHomeIfNeeded(this);
|
||||
if (mLauncherCallbacks != null) {
|
||||
mLauncherCallbacks.onResume();
|
||||
}
|
||||
|
|
|
@ -68,9 +68,6 @@ public class LauncherAppState {
|
|||
}
|
||||
|
||||
private LauncherAppState(Context context) {
|
||||
if (!UserManagerCompat.getInstance(context).isUserUnlocked(Process.myUserHandle())) {
|
||||
throw new RuntimeException("LauncherAppState should not start in direct boot mode");
|
||||
}
|
||||
if (getLocalProvider(context) == null) {
|
||||
throw new RuntimeException(
|
||||
"Initializing LauncherAppState in the absence of LauncherProvider");
|
||||
|
|
|
@ -21,7 +21,6 @@ import android.os.Bundle;
|
|||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* LauncherCallbacks is an interface used to extend the Launcher activity. It includes many hooks
|
||||
|
@ -53,6 +52,11 @@ public interface LauncherCallbacks {
|
|||
boolean handleBackPressed();
|
||||
void onTrimMemory(int level);
|
||||
|
||||
/**
|
||||
* Called when the launcher state changed
|
||||
*/
|
||||
default void onStateChanged() { }
|
||||
|
||||
/*
|
||||
* Extension points for providing custom behavior on certain user interactions.
|
||||
*/
|
||||
|
|
|
@ -40,6 +40,7 @@ import android.animation.AnimatorListenerAdapter;
|
|||
import android.animation.AnimatorSet;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
|
@ -474,6 +475,11 @@ public class LauncherStateManager {
|
|||
// Only change the stable states after the transitions have finished
|
||||
if (state != mCurrentStableState) {
|
||||
mLastStableState = state.getHistoryForState(mCurrentStableState);
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
|
||||
"mCurrentStableState = " + state.getClass().getSimpleName() + " @ " +
|
||||
android.util.Log.getStackTraceString(new Throwable()));
|
||||
}
|
||||
mCurrentStableState = state;
|
||||
}
|
||||
|
||||
|
|
|
@ -625,15 +625,16 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
|||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
final boolean result = super.dispatchTouchEvent(ev);
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mAllAppsStore.setDeferUpdates(true);
|
||||
if (result) mAllAppsStore.setDeferUpdates(true);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mAllAppsStore.setDeferUpdates(false);
|
||||
break;
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
|
|||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
|
@ -29,6 +30,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
|
|||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.SpringObjectAnimator;
|
||||
import com.android.launcher3.anim.PropertySetter;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.MultiValueAlpha;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
|
@ -162,6 +164,10 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
|||
@Override
|
||||
public void setStateWithAnimation(LauncherState toState,
|
||||
AnimatorSetBuilder builder, AnimationConfig config) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG,
|
||||
"setStateWithAnimation " + toState.getClass().getSimpleName());
|
||||
}
|
||||
float targetProgress = toState.getVerticalProgress(mLauncher);
|
||||
if (Float.compare(mProgress, targetProgress) == 0) {
|
||||
setAlphas(toState, config, builder);
|
||||
|
|
|
@ -29,7 +29,7 @@ public class FlingSpringAnim {
|
|||
|
||||
private static final float FLING_FRICTION = 1.5f;
|
||||
private static final float SPRING_STIFFNESS = 200;
|
||||
private static final float SPRING_DAMPING = 0.85f;
|
||||
private static final float SPRING_DAMPING = 0.8f;
|
||||
|
||||
private final FlingAnimation mFlingAnim;
|
||||
private SpringAnimation mSpringAnim;
|
||||
|
|
|
@ -105,7 +105,7 @@ abstract class BaseFlags {
|
|||
"ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
|
||||
|
||||
public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
|
||||
"ENABLE_HINTS_IN_OVERVIEW", true,
|
||||
"ENABLE_HINTS_IN_OVERVIEW", false,
|
||||
"Show chip hints and gleams on the overview screen");
|
||||
|
||||
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
|
||||
|
|
|
@ -130,16 +130,6 @@ public class DragLayer extends BaseDragLayer<Launcher> {
|
|||
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean findActiveController(MotionEvent ev) {
|
||||
if (mActivity.getStateManager().getState().disableInteraction) {
|
||||
// You Shall Not Pass!!!
|
||||
mActiveController = null;
|
||||
return true;
|
||||
}
|
||||
return super.findActiveController(ev);
|
||||
}
|
||||
|
||||
private boolean isEventOverAccessibleDropTargetBar(MotionEvent ev) {
|
||||
return isInAccessibleDrag() && isEventOverView(mActivity.getDropTargetBar(), ev);
|
||||
}
|
||||
|
|
|
@ -243,7 +243,7 @@ public class DragView extends View implements LauncherStateManager.StateListener
|
|||
nDr = new AdaptiveIconDrawable(new ColorDrawable(Color.BLACK), null);
|
||||
}
|
||||
Utilities.scaleRectAboutCenter(bounds,
|
||||
li.getNormalizer().getScale(nDr, null));
|
||||
li.getNormalizer().getScale(nDr, null, null, null));
|
||||
}
|
||||
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
|
||||
|
||||
|
|
|
@ -91,6 +91,10 @@ public abstract class IconShape {
|
|||
|
||||
private SparseArray<TypedValue> mAttrs;
|
||||
|
||||
public boolean enableShapeDetection(){
|
||||
return false;
|
||||
};
|
||||
|
||||
public abstract void drawShape(Canvas canvas, float offsetX, float offsetY, float radius,
|
||||
Paint paint);
|
||||
|
||||
|
@ -194,6 +198,11 @@ public abstract class IconShape {
|
|||
protected float getStartRadius(Rect startRect) {
|
||||
return startRect.width() / 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enableShapeDetection() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RoundedSquare extends SimpleRectShape {
|
||||
|
|
|
@ -30,6 +30,7 @@ import com.android.launcher3.InvariantDeviceProfile;
|
|||
import com.android.launcher3.ItemInfoWithIcon;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.graphics.IconShape;
|
||||
import com.android.launcher3.model.PackageItemInfo;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutManager;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
@ -50,11 +51,15 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
private static LauncherIcons sPool;
|
||||
private static int sPoolId = 0;
|
||||
|
||||
public static LauncherIcons obtain(Context context) {
|
||||
return obtain(context, IconShape.getShape().enableShapeDetection());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new Message instance from the global pool. Allows us to
|
||||
* avoid allocating new objects in many cases.
|
||||
*/
|
||||
public static LauncherIcons obtain(Context context) {
|
||||
public static LauncherIcons obtain(Context context, boolean shapeDetection) {
|
||||
int poolId;
|
||||
synchronized (sPoolSync) {
|
||||
if (sPool != null) {
|
||||
|
@ -67,7 +72,8 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
}
|
||||
|
||||
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
|
||||
return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
|
||||
return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
|
||||
shapeDetection);
|
||||
}
|
||||
|
||||
public static void clearPool() {
|
||||
|
@ -81,8 +87,9 @@ public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
|
|||
|
||||
private LauncherIcons next;
|
||||
|
||||
private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
|
||||
super(context, fillResIconDpi, iconBitmapSize);
|
||||
private LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
|
||||
boolean shapeDetection) {
|
||||
super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
|
||||
mPoolId = poolId;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import android.animation.AnimatorListenerAdapter;
|
|||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
|
@ -118,6 +119,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
|
||||
@Override
|
||||
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 1 " + ev);
|
||||
}
|
||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
mNoIntercept = !canInterceptTouch(ev);
|
||||
if (mNoIntercept) {
|
||||
|
@ -147,6 +151,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
return false;
|
||||
}
|
||||
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 2 ");
|
||||
}
|
||||
onControllerTouchEvent(ev);
|
||||
return mDetector.isDraggingOrSettling();
|
||||
}
|
||||
|
@ -234,6 +241,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
|
||||
@Override
|
||||
public void onDragStart(boolean start) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 1 " + start);
|
||||
}
|
||||
mStartState = mLauncher.getStateManager().getState();
|
||||
if (mStartState == ALL_APPS) {
|
||||
mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
|
||||
|
@ -243,6 +253,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
|
||||
}
|
||||
if (mCurrentAnimation == null) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 2");
|
||||
}
|
||||
mFromState = mStartState;
|
||||
mToState = null;
|
||||
cancelAnimationControllers();
|
||||
|
@ -552,6 +565,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
}
|
||||
|
||||
private void cancelAnimationControllers() {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "cancelAnimationControllers");
|
||||
}
|
||||
mCurrentAnimation = null;
|
||||
cancelAtomicComponentsController();
|
||||
mDetector.finishedScrolling();
|
||||
|
|
|
@ -158,6 +158,9 @@ public class SwipeDetector {
|
|||
// SETTLING -> (View settled) -> IDLE
|
||||
|
||||
private void setState(ScrollState newState) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- start: " + newState);
|
||||
}
|
||||
if (DBG) {
|
||||
Log.d(TAG, "setState:" + mState + "->" + newState);
|
||||
}
|
||||
|
@ -165,6 +168,9 @@ public class SwipeDetector {
|
|||
if (newState == ScrollState.DRAGGING) {
|
||||
initializeDragging();
|
||||
if (mState == ScrollState.IDLE) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "setState -- 1: " + newState);
|
||||
}
|
||||
reportDragStart(false /* recatch */);
|
||||
} else if (mState == ScrollState.SETTLING) {
|
||||
reportDragStart(true /* recatch */);
|
||||
|
@ -318,9 +324,15 @@ public class SwipeDetector {
|
|||
break;
|
||||
}
|
||||
mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 1");
|
||||
}
|
||||
|
||||
// handle state and listener calls.
|
||||
if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onTouchEvent 2");
|
||||
}
|
||||
setState(ScrollState.DRAGGING);
|
||||
}
|
||||
if (mState == ScrollState.DRAGGING) {
|
||||
|
|
|
@ -99,7 +99,6 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
|
|||
handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY());
|
||||
}
|
||||
|
||||
cancelLongPress();
|
||||
if (handleLongPress) {
|
||||
mLongPressState = STATE_REQUESTED;
|
||||
mTouchDownPoint.set(ev.getX(), ev.getY());
|
||||
|
@ -148,6 +147,10 @@ public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListe
|
|||
}
|
||||
}
|
||||
|
||||
if (action == ACTION_UP || action == ACTION_CANCEL) {
|
||||
cancelLongPress();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ import androidx.dynamicanimation.animation.SpringForce;
|
|||
public class FloatingIconView extends View implements
|
||||
Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener {
|
||||
|
||||
public static final float SHAPE_PROGRESS_DURATION = 0.15f;
|
||||
public static final float SHAPE_PROGRESS_DURATION = 0.10f;
|
||||
private static final int FADE_DURATION_MS = 200;
|
||||
private static final Rect sTmpRect = new Rect();
|
||||
private static final RectF sTmpRectF = new RectF();
|
||||
|
@ -85,8 +85,8 @@ public class FloatingIconView extends View implements
|
|||
|
||||
// We spring the foreground drawable relative to the icon's movement in the DragLayer.
|
||||
// We then use these two factor values to scale the movement of the fg within this view.
|
||||
private static final int FG_TRANS_X_FACTOR = 80;
|
||||
private static final int FG_TRANS_Y_FACTOR = 100;
|
||||
private static final int FG_TRANS_X_FACTOR = 60;
|
||||
private static final int FG_TRANS_Y_FACTOR = 75;
|
||||
|
||||
private static final FloatPropertyCompat<FloatingIconView> mFgTransYProperty
|
||||
= new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransY") {
|
||||
|
@ -529,7 +529,8 @@ public class FloatingIconView extends View implements
|
|||
bounds.inset(mBlurSizeOutline / 2, mBlurSizeOutline / 2);
|
||||
|
||||
try (LauncherIcons li = LauncherIcons.obtain(mLauncher)) {
|
||||
Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null));
|
||||
Utilities.scaleRectAboutCenter(bounds, li.getNormalizer().getScale(drawable, null,
|
||||
null, null));
|
||||
}
|
||||
|
||||
bounds.inset(
|
||||
|
|
|
@ -20,8 +20,10 @@ import static com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel
|
|||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
@ -42,6 +44,10 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
|
|||
super(launcher);
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
mHeight = allAppsContainer.getVisibleBounds().height();
|
||||
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
|
||||
"apps_list_view");
|
||||
// Wait for the recycler to populate.
|
||||
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -115,7 +121,7 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
|
|||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer,
|
||||
final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
|
||||
appIconSelector);
|
||||
ensureIconVisible(appIcon, allAppsContainer, appListRecycler);
|
||||
return new AppIcon(mLauncher, appIcon);
|
||||
|
|
|
@ -21,9 +21,9 @@ import static android.content.pm.PackageManager.DONT_KILL_APP;
|
|||
import static android.content.pm.PackageManager.MATCH_ALL;
|
||||
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
|
||||
|
||||
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
|
||||
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
|
||||
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
|
||||
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Instrumentation;
|
||||
|
@ -257,6 +257,7 @@ public final class LauncherInstrumentation {
|
|||
}
|
||||
|
||||
private void fail(String message) {
|
||||
log("Hierarchy dump for: " + getContextDescription() + message);
|
||||
dumpViewHierarchy();
|
||||
Assert.fail("http://go/tapl : " + getContextDescription() + message);
|
||||
}
|
||||
|
@ -570,6 +571,16 @@ public final class LauncherInstrumentation {
|
|||
return object;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
|
||||
final UiObject2 object = container.wait(
|
||||
Until.findObject(selector),
|
||||
WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a launcher object id: " + selector + " in container: " +
|
||||
container.getResourceName(), object);
|
||||
return object;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private boolean hasLauncherObject(String resId) {
|
||||
return mDevice.hasObject(getLauncherObjectSelector(resId));
|
||||
|
@ -580,11 +591,6 @@ public final class LauncherInstrumentation {
|
|||
return waitForObjectBySelector(getLauncherObjectSelector(resName));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForLauncherObjectByClass(String clazz) {
|
||||
return waitForObjectBySelector(getLauncherObjectSelectorByClass(clazz));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForFallbackLauncherObject(String resName) {
|
||||
return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
|
||||
|
@ -600,10 +606,6 @@ public final class LauncherInstrumentation {
|
|||
return By.res(getLauncherPackageName(), resName);
|
||||
}
|
||||
|
||||
BySelector getLauncherObjectSelectorByClass(String clazz) {
|
||||
return By.pkg(getLauncherPackageName()).clazz(clazz);
|
||||
}
|
||||
|
||||
BySelector getFallbackLauncherObjectSelector(String resName) {
|
||||
return By.res(getOverviewPackageName(), resName);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue