[automerger skipped] Import translations. DO NOT MERGE
am: 885dfb7134 -s ours
am skip reason: subject contains skip directive
Change-Id: I515adc3e8a0a534fde521ec1f0ff909b53c28057
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index fe159b5..92900f2 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -25,6 +25,7 @@
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
+import android.content.Context;
import android.os.Handler;
import android.util.Log;
@@ -151,7 +152,7 @@
}
@Override
- public ActivityOptions toActivityOptions(Handler handler, long duration) {
+ public ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) {
@@ -165,7 +166,7 @@
);
return;
}
- result.setAnimation(createWindowAnimation(targetCompats));
+ result.setAnimation(createWindowAnimation(targetCompats), context);
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 900b94e..577b175 100644
--- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -34,6 +34,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.UiThreadHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -137,6 +139,9 @@
return sConnected;
}
+ public static final LooperExecutor BACKGROUND_EXECUTOR =
+ new LooperExecutor(UiThreadHelper.getBackgroundLooper());
+
private RecentsModel mRecentsModel;
private OverviewComponentObserver mOverviewComponentObserver;
private OverviewCommandHelper mOverviewCommandHelper;
@@ -180,4 +185,8 @@
}
return mMyBinder;
}
+
+ public static boolean isInitialized() {
+ return true;
+ }
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index db5515b..fc7d6b3 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -22,6 +22,7 @@
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.NonNull;
/**
* This class will be moved to androidx library. There shouldn't be any dependency outside
@@ -154,7 +155,7 @@
* @param scale returns the scale result from normalization
* @return a bitmap suitable for disaplaying as an icon at various system UIs.
*/
- public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user,
+ public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, UserHandle user,
boolean shrinkNonAdaptiveIcons, boolean isInstantApp, float[] scale) {
if (scale == null) {
scale = new float[1];
@@ -207,8 +208,11 @@
mDisableColorExtractor = true;
}
- private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, boolean shrinkNonAdaptiveIcons,
- RectF outIconBounds, float[] outScale) {
+ private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon,
+ boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) {
+ if (icon == null) {
+ return null;
+ }
float scale = 1f;
if (shrinkNonAdaptiveIcons && ATLEAST_OREO) {
@@ -264,7 +268,7 @@
* @param icon drawable that should be flattened to a bitmap
* @param scale the scale to apply before drawing {@param icon} on the canvas
*/
- public Bitmap createIconBitmap(Drawable icon, float scale, int size) {
+ public Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size) {
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
if (icon == null) {
return bitmap;
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 332e0fa..5465480 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -23,6 +23,7 @@
package="com.android.launcher3" >
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
index 311db21..425fb13 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -31,6 +31,9 @@
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -41,9 +44,6 @@
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.util.Themes;
-import androidx.annotation.ColorInt;
-import androidx.core.content.ContextCompat;
-
/**
* A view which shows a horizontal divider
*/
@@ -288,10 +288,10 @@
}
@Override
- public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
- PropertySetter setter, Interpolator fadeInterpolator) {
+ public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+ PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
// Don't use setViewAlpha as we want to control the visibility ourselves.
- setter.setFloat(this, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+ setter.setFloat(this, ALPHA, hasAllAppsContent ? 1 : 0, allAppsFade);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index cb5cbdd..0c7ba9c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -32,6 +32,9 @@
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
@@ -62,9 +65,6 @@
import java.util.Collections;
import java.util.List;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
@TargetApi(Build.VERSION_CODES.P)
public class PredictionRowView extends LinearLayout implements
LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow {
@@ -80,7 +80,7 @@
@Override
public Integer get(PredictionRowView view) {
- return view.mIconCurrentTextAlpha;
+ return view.mIconLastSetTextAlpha;
}
};
@@ -103,6 +103,8 @@
private final int mIconTextColor;
private final int mIconFullTextAlpha;
+ private int mIconLastSetTextAlpha;
+ // Might use mIconFullTextAlpha instead of mIconLastSetTextAlpha if we are translucent.
private int mIconCurrentTextAlpha;
private FloatingHeaderView mParent;
@@ -315,8 +317,14 @@
}
}
- public void setTextAlpha(int alpha) {
- mIconCurrentTextAlpha = alpha;
+ public void setTextAlpha(int textAlpha) {
+ mIconLastSetTextAlpha = textAlpha;
+ if (getAlpha() < 1 && textAlpha > 0) {
+ // If the entire header is translucent, make sure the text is at full opacity so it's
+ // not double-translucent. However, we support keeping the text invisible (alpha == 0).
+ textAlpha = mIconFullTextAlpha;
+ }
+ mIconCurrentTextAlpha = textAlpha;
int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
for (int i = 0; i < getChildCount(); i++) {
((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
@@ -324,6 +332,13 @@
}
@Override
+ public void setAlpha(float alpha) {
+ super.setAlpha(alpha);
+ // Reapply text alpha so that we update it to be full alpha if the row is now translucent.
+ setTextAlpha(mIconLastSetTextAlpha);
+ }
+
+ @Override
public boolean hasOverlappingRendering() {
return false;
}
@@ -351,23 +366,15 @@
}
@Override
- public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
- PropertySetter setter, Interpolator fadeInterpolator) {
- boolean isDrawn = getAlpha() > 0;
- int textAlpha = hasHeaderExtra
- ? (hasContent ? mIconFullTextAlpha : 0) // Text follows the content visibility
- : mIconCurrentTextAlpha; // Leave as before
- if (!isDrawn) {
- // If the header is not drawn, no need to animate the text alpha
- setTextAlpha(textAlpha);
- } else {
- setter.setInt(this, TEXT_ALPHA, textAlpha, fadeInterpolator);
- }
-
+ public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+ PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
+ // Text follows all apps visibility
+ int textAlpha = hasHeaderExtra && hasAllAppsContent ? mIconFullTextAlpha : 0;
+ setter.setInt(this, TEXT_ALPHA, textAlpha, allAppsFade);
setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
- (hasHeaderExtra && !hasContent) ? 1 : 0, LINEAR);
+ (hasHeaderExtra && !hasAllAppsContent) ? 1 : 0, LINEAR);
setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
- fadeInterpolator);
+ headerFade);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index a3c2e3c..596bc4f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -164,6 +164,11 @@
}
}
+ if (FeatureFlags.PULL_DOWN_STATUS_BAR
+ && !launcher.getDeviceProfile().isMultiWindowMode) {
+ list.add(new StatusBarTouchController(launcher));
+ }
+
list.add(new LauncherTaskViewController(launcher));
return list.toArray(new TouchController[list.size()]);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 5ee08c1..50cfac8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -19,6 +19,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.LayoutUtils;
@@ -68,8 +69,8 @@
if (taskCount == 0) {
return super.getOverviewScaleAndTranslation(launcher);
}
- TaskView dummyTask = recentsView.getTaskViewAt(Math.max(taskCount - 1,
- recentsView.getCurrentPage()));
+ TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange(
+ recentsView.getCurrentPage(), 0, taskCount - 1));
return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask)
.getScaleAndTranslation();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
index c954762..427206a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -15,7 +15,9 @@
*/
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
@@ -43,6 +45,7 @@
if (this == OVERVIEW_PEEK && fromState == NORMAL) {
builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+ builder.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 5543860..151ceb8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -43,7 +43,6 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -128,14 +127,15 @@
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
} else {
+ boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
+ && launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
- (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
- ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
+ (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
}
}
@Override
- public float getWorkspaceScrimAlpha(Launcher launcher) {
+ public float getOverviewScrimAlpha(Launcher launcher) {
return 0.5f;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 3fe4bcf..ab346c0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -23,13 +23,17 @@
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -43,6 +47,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.util.MotionPauseDetector;
@@ -102,6 +107,9 @@
mPeekAnim.start();
recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+
+ mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
+ peekDuration, 0);
});
}
}
@@ -120,6 +128,13 @@
LauncherState toState) {
if (fromState == NORMAL && toState == ALL_APPS) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ // Fade in prediction icons quickly, then rest of all apps after reaching overview.
+ float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
+ - OVERVIEW.getVerticalProgress(mLauncher);
+ builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(ACCEL,
+ 0, ALL_APPS_CONTENT_FADE_THRESHOLD));
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(LINEAR,
+ progressToReachOverview, 1));
// Get workspace out of the way quickly, to prepare for potential pause.
builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
@@ -168,6 +183,21 @@
}
@Override
+ protected void goToTargetState(LauncherState targetState, int logAction) {
+ if (mPeekAnim != null && mPeekAnim.isStarted()) {
+ // Don't jump to the target state until overview is no longer peeking.
+ mPeekAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ FlingAndHoldTouchController.super.goToTargetState(targetState, logAction);
+ }
+ });
+ } else {
+ super.goToTargetState(targetState, logAction);
+ }
+ }
+
+ @Override
protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) {
if (handlingOverviewAnim()) {
// We don't want the state transition to all apps to animate overview,
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 8e32bb3..00e4f58 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -16,10 +16,10 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -266,8 +266,8 @@
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
}
- float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
+ float nextFrameProgress = Utilities.boundToRange(progress
+ + velocity * getSingleFrameMs(mActivity) / Math.abs(mEndDisplacement), 0f, 1f);
mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
new file mode 100644
index 0000000..d627a7f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -0,0 +1,509 @@
+/*
+ * 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.
+ */
+package com.android.quickstep;
+
+import static android.os.VibrationEffect.EFFECT_CLICK;
+import static android.os.VibrationEffect.createPredefined;
+
+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.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
+import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
+import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+
+import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.views.FloatingIconView;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.inputconsumers.InputConsumer;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
+import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+
+import java.util.function.Consumer;
+
+import androidx.annotation.UiThread;
+
+/**
+ * Base class for swipe up handler with some utility methods
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q extends RecentsView>
+ implements SwipeAnimationListener {
+
+ private static final String TAG = "BaseSwipeUpHandler";
+ protected static final Rect TEMP_RECT = new Rect();
+
+ // Start resisting when swiping past this factor of mTransitionDragLength.
+ private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f;
+ // This is how far down we can scale down, where 0f is full screen and 1f is recents.
+ private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f;
+ private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
+
+ // The distance needed to drag to reach the task size in recents.
+ protected int mTransitionDragLength;
+ // How much further we can drag past recents, as a factor of mTransitionDragLength.
+ protected float mDragLengthFactor = 1;
+
+ protected final Context mContext;
+ protected final OverviewComponentObserver mOverviewComponentObserver;
+ protected final ActivityControlHelper<T> mActivityControlHelper;
+ protected final RecentsModel mRecentsModel;
+ protected final int mRunningTaskId;
+
+ protected final ClipAnimationHelper mClipAnimationHelper;
+ protected final TransformParams mTransformParams = new TransformParams();
+
+ private final Vibrator mVibrator;
+ protected final Mode mMode;
+
+ // Shift in the range of [0, 1].
+ // 0 => preview snapShot is completely visible, and hotseat is completely translated down
+ // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
+ // visible.
+ protected final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+
+ protected final ActivityInitListener mActivityInitListener;
+ protected final RecentsAnimationWrapper mRecentsAnimationWrapper;
+
+ protected T mActivity;
+ protected Q mRecentsView;
+ protected DeviceProfile mDp;
+ private final int mPageSpacing;
+
+ protected Runnable mGestureEndCallback;
+
+ protected final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler();
+ protected MultiStateCallback mStateCallback;
+
+ protected boolean mCanceled;
+ protected int mFinishingRecentsAnimationForNewTaskId = -1;
+
+ protected BaseSwipeUpHandler(Context context,
+ OverviewComponentObserver overviewComponentObserver,
+ RecentsModel recentsModel, InputConsumerController inputConsumer, int runningTaskId) {
+ mContext = context;
+ mOverviewComponentObserver = overviewComponentObserver;
+ mActivityControlHelper = overviewComponentObserver.getActivityControlHelper();
+ mRecentsModel = recentsModel;
+ mActivityInitListener =
+ mActivityControlHelper.createActivityInitListener(this::onActivityInit);
+ mRunningTaskId = runningTaskId;
+ mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
+ this::createNewInputProxyHandler);
+ mMode = SysUINavigationMode.getMode(context);
+
+ mClipAnimationHelper = new ClipAnimationHelper(context);
+ mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+ mVibrator = context.getSystemService(Vibrator.class);
+ initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
+ .getDeviceProfile(mContext));
+ }
+
+ protected void setStateOnUiThread(int stateFlag) {
+ if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
+ mStateCallback.setState(stateFlag);
+ } else {
+ postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
+ }
+ }
+
+ protected void performHapticFeedback() {
+ if (!mVibrator.hasVibrator()) {
+ return;
+ }
+ if (Settings.System.getInt(
+ mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) {
+ return;
+ }
+
+ VibrationEffect effect = createPredefined(EFFECT_CLICK);
+ if (effect == null) {
+ return;
+ }
+ BACKGROUND_EXECUTOR.execute(() -> mVibrator.vibrate(effect));
+ }
+
+ public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
+ return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null;
+ }
+
+ @UiThread
+ public void updateDisplacement(float displacement) {
+ // We are moving in the negative x/y direction
+ displacement = -displacement;
+ float shift;
+ if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
+ shift = mDragLengthFactor;
+ } else {
+ float translation = Math.max(displacement, 0);
+ shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
+ if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
+ float pullbackProgress = Utilities.getProgress(shift,
+ DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
+ pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
+ shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
+ * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
+ }
+ }
+
+ mCurrentShift.updateValue(shift);
+ }
+
+ public void setGestureEndCallback(Runnable gestureEndCallback) {
+ mGestureEndCallback = gestureEndCallback;
+ }
+
+ public abstract Intent getLaunchIntent();
+
+ protected void linkRecentsViewScroll() {
+ SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
+ mTransformParams.setSyncTransactionApplier(applier);
+ mRecentsAnimationWrapper.runOnInit(() ->
+ mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
+ });
+
+ mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ if (moveWindowWithRecentsScroll()) {
+ updateFinalShift();
+ }
+ });
+ mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
+ mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+ }
+
+ protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
+ // Launch the task user scrolled to (mRecentsView.getNextPage()).
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // We finish recents animation inside launchTask() when live tile is enabled.
+ mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
+ true /* freezeTaskList */);
+ } else {
+ int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
+ mFinishingRecentsAnimationForNewTaskId = taskId;
+ mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
+ if (!mCanceled) {
+ TaskView nextTask = mRecentsView.getTaskView(taskId);
+ if (nextTask != null) {
+ nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
+ success -> {
+ resultCallback.accept(success);
+ if (!success) {
+ mActivityControlHelper.onLaunchTaskFailed(mActivity);
+ nextTask.notifyTaskLaunchFailed(TAG);
+ } else {
+ mActivityControlHelper.onLaunchTaskSuccess(mActivity);
+ }
+ }, mMainThreadHandler);
+ }
+ setStateOnUiThread(successStateFlag);
+ }
+ mCanceled = false;
+ mFinishingRecentsAnimationForNewTaskId = -1;
+ });
+ }
+ TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+ }
+
+ @Override
+ public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
+ DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
+ final Rect overviewStackBounds;
+ RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
+
+ if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) {
+ overviewStackBounds = mActivityControlHelper
+ .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget);
+ dp = dp.getMultiWindowProfile(mContext, new Point(
+ overviewStackBounds.width(), overviewStackBounds.height()));
+ } else {
+ // If we are not in multi-window mode, home insets should be same as system insets.
+ dp = dp.copy(mContext);
+ overviewStackBounds = getStackBounds(dp);
+ }
+ dp.updateInsets(targetSet.homeContentInsets);
+ dp.updateIsSeascape(mContext.getSystemService(WindowManager.class));
+ if (runningTaskTarget != null) {
+ mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
+ }
+
+ mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */);
+ initTransitionEndpoints(dp);
+
+ mRecentsAnimationWrapper.setController(targetSet);
+ }
+
+ private Rect getStackBounds(DeviceProfile dp) {
+ if (mActivity != null) {
+ int loc[] = new int[2];
+ View rootView = mActivity.getRootView();
+ rootView.getLocationOnScreen(loc);
+ return new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
+ loc[1] + rootView.getHeight());
+ } else {
+ return new Rect(0, 0, dp.widthPx, dp.heightPx);
+ }
+ }
+
+ protected void initTransitionEndpoints(DeviceProfile dp) {
+ mDp = dp;
+
+ mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
+ dp, mContext, TEMP_RECT);
+ if (!dp.isMultiWindowMode) {
+ // When updating the target rect, also update the home bounds since the location on
+ // screen of the launcher window may be stale (position is not updated until first
+ // traversal after the window is resized). We only do this for non-multiwindow because
+ // we otherwise use the minimized home bounds provided by the system.
+ mClipAnimationHelper.updateHomeBounds(getStackBounds(dp));
+ }
+ mClipAnimationHelper.updateTargetRect(TEMP_RECT);
+ if (mMode == Mode.NO_BUTTON) {
+ // We can drag all the way to the top of the screen.
+ mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
+ }
+ }
+
+ /**
+ * Return true if the window should be translated horizontally if the recents view scrolls
+ */
+ protected abstract boolean moveWindowWithRecentsScroll();
+
+ protected abstract boolean onActivityInit(final T activity, Boolean alreadyOnHome);
+
+ /**
+ * Called to create a input proxy for the running task
+ */
+ @UiThread
+ protected abstract InputConsumer createNewInputProxyHandler();
+
+ /**
+ * Called when the value of {@link #mCurrentShift} changes
+ */
+ @UiThread
+ public abstract void updateFinalShift();
+
+ /**
+ * Called when motion pause is detected
+ */
+ public abstract void onMotionPauseChanged(boolean isPaused);
+
+ @UiThread
+ public void onGestureStarted() { }
+
+ @UiThread
+ public abstract void onGestureCancelled();
+
+ @UiThread
+ public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
+
+ public abstract void onConsumerAboutToBeSwitched(SwipeSharedState sharedState);
+
+ public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
+
+ public void initWhenReady() {
+ // Preload the plan
+ mRecentsModel.getTasks(null);
+
+ mActivityInitListener.register();
+ }
+
+ /**
+ * Applies the transform on the recents animation without any additional null checks
+ */
+ protected void applyTransformUnchecked() {
+ float shift = mCurrentShift.value;
+ float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset();
+ float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
+ mClipAnimationHelper.getTargetRect().width());
+ mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
+ mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
+ mTransformParams);
+ }
+
+ private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
+ float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 + mPageSpacing;
+ float interpolation = Math.min(1, offsetX / distanceToReachEdge);
+ return TaskView.getCurveScaleForInterpolation(interpolation);
+ }
+
+ /**
+ * Creates an animation that transforms the current app window into the home app.
+ * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
+ * @param homeAnimationFactory The home animation factory.
+ */
+ protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
+ HomeAnimationFactory homeAnimationFactory) {
+ final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
+ final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
+ mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
+ final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
+
+ final View floatingView = homeAnimationFactory.getFloatingView();
+ final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
+ RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext.getResources());
+ if (isFloatingIconView) {
+ FloatingIconView fiv = (FloatingIconView) floatingView;
+ anim.addAnimatorListener(fiv);
+ fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
+ }
+
+ 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(new RectFSpringAnim.OnUpdateListener() {
+
+ // Alpha interpolates between [1, 0] between progress values [start, end]
+ final float start = 0f;
+ final float end = 0.85f;
+
+ private float getWindowAlpha(float progress) {
+ if (progress <= start) {
+ return 1f;
+ }
+ if (progress >= end) {
+ return 0f;
+ }
+ return Utilities.mapToRange(progress, start, end, 1, 0, ACCEL_1_5);
+ }
+
+ @Override
+ public void onUpdate(RectF currentRect, float progress) {
+ homeAnim.setPlayFraction(progress);
+
+ mTransformParams.setProgress(progress)
+ .setCurrentRectAndTargetAlpha(currentRect, getWindowAlpha(progress));
+ if (isFloatingIconView) {
+ mTransformParams.setCornerRadius(endRadius * progress + startRadius
+ * (1f - progress));
+ }
+ mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
+ false /* launcherOnTop */);
+
+ if (isFloatingIconView) {
+ ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
+ windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(),
+ false);
+ }
+ }
+
+ @Override
+ public void onCancel() {
+ if (isFloatingIconView) {
+ ((FloatingIconView) floatingView).fastFinish();
+ }
+ }
+ });
+ anim.addAnimatorListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ homeAnim.dispatchOnStart();
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ homeAnim.getAnimationPlayer().end();
+ }
+ });
+ return anim;
+ }
+
+ public interface Factory {
+
+ BaseSwipeUpHandler newHandler(RunningTaskInfo runningTask,
+ long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask);
+ }
+
+ protected interface RunningWindowAnim {
+ void end();
+
+ void cancel();
+
+ static RunningWindowAnim wrap(Animator animator) {
+ return new RunningWindowAnim() {
+ @Override
+ public void end() {
+ animator.end();
+ }
+
+ @Override
+ public void cancel() {
+ animator.cancel();
+ }
+ };
+ }
+
+ static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
+ return new RunningWindowAnim() {
+ @Override
+ public void end() {
+ rectFSpringAnim.end();
+ }
+
+ @Override
+ public void cancel() {
+ rectFSpringAnim.cancel();
+ }
+ };
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index c43155b..8c5a788 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -80,7 +80,9 @@
@Override
public void onAssistantVisibilityChanged(float visibility) {
- // TODO:
+ // This class becomes active when the screen is locked.
+ // Rather than having it handle assistant visibility changes, the assistant visibility is
+ // set to zero prior to this class becoming active.
}
@NonNull
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index b2a71a4..295585e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -151,16 +151,10 @@
@NonNull
@Override
public RectF getWindowTargetRect() {
- final int halfIconSize = dp.iconSizePx / 2;
- final float targetCenterX = dp.availableWidthPx / 2f;
- final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
-
if (canUseWorkspaceView) {
return iconLocation;
} else {
- // Fallback to animate to center of screen.
- return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
- targetCenterX + halfIconSize, targetCenterY + halfIconSize);
+ return HomeAnimationFactory.getDefaultWindowTargetRect(dp);
}
}
@@ -194,9 +188,6 @@
@Override
public AnimationFactory prepareRecentsUI(Launcher activity, boolean activityVisible,
boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "prepareRecentsUI");
- }
final LauncherState startState = activity.getStateManager().getState();
LauncherState resetState = startState;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
new file mode 100644
index 0000000..4eb9df2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -0,0 +1,83 @@
+package com.android.quickstep;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.testing.TestInformationHandler;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.RecentsView;
+
+import java.util.concurrent.ExecutionException;
+
+public class QuickstepTestInformationHandler extends TestInformationHandler {
+
+ public QuickstepTestInformationHandler(Context context) {
+ }
+
+ @Override
+ public Bundle call(String method) {
+ final Bundle response = new Bundle();
+ switch (method) {
+ case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
+ final float swipeHeight =
+ OverviewState.getDefaultSwipeHeight(mDeviceProfile);
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
+ final float swipeHeight =
+ LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile);
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ TouchInteractionService.isInitialized());
+ return response;
+ }
+
+ case TestProtocol.REQUEST_HOTSEAT_TOP: {
+ if (mLauncher == null) return null;
+
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ PortraitStatesTouchController.getHotseatTop(mLauncher));
+ return response;
+ }
+
+ case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: {
+ try {
+ final int leftMargin = new MainThreadExecutor().submit(() ->
+ mLauncher.<RecentsView>getOverviewPanel().getLeftGestureMargin()).get();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin);
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return response;
+ }
+
+ case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: {
+ try {
+ final int rightMargin = new MainThreadExecutor().submit(() ->
+ mLauncher.<RecentsView>getOverviewPanel().getRightGestureMargin()).
+ get();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin);
+ } catch (ExecutionException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return response;
+ }
+ }
+
+ return super.call(method);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index fc29a56..f08ae4a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -28,8 +28,10 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.app.ActivityOptions;
+import android.content.Intent;
import android.content.res.Configuration;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.view.View;
@@ -42,7 +44,9 @@
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.fallback.RecentsRootView;
import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.ObjectWrapper;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -54,6 +58,9 @@
*/
public final class RecentsActivity extends BaseRecentsActivity {
+ public static final String EXTRA_THUMBNAIL = "thumbnailData";
+ public static final String EXTRA_TASK_ID = "taskID";
+
private Handler mUiHandler = new Handler(Looper.getMainLooper());
private RecentsRootView mRecentsRootView;
private FallbackRecentsView mFallbackRecentsView;
@@ -79,6 +86,22 @@
}
@Override
+ protected void onNewIntent(Intent intent) {
+ if (intent.getExtras() != null) {
+ int taskID = intent.getIntExtra(EXTRA_TASK_ID, 0);
+ IBinder thumbnail = intent.getExtras().getBinder(EXTRA_THUMBNAIL);
+ if (taskID != 0 && thumbnail instanceof ObjectWrapper) {
+ ThumbnailData thumbnailData = ((ObjectWrapper<ThumbnailData>) thumbnail).get();
+ mFallbackRecentsView.showCurrentTask(taskID);
+ mFallbackRecentsView.updateThumbnail(taskID, thumbnailData);
+ }
+ }
+ intent.removeExtra(EXTRA_TASK_ID);
+ intent.removeExtra(EXTRA_THUMBNAIL);
+ super.onNewIntent(intent);
+ }
+
+ @Override
protected void onHandleConfigChanged() {
super.onHandleConfigChanged();
mRecentsRootView.setup();
@@ -132,7 +155,7 @@
mFallbackRecentsView.resetViewUI();
}
});
- result.setAnimation(anim);
+ result.setAnimation(anim, RecentsActivity.this);
}
};
return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
@@ -178,6 +201,12 @@
mFallbackRecentsView.resetTaskVisuals();
}
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mFallbackRecentsView.reset();
+ }
+
public void onTaskLaunched() {
mFallbackRecentsView.resetTaskVisuals();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
index ddd28a3..e51ba63 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -19,12 +19,12 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.Utilities.FLAG_NO_GESTURES;
-
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.annotation.UiThread;
+
import com.android.launcher3.util.Preconditions;
import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.util.SwipeAnimationTargetSet;
@@ -33,8 +33,6 @@
import java.util.ArrayList;
import java.util.function.Supplier;
-import androidx.annotation.UiThread;
-
/**
* Wrapper around RecentsAnimationController to help with some synchronization
*/
@@ -184,18 +182,15 @@
}
}
if (mInputConsumer != null) {
- int flags = ev.getEdgeFlags();
- ev.setEdgeFlags(flags | FLAG_NO_GESTURES);
mInputConsumer.onMotionEvent(ev);
- ev.setEdgeFlags(flags);
}
return true;
}
- public void setCancelWithDeferredScreenshot(boolean deferredWithScreenshot) {
+ public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
if (targetSet != null) {
- targetSet.controller.setCancelWithDeferredScreenshot(deferredWithScreenshot);
+ targetSet.controller.setDeferCancelUntilNextTransition(defer, screenshot);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
index 213c5d3..fd45923 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -207,8 +207,7 @@
}
};
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
- future, animStartedListener, mHandler, true /* scaleUp */,
- v.getDisplay().getDisplayId());
+ future, animStartedListener, mHandler, true /* scaleUp */, displayId);
}
});
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 53da0f9..86ba855 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -31,18 +31,22 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
+import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
@@ -65,6 +69,7 @@
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.MainThreadExecutor;
@@ -74,6 +79,9 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -96,8 +104,10 @@
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.RecentsAnimationListener;
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
+import com.android.systemui.shared.system.TaskInfoCompat;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -119,10 +129,6 @@
public String nextArg() {
return pollFirst().toLowerCase();
}
-
- public String nextArgExact() {
- return pollFirst();
- }
}
/**
@@ -141,6 +147,11 @@
private static final String TAG = "TouchInteractionService";
+ private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount";
+ private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
+ private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
+ private int mBackGestureNotificationCounter = -1;
+
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
public void onActiveNavBarRegionChanges(Region region) {
@@ -152,6 +163,8 @@
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet);
+ MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */));
+ sIsInitialized = true;
}
@Override
@@ -199,6 +212,10 @@
mOverviewComponentObserver.getActivityControlHelper();
UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
isButton, gestureSwipeLeft, activityControl.getContainerType());
+
+ if (completed && !isButton && shouldNotifyBackGesture()) {
+ BACKGROUND_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
+ }
}
public void onSystemUiStateChanged(int stateFlags) {
@@ -225,12 +242,17 @@
};
private static boolean sConnected = false;
+ private static boolean sIsInitialized = false;
private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState();
public static boolean isConnected() {
return sConnected;
}
+ public static boolean isInitialized() {
+ return sIsInitialized;
+ }
+
public static SwipeSharedState getSwipeSharedState() {
return sSwipeSharedState;
}
@@ -238,6 +260,11 @@
private final InputConsumer mResetGestureInputConsumer =
new ResetGestureInputConsumer(sSwipeSharedState);
+ private final BaseSwipeUpHandler.Factory mWindowTreansformFactory =
+ this::createWindowTransformSwipeHandler;
+ private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory =
+ this::createFallbackNoButtonSwipeHandler;
+
private ActivityManagerWrapper mAM;
private RecentsModel mRecentsModel;
private ISystemUiProxy mISystemUiProxy;
@@ -319,6 +346,9 @@
if (mInputEventReceiver != null) {
mInputEventReceiver.dispose();
mInputEventReceiver = null;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "disposeEventHandlers");
+ }
}
if (mInputMonitorCompat != null) {
mInputMonitorCompat.dispose();
@@ -327,16 +357,25 @@
}
private void initInputMonitor() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 1");
+ }
if (!mMode.hasGestures || mISystemUiProxy == null) {
return;
}
disposeEventHandlers();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 2");
+ }
try {
mInputMonitorCompat = InputMonitorCompat.fromBundle(mISystemUiProxy
.monitorGestureInput("swipe-up", mDefaultDisplayId), KEY_EXTRA_INPUT_MONITOR);
mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
mMainChoreographer, this::onInputEvent);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "initInputMonitor 3");
+ }
} catch (RemoteException e) {
Log.e(TAG, "Unable to create input monitor", e);
}
@@ -394,6 +433,9 @@
@Override
public void onNavigationModeChanged(Mode newMode) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onNavigationModeChanged " + newMode);
+ }
if (mMode.hasGestures != newMode.hasGestures) {
if (newMode.hasGestures) {
getSystemService(DisplayManager.class).registerDisplayListener(
@@ -448,6 +490,8 @@
// Temporarily disable model preload
// new ModelPreload().start(this);
+ mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this)
+ .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT));
Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver);
}
@@ -478,6 +522,7 @@
@Override
public void onDestroy() {
+ sIsInitialized = false;
if (mIsUserUnlocked) {
mInputConsumer.unregisterInputConsumer();
mOverviewComponentObserver.onDestroy();
@@ -502,6 +547,9 @@
}
private void onInputEvent(InputEvent ev) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_BACKGROUND_TO_OVERVIEW_TAG, "onInputEvent " + ev);
+ }
if (!(ev instanceof MotionEvent)) {
Log.e(TAG, "Unknown event " + ev);
return;
@@ -534,6 +582,7 @@
private boolean validSystemUiFlags() {
return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
+ && (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
&& ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
|| (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
}
@@ -553,7 +602,7 @@
if (isInValidSystemUiState) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps
// launched while device is locked even after exiting direct boot mode (e.g. camera).
- return createDeviceLockedInputConsumer(mAM.getRunningTask(0));
+ return createDeviceLockedInputConsumer(mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT));
} else {
return mResetGestureInputConsumer;
}
@@ -591,7 +640,7 @@
}
private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) {
- final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+ RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
if (!useSharedState) {
sSwipeSharedState.clearAllState(false /* finishAnimation */);
}
@@ -603,6 +652,19 @@
final ActivityControlHelper activityControl =
mOverviewComponentObserver.getActivityControlHelper();
+ boolean forceOverviewInputConsumer = false;
+ if (isExcludedAssistant(runningTaskInfo)) {
+ // In the case where we are in the excluded assistant state, ignore it and treat the
+ // running activity as the task behind the assistant
+ runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT);
+ if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) {
+ final ComponentName homeComponent =
+ mOverviewComponentObserver.getHomeIntent().getComponent();
+ forceOverviewInputConsumer =
+ runningTaskInfo.baseIntent.getComponent().equals(homeComponent);
+ }
+ }
+
if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher
&& !sSwipeSharedState.recentsAnimationFinishInterrupted) {
return mResetGestureInputConsumer;
@@ -612,22 +674,25 @@
RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.id = sSwipeSharedState.nextRunningTaskId;
return createOtherActivityInputConsumer(event, info);
- } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
+ } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()
+ || forceOverviewInputConsumer) {
return createOverviewInputConsumer(event);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) {
return createOverviewInputConsumer(event);
} else if (mGestureBlockingActivity != null && runningTaskInfo != null
&& mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
return mResetGestureInputConsumer;
- } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
- return new FallbackNoButtonInputConsumer(this, activityControl,
- mInputMonitorCompat, sSwipeSharedState, mSwipeTouchRegion,
- mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo);
} else {
return createOtherActivityInputConsumer(event, runningTaskInfo);
}
}
+ private boolean isExcludedAssistant(TaskInfo info) {
+ return info != null
+ && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT
+ && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
+ }
+
private boolean disableHorizontalSwipe(MotionEvent event) {
// mExclusionRegion can change on binder thread, use a local instance here.
Region exclusionRegion = mExclusionRegion;
@@ -635,17 +700,25 @@
&& exclusionRegion.contains((int) event.getX(), (int) event.getY());
}
- private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
+ private InputConsumer createOtherActivityInputConsumer(MotionEvent event,
RunningTaskInfo runningTaskInfo) {
- final ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
- return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
- mOverviewComponentObserver.getOverviewIntent(), activityControl,
- shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
+ final boolean shouldDefer;
+ final BaseSwipeUpHandler.Factory factory;
+
+ if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ shouldDefer = !sSwipeSharedState.recentsAnimationFinishInterrupted;
+ factory = mFallbackNoButtonFactory;
+ } else {
+ shouldDefer = mOverviewComponentObserver.getActivityControlHelper()
+ .deferStartingActivity(mActiveNavBarRegion, event);
+ factory = mWindowTreansformFactory;
+ }
+
+ return new OtherActivityInputConsumer(this, runningTaskInfo,
+ shouldDefer, mOverviewCallbacks, this::onConsumerInactive,
sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion,
- disableHorizontalSwipe(event));
+ disableHorizontalSwipe(event), factory);
}
private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) {
@@ -669,7 +742,7 @@
return new OverviewInputConsumer(activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
- return new OverviewWithoutFocusInputConsumer(this, mInputMonitorCompat,
+ return new OverviewWithoutFocusInputConsumer(activity, mInputMonitorCompat,
disableHorizontalSwipe(event));
}
}
@@ -684,6 +757,60 @@
}
}
+ private void preloadOverview(boolean fromInit) {
+ if (!mIsUserUnlocked) {
+ return;
+ }
+ if (!mMode.hasGestures && !mOverviewComponentObserver.isHomeAndOverviewSame()) {
+ // Prevent the overview from being started before the real home on first boot.
+ return;
+ }
+
+ if (RestoreDbTask.isPending(this)) {
+ // Preloading while a restore is pending may cause launcher to start the restore
+ // too early.
+ return;
+ }
+
+ final ActivityControlHelper<BaseDraggingActivity> activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
+ if (activityControl.getCreatedActivity() == null) {
+ // Make sure that UI states will be initialized.
+ activityControl.createActivityInitListener((activity, wasVisible) -> {
+ AppLaunchTracker.INSTANCE.get(activity);
+ return false;
+ }).register();
+ } else if (fromInit) {
+ // The activity has been created before the initialization of overview service. It is
+ // usually happens when booting or launcher is the top activity, so we should already
+ // have the latest state.
+ return;
+ }
+
+ // Pass null animation handler to indicate this start is preload.
+ startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), null);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (!mIsUserUnlocked) {
+ return;
+ }
+ final ActivityControlHelper activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
+ final BaseDraggingActivity activity = activityControl.getCreatedActivity();
+ if (activity == null || activity.isStarted()) {
+ // We only care about the existing background activity.
+ return;
+ }
+ if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
+ activity.getResources().getConfiguration().diff(newConfig))) {
+ return;
+ }
+
+ preloadOverview(false /* fromInit */);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) {
@@ -708,8 +835,9 @@
pw.println(" assistantAvailable=" + mAssistantAvailable);
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
- pw.println(" resumed="
- + mOverviewComponentObserver.getActivityControlHelper().isResumed());
+ boolean resumed = mOverviewComponentObserver != null
+ && mOverviewComponentObserver.getActivityControlHelper().isResumed();
+ pw.println(" resumed=" + resumed);
pw.println(" useSharedState=" + mConsumer.useSharedSwipeState());
if (mConsumer.useSharedSwipeState()) {
sSwipeSharedState.dump(" ", pw);
@@ -739,4 +867,37 @@
break;
}
}
+
+ private BaseSwipeUpHandler createWindowTransformSwipeHandler(RunningTaskInfo runningTask,
+ long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
+ return new WindowTransformSwipeHandler(runningTask, this, touchTimeMs,
+ mOverviewComponentObserver, continuingLastGesture, mInputConsumer, mRecentsModel);
+ }
+
+ private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(RunningTaskInfo runningTask,
+ long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
+ return new FallbackNoButtonInputConsumer(this, mOverviewComponentObserver, runningTask,
+ mRecentsModel, mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
+ }
+
+ protected boolean shouldNotifyBackGesture() {
+ return mBackGestureNotificationCounter > 0 &&
+ mGestureBlockingActivity != null;
+ }
+
+ @WorkerThread
+ protected void tryNotifyBackGesture() {
+ if (shouldNotifyBackGesture()) {
+ mBackGestureNotificationCounter--;
+ Utilities.getDevicePrefs(this).edit()
+ .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
+ sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
+ mGestureBlockingActivity.getPackageName()));
+ }
+ }
+
+ public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) {
+ BACKGROUND_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, null, listener, null, null));
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index af18bbf..0d29e5d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -17,22 +17,18 @@
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.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
@@ -43,65 +39,49 @@
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;
+import android.content.Intent;
import android.graphics.Canvas;
-import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.os.SystemClock;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets;
-import android.view.WindowManager;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.views.FloatingIconView;
-import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
-import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -109,19 +89,14 @@
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
import com.android.systemui.shared.system.WindowCallbacksCompat;
-import java.util.function.BiFunction;
-import java.util.function.Consumer;
-
@TargetApi(Build.VERSION_CODES.O)
public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
- implements SwipeAnimationListener, OnApplyWindowInsetsListener {
+ extends BaseSwipeUpHandler<T, RecentsView>
+ implements 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) {
@@ -220,64 +195,30 @@
private static final long SHELF_ANIM_DURATION = 240;
public static final long RECENTS_ATTACH_DURATION = 300;
- // Start resisting when swiping past this factor of mTransitionDragLength.
- private static final float DRAG_LENGTH_FACTOR_START_PULLBACK = 1.4f;
- // This is how far down we can scale down, where 0f is full screen and 1f is recents.
- private static final float DRAG_LENGTH_FACTOR_MAX_PULLBACK = 1.8f;
- private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL;
-
/**
* Used as the page index for logging when we return to the last task at the end of the gesture.
*/
private static final int LOG_NO_OP_PAGE_INDEX = -1;
- private final ClipAnimationHelper mClipAnimationHelper;
- private final ClipAnimationHelper.TransformParams mTransformParams;
-
- private Runnable mGestureEndCallback;
private GestureEndTarget mGestureEndTarget;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
private boolean mIsShelfPeeking;
- private DeviceProfile mDp;
- // The distance needed to drag to reach the task size in recents.
- private int mTransitionDragLength;
- // How much further we can drag past recents, as a factor of mTransitionDragLength.
- private float mDragLengthFactor = 1;
- // Shift in the range of [0, 1].
- // 0 => preview snapShot is completely visible, and hotseat is completely translated down
- // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
- // visible.
- private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
private boolean mContinuingLastGesture;
// To avoid UI jump when gesture is started, we offset the animation by the threshold.
private float mShiftAtGestureStart = 0;
- private final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler();
-
- private final Context mContext;
- private final ActivityControlHelper<T> mActivityControlHelper;
- private final ActivityInitListener mActivityInitListener;
-
- private final SysUINavigationMode.Mode mMode;
-
- private final int mRunningTaskId;
private ThumbnailData mTaskSnapshot;
- private MultiStateCallback mStateCallback;
// Used to control launcher components throughout the swipe gesture.
private AnimatorPlaybackController mLauncherTransitionController;
private boolean mHasLauncherTransitionControllerStarted;
- private T mActivity;
- private RecentsView mRecentsView;
private AnimationFactory mAnimationFactory = (t) -> { };
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
- private boolean mCanceled;
private boolean mWasLauncherAlreadyVisible;
- private int mFinishingRecentsAnimationForNewTaskId = -1;
private boolean mPassedOverviewThreshold;
private boolean mGestureStarted;
@@ -286,31 +227,17 @@
private PointF mDownPos;
private boolean mIsLikelyToStartNewTask;
- private final RecentsAnimationWrapper mRecentsAnimationWrapper;
-
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
public WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
- long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
- InputConsumerController inputConsumer) {
- mContext = context;
- mRunningTaskId = runningTaskInfo.id;
+ long touchTimeMs, OverviewComponentObserver overviewComponentObserver,
+ boolean continuingLastGesture,
+ InputConsumerController inputConsumer, RecentsModel recentsModel) {
+ super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
mTouchTimeMs = touchTimeMs;
- mActivityControlHelper = controller;
- mActivityInitListener = mActivityControlHelper
- .createActivityInitListener(this::onActivityInit);
mContinuingLastGesture = continuingLastGesture;
- mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
- this::createNewInputProxyHandler);
- mClipAnimationHelper = new ClipAnimationHelper(context);
- mTransformParams = new ClipAnimationHelper.TransformParams();
-
- mMode = SysUINavigationMode.getMode(context);
initStateCallbacks();
-
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
- initTransitionEndpoints(dp);
}
private void initStateCallbacks() {
@@ -373,44 +300,8 @@
}
}
- private void setStateOnUiThread(int stateFlag) {
- if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
- mStateCallback.setState(stateFlag);
- } else {
- postAsyncCallback(mMainThreadHandler, () -> mStateCallback.setState(stateFlag));
- }
- }
-
- private void initTransitionEndpoints(DeviceProfile dp) {
- mDp = dp;
-
- Rect tempRect = new Rect();
- mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
- dp, mContext, tempRect);
- mClipAnimationHelper.updateTargetRect(tempRect);
- if (mMode == Mode.NO_BUTTON) {
- // We can drag all the way to the top of the screen.
- mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
- }
- }
-
- private long getFadeInDuration() {
- if (mCurrentShift.getCurrentAnimation() != null) {
- ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
- long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
-
- // TODO: Find a better heuristic
- return Math.min(MAX_SWIPE_DURATION, Math.max(theirDuration, MIN_SWIPE_DURATION));
- } else {
- return MAX_SWIPE_DURATION;
- }
- }
-
- public void initWhenReady() {
- mActivityInitListener.register();
- }
-
- private boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
+ @Override
+ protected boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
if (mActivity == activity) {
return true;
}
@@ -431,19 +322,7 @@
}
mRecentsView = activity.getOverviewPanel();
- SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
- mTransformParams.setSyncTransactionApplier(applier);
- mRecentsAnimationWrapper.runOnInit(() ->
- mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
- });
-
- mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
- if (mGestureEndTarget != HOME) {
- updateFinalShift();
- }
- });
- mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
- mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
+ linkRecentsViewScroll();
mRecentsView.setLiveTileOverlay(mLiveTileOverlay);
mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
@@ -458,33 +337,23 @@
return true;
}
+ @Override
+ protected boolean moveWindowWithRecentsScroll() {
+ return mGestureEndTarget != HOME;
+ }
+
private void onLauncherStart(final T activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart");
- }
if (mActivity != activity) {
return;
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 1");
- }
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return;
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 2");
- }
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
if (mGestureEndTarget != HOME) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 3");
- }
Runnable initAnimFactory = () -> {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 4");
- }
mAnimationFactory = mActivityControlHelper.prepareRecentsUI(mActivity,
mWasLauncherAlreadyVisible, true,
this::onAnimatorPlaybackControllerCreated);
@@ -494,18 +363,13 @@
// Launcher is visible, but might be about to stop. Thus, if we prepare recents
// now, it might get overridden by moveToRestState() in onStop(). To avoid this,
// wait until the next gesture (and possibly launcher) starts.
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 5");
- }
mStateCallback.addCallback(STATE_GESTURE_STARTED, initAnimFactory);
} else {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "onLauncherStart 6");
- }
initAnimFactory.run();
}
}
- AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible);
+ AbstractFloatingView.closeAllOpenViewsExcept(activity, mWasLauncherAlreadyVisible,
+ AbstractFloatingView.TYPE_LISTENER);
if (mWasLauncherAlreadyVisible) {
mStateCallback.setState(STATE_LAUNCHER_DRAWN);
@@ -576,30 +440,7 @@
return TaskView.getCurveScaleForInterpolation(interpolation);
}
- public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
- return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null;
- }
-
- @UiThread
- public void updateDisplacement(float displacement) {
- // We are moving in the negative x/y direction
- displacement = -displacement;
- if (displacement > mTransitionDragLength * mDragLengthFactor && mTransitionDragLength > 0) {
- mCurrentShift.updateValue(mDragLengthFactor);
- } else {
- float translation = Math.max(displacement, 0);
- float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
- if (shift > DRAG_LENGTH_FACTOR_START_PULLBACK) {
- float pullbackProgress = Utilities.getProgress(shift,
- DRAG_LENGTH_FACTOR_START_PULLBACK, mDragLengthFactor);
- pullbackProgress = PULLBACK_INTERPOLATOR.getInterpolation(pullbackProgress);
- shift = DRAG_LENGTH_FACTOR_START_PULLBACK + pullbackProgress
- * (DRAG_LENGTH_FACTOR_MAX_PULLBACK - DRAG_LENGTH_FACTOR_START_PULLBACK);
- }
- mCurrentShift.updateValue(shift);
- }
- }
-
+ @Override
public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION);
}
@@ -650,6 +491,7 @@
mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
}
+ @Override
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
if (mIsLikelyToStartNewTask != isLikelyToStartNewTask) {
mIsLikelyToStartNewTask = isLikelyToStartNewTask;
@@ -665,9 +507,8 @@
if (mIsShelfPeeking != wasShelfPeeking) {
maybeUpdateRecentsAttachedState();
}
- if (mRecentsView != null && shelfState.shouldPreformHaptic) {
- mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (shelfState.shouldPreformHaptic) {
+ performHapticFeedback();
}
}
@@ -696,19 +537,18 @@
updateLauncherTransitionProgress();
}
- @UiThread
- private void updateFinalShift() {
- float shift = mCurrentShift.value;
+ @Override
+ public Intent getLaunchIntent() {
+ return mOverviewComponentObserver.getOverviewIntent();
+ }
+
+ @Override
+ public void updateFinalShift() {
SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
if (controller != null) {
- float offsetX = mRecentsView == null ? 0 : mRecentsView.getScrollOffset();
- float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
- mClipAnimationHelper.getTargetRect().width());
- mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
- mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
- mTransformParams);
- updateSysUiFlags(shift);
+ applyTransformUnchecked();
+ updateSysUiFlags(mCurrentShift.value);
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
@@ -721,9 +561,8 @@
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
- if (mRecentsView != null && mMode != Mode.NO_BUTTON) {
- mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (mMode != Mode.NO_BUTTON) {
+ performHapticFeedback();
}
}
@@ -766,39 +605,7 @@
@Override
public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
- DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
- final Rect overviewStackBounds;
- RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
-
- if (targetSet.minimizedHomeBounds != null && runningTaskTarget != null) {
- overviewStackBounds = mActivityControlHelper
- .getOverviewWindowBounds(targetSet.minimizedHomeBounds, runningTaskTarget);
- dp = dp.getMultiWindowProfile(mContext, new Point(
- targetSet.minimizedHomeBounds.width(), targetSet.minimizedHomeBounds.height()));
- dp.updateInsets(targetSet.homeContentInsets);
- } else {
- if (mActivity != null) {
- int loc[] = new int[2];
- View rootView = mActivity.getRootView();
- rootView.getLocationOnScreen(loc);
- overviewStackBounds = new Rect(loc[0], loc[1], loc[0] + rootView.getWidth(),
- loc[1] + rootView.getHeight());
- } else {
- overviewStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
- }
- // If we are not in multi-window mode, home insets should be same as system insets.
- dp = dp.copy(mContext);
- dp.updateInsets(targetSet.homeContentInsets);
- }
- dp.updateIsSeascape(mContext.getSystemService(WindowManager.class));
-
- if (runningTaskTarget != null) {
- mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
- }
- mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */);
- initTransitionEndpoints(dp);
-
- mRecentsAnimationWrapper.setController(targetSet);
+ super.onRecentsAnimationStart(targetSet);
TOUCH_INTERACTION_LOG.addLog("startRecentsAnimationCallback", targetSet.apps.length);
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
@@ -813,7 +620,7 @@
TOUCH_INTERACTION_LOG.addLog("cancelRecentsAnimation");
}
- @UiThread
+ @Override
public void onGestureStarted() {
notifyGestureStartedAsync();
mShiftAtGestureStart = mCurrentShift.value;
@@ -837,7 +644,7 @@
/**
* Called as a result on ACTION_CANCEL to return the UI to the start state.
*/
- @UiThread
+ @Override
public void onGestureCancelled() {
updateDisplacement(0);
setStateOnUiThread(STATE_GESTURE_COMPLETED);
@@ -850,7 +657,7 @@
* @param velocity The x and y components of the velocity when the gesture ends.
* @param downPos The x and y value of where the gesture started.
*/
- @UiThread
+ @Override
public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
@@ -868,9 +675,9 @@
handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
- @UiThread
- private InputConsumer createNewInputProxyHandler() {
- endRunningWindowAnim();
+ @Override
+ protected InputConsumer createNewInputProxyHandler() {
+ endRunningWindowAnim(mGestureEndTarget == HOME /* cancel */);
endLauncherTransitionController();
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// Hide the task view, if not already hidden
@@ -882,9 +689,13 @@
? InputConsumer.NO_OP : new OverviewInputConsumer(activity, null, true);
}
- private void endRunningWindowAnim() {
+ private void endRunningWindowAnim(boolean cancel) {
if (mRunningWindowAnim != null) {
- mRunningWindowAnim.end();
+ if (cancel) {
+ mRunningWindowAnim.cancel();
+ } else {
+ mRunningWindowAnim.end();
+ }
}
}
@@ -925,18 +736,19 @@
: LAST_TASK;
}
} else {
- if (mMode == Mode.NO_BUTTON && endVelocity < 0 && !mIsShelfPeeking) {
+ // If swiping at a diagonal, base end target on the faster velocity.
+ boolean isSwipeUp = endVelocity < 0;
+ boolean willGoToNewTaskOnSwipeUp =
+ goingToNewTask && Math.abs(velocity.x) > Math.abs(endVelocity);
+
+ if (mMode == Mode.NO_BUTTON && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
+ endTarget = HOME;
+ } else if (mMode == Mode.NO_BUTTON && isSwipeUp && !mIsShelfPeeking) {
// 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) {
- 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;
- }
+ endTarget = NEW_TASK;
+ } else if (isSwipeUp) {
+ endTarget = !reachedOverviewThreshold && willGoToNewTaskOnSwipeUp
+ ? NEW_TASK : RECENTS;
} else {
endTarget = goingToNewTask ? NEW_TASK : LAST_TASK;
}
@@ -969,14 +781,14 @@
interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
} else {
startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
- * SINGLE_FRAME_MS / mTransitionDragLength, 0, mDragLengthFactor);
+ * getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
float minFlingVelocity = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_min_velocity);
if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
if (endTarget == RECENTS && mMode != Mode.NO_BUTTON) {
Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
- startShift, endShift, endShift, velocityPxPerMs.y,
- mTransitionDragLength);
+ startShift, endShift, endShift, endVelocity / 1000,
+ mTransitionDragLength, mContext);
endShift = overshoot.end;
interpolator = overshoot.interpolator;
duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION,
@@ -1150,57 +962,15 @@
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param homeAnimationFactory The home animation factory.
*/
- private RectFSpringAnim createWindowAnimationToHome(float startProgress,
+ @Override
+ protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
- final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
- final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
- mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
- final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
-
- final View floatingView = homeAnimationFactory.getFloatingView();
- final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
- RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mActivity.getResources());
- if (isFloatingIconView) {
- FloatingIconView fiv = (FloatingIconView) floatingView;
- anim.addAnimatorListener(fiv);
- fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
- }
-
- 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 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 */);
-
- if (isFloatingIconView) {
- ((FloatingIconView) floatingView).update(currentRect, 1f, progress,
- windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
- }
-
- updateSysUiFlags(Math.max(progress, mCurrentShift.value));
- });
+ RectFSpringAnim anim =
+ super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
+ anim.addOnUpdateListener((r, p) -> updateSysUiFlags(Math.max(p, mCurrentShift.value)));
anim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
- homeAnim.dispatchOnStart();
if (mActivity != null) {
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
}
@@ -1208,7 +978,6 @@
@Override
public void onAnimationSuccess(Animator animator) {
- homeAnim.getAnimationPlayer().end();
if (mRecentsView != null) {
mRecentsView.post(mRecentsView::resetTaskVisuals);
}
@@ -1220,11 +989,18 @@
return anim;
}
- /**
- * @return The GestureEndTarget if the gesture has ended, else null.
- */
- public @Nullable GestureEndTarget getGestureEndTarget() {
- return mGestureEndTarget;
+ @Override
+ public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) {
+ if (mGestureEndTarget != null) {
+ sharedState.canGestureBeContinued = mGestureEndTarget.canBeContinued;
+ sharedState.goingToLauncher = mGestureEndTarget.isLauncher;
+ }
+
+ if (sharedState.canGestureBeContinued) {
+ cancelCurrentAnimation(sharedState);
+ } else {
+ reset();
+ }
}
@UiThread
@@ -1237,43 +1013,18 @@
@UiThread
private void startNewTask() {
- // Launch the task user scrolled to (mRecentsView.getNextPage()).
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- // We finish recents animation inside launchTask() when live tile is enabled.
- mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
- true /* freezeTaskList */);
- } else {
- int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id;
- mFinishingRecentsAnimationForNewTaskId = taskId;
- mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
- if (!mCanceled) {
- TaskView nextTask = mRecentsView.getTaskView(taskId);
- if (nextTask != null) {
- nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
- success -> {
- if (!success) {
- // We couldn't launch the task, so take user to overview so they can
- // decide what to do instead of staying in this broken state.
- endLauncherTransitionController();
- mActivityControlHelper.onLaunchTaskFailed(mActivity);
- nextTask.notifyTaskLaunchFailed(TAG);
- updateSysUiFlags(1 /* windowProgress == overview */);
- } else {
- mActivityControlHelper.onLaunchTaskSuccess(mActivity);
- }
- }, mMainThreadHandler);
- doLogGesture(NEW_TASK);
- }
- reset();
- }
- mCanceled = false;
- mFinishingRecentsAnimationForNewTaskId = -1;
- });
- }
- TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
+ startNewTask(STATE_HANDLER_INVALIDATED, success -> {
+ if (!success) {
+ // We couldn't launch the task, so take user to overview so they can
+ // decide what to do instead of staying in this broken state.
+ endLauncherTransitionController();
+ updateSysUiFlags(1 /* windowProgress == overview */);
+ }
+ doLogGesture(NEW_TASK);
+ });
}
- public void reset() {
+ private void reset() {
setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
@@ -1281,7 +1032,7 @@
* Cancels any running animation so that the active target can be overriden by a new swipe
* handle (in case of quick switch).
*/
- public void cancelCurrentAnimation(SwipeSharedState sharedState) {
+ private void cancelCurrentAnimation(SwipeSharedState sharedState) {
mCanceled = true;
mCurrentShift.cancelAnimation();
if (mLauncherTransitionController != null && mLauncherTransitionController
@@ -1304,7 +1055,7 @@
}
private void invalidateHandler() {
- endRunningWindowAnim();
+ endRunningWindowAnim(false /* cancel */);
if (mGestureEndCallback != null) {
mGestureEndCallback.run();
@@ -1435,7 +1186,8 @@
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
endLauncherTransitionController();
mActivityControlHelper.onSwipeUpToRecentsComplete(mActivity);
- mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true);
+ mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
+ true /* screenshot */);
mRecentsView.onSwipeUpAnimationSuccess();
RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
@@ -1444,17 +1196,12 @@
reset();
}
- public void setGestureEndCallback(Runnable gestureEndCallback) {
- mGestureEndCallback = gestureEndCallback;
- }
-
- private void setTargetAlphaProvider(
- BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
+ private void setTargetAlphaProvider(TargetAlphaProvider provider) {
mClipAnimationHelper.setTaskAlphaCallback(provider);
updateFinalShift();
}
- public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
+ public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, float expectedAlpha) {
if (!isNotInRecents(app)) {
return 0;
}
@@ -1465,16 +1212,4 @@
return app.isNotInRecents
|| app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
}
-
- private interface RunningWindowAnim {
- void end();
-
- static RunningWindowAnim wrap(Animator animator) {
- return animator::end;
- }
-
- static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
- return rectFSpringAnim::end;
- }
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index c2876180..2e9c0a3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -31,6 +32,10 @@
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
+
+import java.util.ArrayList;
public class FallbackRecentsView extends RecentsView<RecentsActivity> {
@@ -54,6 +59,8 @@
private float mZoomScale = 1f;
private float mZoomTranslationY = 0f;
+ private RunningTaskInfo mRunningTaskInfo;
+
public FallbackRecentsView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -88,6 +95,12 @@
}
@Override
+ public void reset() {
+ super.reset();
+ resetViewUI();
+ }
+
+ @Override
protected void getTaskSize(DeviceProfile dp, Rect outRect) {
LayoutUtils.calculateFallbackTaskSize(getContext(), dp, outRect);
}
@@ -115,6 +128,12 @@
}
@Override
+ public void resetTaskVisuals() {
+ super.resetTaskVisuals();
+ setFullscreenProgress(mFullscreenProgress);
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -139,4 +158,41 @@
TRANSLATION_Y.set(this, Utilities.mapRange(mZoomInProgress, 0, mZoomTranslationY));
FULLSCREEN_PROGRESS.set(this, mZoomInProgress);
}
+
+ public void onGestureAnimationStart(RunningTaskInfo runningTaskInfo) {
+ mRunningTaskInfo = runningTaskInfo;
+ onGestureAnimationStart(runningTaskInfo == null ? -1 : runningTaskInfo.taskId);
+ }
+
+ @Override
+ public void setCurrentTask(int runningTaskId) {
+ super.setCurrentTask(runningTaskId);
+ if (mRunningTaskInfo != null && mRunningTaskInfo.taskId != runningTaskId) {
+ mRunningTaskInfo = null;
+ }
+ }
+
+ @Override
+ protected void applyLoadPlan(ArrayList<Task> tasks) {
+ // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
+ // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
+ // track the index of the next task appropriately, as it we are switching on any other app.
+ if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) {
+ // Check if the task list has running task
+ boolean found = false;
+ for (Task t : tasks) {
+ if (t.key.id == mRunningTaskId) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ ArrayList<Task> newList = new ArrayList<>(tasks.size() + 1);
+ newList.addAll(tasks);
+ newList.add(Task.from(new TaskKey(mRunningTaskInfo), mRunningTaskInfo, false));
+ tasks = newList;
+ }
+ }
+ super.applyLoadPlan(tasks);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index db2af59..3d763ab 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import android.content.ComponentName;
import android.content.Context;
@@ -44,8 +45,6 @@
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.RecentsAnimationListenerSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -209,9 +208,7 @@
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mInputMonitorCompat.pilferPointers();
- BackgroundExecutor.get().submit(
- () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
- intent, null, newListenerSet, null, null));
+ startRecentsActivityAsync(intent, newListenerSet);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
index d05ca2a..6ec1da0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
@@ -15,344 +15,443 @@
*/
package com.android.quickstep.inputconsumers;
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_DOWN;
-import static android.view.MotionEvent.ACTION_POINTER_UP;
-import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.INVALID_POINTER_ID;
-
-import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
+import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION;
-import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
+import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS;
+import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.animation.AnimatorSet;
import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.graphics.RectF;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
+import android.os.Bundle;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
-import com.android.quickstep.ActivityControlHelper;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.BaseSwipeUpHandler;
+import com.android.quickstep.MultiStateCallback;
import com.android.quickstep.OverviewComponentObserver;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.RecentsModel;
import com.android.quickstep.SwipeSharedState;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
-import com.android.quickstep.util.NavBarPosition;
-import com.android.quickstep.util.RecentsAnimationListenerSet;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.util.ObjectWrapper;
+import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SwipeAnimationTargetSet;
-import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.BackgroundExecutor;
-import com.android.systemui.shared.system.InputMonitorCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.InputConsumerController;
-public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener {
+public class FallbackNoButtonInputConsumer extends
+ BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
- private static final int STATE_NOT_FINISHED = 0;
- private static final int STATE_FINISHED_TO_HOME = 1;
- private static final int STATE_FINISHED_TO_APP = 2;
+ private static final String[] STATE_NAMES = DEBUG_STATES ? new String[5] : null;
- private static final float PROGRESS_TO_END_GESTURE = -2;
+ private static int getFlagForIndex(int index, String name) {
+ if (DEBUG_STATES) {
+ STATE_NAMES[index] = name;
+ }
+ return 1 << index;
+ }
- private final ActivityControlHelper mActivityControlHelper;
- private final InputMonitorCompat mInputMonitor;
- private final Context mContext;
- private final NavBarPosition mNavBarPosition;
- private final SwipeSharedState mSwipeSharedState;
- private final OverviewComponentObserver mOverviewComponentObserver;
- private final int mRunningTaskId;
+ private static final int STATE_RECENTS_PRESENT =
+ getFlagForIndex(0, "STATE_RECENTS_PRESENT");
+ private static final int STATE_HANDLER_INVALIDATED =
+ getFlagForIndex(1, "STATE_HANDLER_INVALIDATED");
- private final ClipAnimationHelper mClipAnimationHelper;
- private final TransformParams mTransformParams = new TransformParams();
- private final float mTransitionDragLength;
- private final DeviceProfile mDP;
+ private static final int STATE_GESTURE_CANCELLED =
+ getFlagForIndex(2, "STATE_GESTURE_CANCELLED");
+ private static final int STATE_GESTURE_COMPLETED =
+ getFlagForIndex(3, "STATE_GESTURE_COMPLETED");
+ private static final int STATE_APP_CONTROLLER_RECEIVED =
+ getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
- private final RectF mSwipeTouchRegion;
- private final boolean mDisableHorizontalSwipe;
+ public enum GestureEndTarget {
+ HOME(3, 100, 1),
+ RECENTS(1, 300, 0),
+ LAST_TASK(0, 150, 1),
+ NEW_TASK(0, 150, 1);
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
+ private final float mEndProgress;
+ private final long mDurationMultiplier;
+ private final float mLauncherAlpha;
- private int mActivePointerId = -1;
- // Slop used to determine when we say that the gesture has started.
- private boolean mPassedPilferInputSlop;
+ GestureEndTarget(float endProgress, long durationMultiplier, float launcherAlpha) {
+ mEndProgress = endProgress;
+ mDurationMultiplier = durationMultiplier;
+ mLauncherAlpha = launcherAlpha;
+ }
+ }
- private VelocityTracker mVelocityTracker;
+ private final AnimatedFloat mLauncherAlpha = new AnimatedFloat(this::onLauncherAlphaChanged);
- // Distance after which we start dragging the window.
- private final float mTouchSlop;
+ private boolean mIsMotionPaused = false;
+ private GestureEndTarget mEndTarget;
- // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
- private float mStartDisplacement;
- private SwipeAnimationTargetSet mSwipeAnimationTargetSet;
- private float mProgress;
+ private final boolean mInQuickSwitchMode;
+ private final boolean mContinuingLastGesture;
+ private final boolean mRunningOverHome;
+ private final boolean mSwipeUpOverHome;
- private int mState = STATE_NOT_FINISHED;
+ private final RunningTaskInfo mRunningTaskInfo;
+
+ private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
+ private RunningWindowAnim mFinishAnimation;
public FallbackNoButtonInputConsumer(Context context,
- ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor,
- SwipeSharedState swipeSharedState, RectF swipeTouchRegion,
OverviewComponentObserver overviewComponentObserver,
- boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) {
- mContext = context;
- mActivityControlHelper = activityControlHelper;
- mInputMonitor = inputMonitor;
- mOverviewComponentObserver = overviewComponentObserver;
- mRunningTaskId = runningTaskInfo.id;
+ RunningTaskInfo runningTaskInfo, RecentsModel recentsModel,
+ InputConsumerController inputConsumer,
+ boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
+ super(context, overviewComponentObserver, recentsModel, inputConsumer, runningTaskInfo.id);
+ mLauncherAlpha.value = 1;
- mSwipeSharedState = swipeSharedState;
- mSwipeTouchRegion = swipeTouchRegion;
- mDisableHorizontalSwipe = disableHorizontalSwipe;
+ mRunningTaskInfo = runningTaskInfo;
+ mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
+ mContinuingLastGesture = continuingLastGesture;
+ mRunningOverHome = ActivityManagerWrapper.isHomeTask(runningTaskInfo);
+ mSwipeUpOverHome = mRunningOverHome && !mInQuickSwitchMode;
- mNavBarPosition = new NavBarPosition(context);
- mVelocityTracker = VelocityTracker.obtain();
-
- mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO
- * ViewConfiguration.get(context).getScaledTouchSlop();
-
- mClipAnimationHelper = new ClipAnimationHelper(context);
-
- mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context);
- Rect tempRect = new Rect();
- mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
- mDP, context, tempRect);
- mClipAnimationHelper.updateTargetRect(tempRect);
- }
-
- @Override
- public int getType() {
- return TYPE_FALLBACK_NO_BUTTON;
- }
-
- @Override
- public void onMotionEvent(MotionEvent ev) {
- if (mVelocityTracker == null) {
- return;
- }
-
- mVelocityTracker.addMovement(ev);
- if (ev.getActionMasked() == ACTION_POINTER_UP) {
- mVelocityTracker.clear();
- }
-
- switch (ev.getActionMasked()) {
- case ACTION_DOWN: {
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
- break;
- }
- case ACTION_POINTER_DOWN: {
- if (!mPassedPilferInputSlop) {
- // Cancel interaction in case of multi-touch interaction
- int ptrIdx = ev.getActionIndex();
- if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) {
- forceCancelGesture(ev);
- }
- }
- break;
- }
- case ACTION_POINTER_UP: {
- int ptrIdx = ev.getActionIndex();
- int ptrId = ev.getPointerId(ptrIdx);
- if (ptrId == mActivePointerId) {
- final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
- mDownPos.set(
- ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
- ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
- mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
- mActivePointerId = ev.getPointerId(newPointerIdx);
- }
- break;
- }
- case ACTION_MOVE: {
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- float displacement = getDisplacement(ev);
-
- if (!mPassedPilferInputSlop) {
- if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x)
- > Math.abs(mLastPos.y - mDownPos.y)) {
- // Horizontal gesture is not allowed in this region
- forceCancelGesture(ev);
- break;
- }
-
- if (Math.abs(displacement) >= mTouchSlop) {
- mPassedPilferInputSlop = true;
-
- // Deferred gesture, start the animation and gesture tracking once
- // we pass the actual touch slop
- startTouchTrackingForWindowAnimation(displacement);
- }
- } else {
- updateDisplacement(displacement - mStartDisplacement);
- }
- break;
- }
- case ACTION_CANCEL:
- case ACTION_UP: {
- finishTouchTracking(ev);
- break;
- }
- }
- }
-
- private void startTouchTrackingForWindowAnimation(float displacement) {
- mStartDisplacement = Math.min(displacement, -mTouchSlop);
-
- RecentsAnimationListenerSet listenerSet =
- mSwipeSharedState.newRecentsAnimationListenerSet();
- listenerSet.addListener(this);
- Intent homeIntent = mOverviewComponentObserver.getHomeIntent();
- BackgroundExecutor.get().submit(
- () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
- homeIntent, null, listenerSet, null, null));
-
- ActivityManagerWrapper.getInstance().closeSystemWindows(
- CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
- mInputMonitor.pilferPointers();
- }
-
- private void updateDisplacement(float displacement) {
- mProgress = displacement / mTransitionDragLength;
- mTransformParams.setProgress(mProgress);
-
- if (mSwipeAnimationTargetSet != null) {
- mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
- }
- }
-
- private void forceCancelGesture(MotionEvent ev) {
- int action = ev.getAction();
- ev.setAction(ACTION_CANCEL);
- finishTouchTracking(ev);
- ev.setAction(action);
- }
-
- /**
- * Called when the gesture has ended. Does not correlate to the completion of the interaction as
- * the animation can still be running.
- */
- private void finishTouchTracking(MotionEvent ev) {
- if (ev.getAction() == ACTION_CANCEL) {
- mState = STATE_FINISHED_TO_APP;
+ if (mSwipeUpOverHome) {
+ mClipAnimationHelper.setBaseAlphaCallback((t, a) -> 1 - mLauncherAlpha.value);
} else {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
- float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
- float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = mNavBarPosition.isRightEdge() ? velocityX
- : mNavBarPosition.isLeftEdge() ? -velocityX
- : velocityY;
+ mClipAnimationHelper.setBaseAlphaCallback((t, a) -> mLauncherAlpha.value);
+ }
+
+ initStateCallbacks();
+ }
+
+ private void initStateCallbacks() {
+ mStateCallback = new MultiStateCallback(STATE_NAMES);
+
+ mStateCallback.addCallback(STATE_HANDLER_INVALIDATED,
+ this::onHandlerInvalidated);
+ mStateCallback.addCallback(STATE_RECENTS_PRESENT | STATE_HANDLER_INVALIDATED,
+ this::onHandlerInvalidatedWithRecents);
+
+ mStateCallback.addCallback(STATE_GESTURE_CANCELLED | STATE_APP_CONTROLLER_RECEIVED,
+ this::finishAnimationTargetSetAnimationComplete);
+
+ if (mInQuickSwitchMode) {
+ mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_RECENTS_PRESENT,
+ this::finishAnimationTargetSet);
+ } else {
+ mStateCallback.addCallback(STATE_GESTURE_COMPLETED | STATE_APP_CONTROLLER_RECEIVED,
+ this::finishAnimationTargetSet);
+ }
+ }
+
+ private void onLauncherAlphaChanged() {
+ if (mRecentsAnimationWrapper.targetSet != null && mEndTarget == null) {
+ applyTransformUnchecked();
+ }
+ }
+
+ @Override
+ protected boolean onActivityInit(final RecentsActivity activity, Boolean alreadyOnHome) {
+ mActivity = activity;
+ mRecentsView = activity.getOverviewPanel();
+ linkRecentsViewScroll();
+ mRecentsView.setDisallowScrollToClearAll(true);
+ mRecentsView.getClearAllButton().setVisibilityAlpha(0);
+
+ mRecentsView.setZoomProgress(1);
+
+ if (!mContinuingLastGesture) {
+ if (mRunningOverHome) {
+ mRecentsView.onGestureAnimationStart(mRunningTaskInfo);
+ } else {
+ mRecentsView.onGestureAnimationStart(mRunningTaskId);
+ }
+ }
+ setStateOnUiThread(STATE_RECENTS_PRESENT);
+ return true;
+ }
+
+ @Override
+ protected boolean moveWindowWithRecentsScroll() {
+ return mInQuickSwitchMode;
+ }
+
+ @Override
+ public void initWhenReady() {
+ if (mInQuickSwitchMode) {
+ // Only init if we are in quickswitch mode
+ super.initWhenReady();
+ }
+ }
+
+ @Override
+ public void updateDisplacement(float displacement) {
+ if (!mInQuickSwitchMode) {
+ super.updateDisplacement(displacement);
+ }
+ }
+
+ @Override
+ protected InputConsumer createNewInputProxyHandler() {
+ // Just consume all input on the active task
+ return InputConsumer.NO_OP;
+ }
+
+ @Override
+ public void onMotionPauseChanged(boolean isPaused) {
+ if (!mInQuickSwitchMode) {
+ mIsMotionPaused = isPaused;
+ mLauncherAlpha.animateToValue(mLauncherAlpha.value, isPaused ? 0 : 1)
+ .setDuration(150).start();
+ performHapticFeedback();
+ }
+ }
+
+ @Override
+ public Intent getLaunchIntent() {
+ if (mInQuickSwitchMode || mSwipeUpOverHome) {
+ return mOverviewComponentObserver.getOverviewIntent();
+ } else {
+ return mOverviewComponentObserver.getHomeIntent();
+ }
+ }
+
+ @Override
+ public void updateFinalShift() {
+ mTransformParams.setProgress(mCurrentShift.value);
+ mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode
+ && (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
+ if (mRecentsAnimationWrapper.targetSet != null) {
+ applyTransformUnchecked();
+ }
+ }
+
+ @Override
+ public void onGestureCancelled() {
+ updateDisplacement(0);
+ mEndTarget = LAST_TASK;
+ setStateOnUiThread(STATE_GESTURE_CANCELLED);
+ }
+
+ @Override
+ public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
+ mEndVelocityPxPerMs.set(0, velocity.y / 1000);
+ if (mInQuickSwitchMode) {
+ // For now set it to non-null, it will be reset before starting the animation
+ mEndTarget = LAST_TASK;
+ } else {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
- boolean isFling = Math.abs(velocity) > flingThreshold;
+ boolean isFling = Math.abs(endVelocity) > flingThreshold;
- boolean goingHome;
- if (!isFling) {
- goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW;
+ if (isFling) {
+ mEndTarget = endVelocity < 0 ? HOME : LAST_TASK;
+ } else if (mIsMotionPaused) {
+ mEndTarget = RECENTS;
} else {
- goingHome = velocity < 0;
+ mEndTarget = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? HOME : LAST_TASK;
+ }
+ }
+ setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ }
+
+ @Override
+ public void onConsumerAboutToBeSwitched(SwipeSharedState sharedState) {
+ if (mInQuickSwitchMode && mEndTarget != null) {
+ sharedState.canGestureBeContinued = true;
+ sharedState.goingToLauncher = false;
+
+ mCanceled = true;
+ mCurrentShift.cancelAnimation();
+ if (mFinishAnimation != null) {
+ mFinishAnimation.cancel();
}
- if (goingHome) {
- mState = STATE_FINISHED_TO_HOME;
- } else {
- mState = STATE_FINISHED_TO_APP;
+ if (mRecentsView != null) {
+ if (mFinishingRecentsAnimationForNewTaskId != -1) {
+ TaskView newRunningTaskView = mRecentsView.getTaskView(
+ mFinishingRecentsAnimationForNewTaskId);
+ int newRunningTaskId = newRunningTaskView != null
+ ? newRunningTaskView.getTask().key.id
+ : -1;
+ mRecentsView.setCurrentTask(newRunningTaskId);
+ sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId);
+ }
+ mRecentsView.setOnScrollChangeListener(null);
+ }
+ } else {
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ }
+ }
+
+ private void onHandlerInvalidated() {
+ mActivityInitListener.unregister();
+ if (mGestureEndCallback != null) {
+ mGestureEndCallback.run();
+ }
+ if (mFinishAnimation != null) {
+ mFinishAnimation.end();
+ }
+ }
+
+ private void onHandlerInvalidatedWithRecents() {
+ mRecentsView.onGestureAnimationEnd();
+ mRecentsView.setDisallowScrollToClearAll(false);
+ mRecentsView.getClearAllButton().setVisibilityAlpha(1);
+ }
+
+ private void finishAnimationTargetSetAnimationComplete() {
+ switch (mEndTarget) {
+ case HOME: {
+ if (mSwipeUpOverHome) {
+ mRecentsAnimationWrapper.finish(false, null, false);
+ // Send a home intent to clear the task stack
+ mContext.startActivity(mOverviewComponentObserver.getHomeIntent());
+ } else {
+ mRecentsAnimationWrapper.finish(true, null, true);
+ }
+ break;
+ }
+ case LAST_TASK:
+ mRecentsAnimationWrapper.finish(false, null, false);
+ break;
+ case RECENTS: {
+ if (mSwipeUpOverHome) {
+ mRecentsAnimationWrapper.finish(true, null, true);
+ break;
+ }
+
+ ThumbnailData thumbnail =
+ mRecentsAnimationWrapper.targetSet.controller.screenshotTask(mRunningTaskId);
+ mRecentsAnimationWrapper.setDeferCancelUntilNextTransition(true /* defer */,
+ false /* screenshot */);
+
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+ ActivityOptionsCompat.setFreezeRecentTasksList(options);
+
+ Bundle extras = new Bundle();
+ extras.putBinder(EXTRA_THUMBNAIL, new ObjectWrapper<>(thumbnail));
+ extras.putInt(EXTRA_TASK_ID, mRunningTaskId);
+
+ Intent intent = new Intent(mOverviewComponentObserver.getOverviewIntent())
+ .putExtras(extras);
+ mContext.startActivity(intent, options.toBundle());
+ mRecentsAnimationWrapper.targetSet.controller.cleanupScreenshot();
+ break;
+ }
+ case NEW_TASK: {
+ startNewTask(STATE_HANDLER_INVALIDATED, b -> {});
+ break;
}
}
- if (mSwipeAnimationTargetSet != null) {
- finishAnimationTargetSet();
- }
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
private void finishAnimationTargetSet() {
- if (mState == STATE_FINISHED_TO_APP) {
- mSwipeAnimationTargetSet.finishController(false, null, false);
- } else {
- if (mProgress < PROGRESS_TO_END_GESTURE) {
- mSwipeAnimationTargetSet.finishController(true, null, true);
+ if (mInQuickSwitchMode) {
+ // Recalculate the end target, some views might have been initialized after
+ // gesture has ended.
+ if (mRecentsView == null || !mRecentsAnimationWrapper.hasTargets()) {
+ mEndTarget = LAST_TASK;
} else {
- long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1)
- * MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE));
- if (duration < 0) {
- duration = MIN_SWIPE_DURATION;
- }
-
- ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE);
- anim.addUpdateListener(a -> {
- float p = (Float) anim.getAnimatedValue();
- mTransformParams.setProgress(p);
- mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
- });
- anim.setDuration(duration);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimationTargetSet.finishController(true, null, true);
- }
- });
- anim.start();
+ final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
+ final int taskToLaunch = mRecentsView.getNextPage();
+ mEndTarget = (runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex)
+ ? NEW_TASK : LAST_TASK;
}
}
+
+ float endProgress = mEndTarget.mEndProgress;
+ long duration = (long) (mEndTarget.mDurationMultiplier *
+ Math.abs(endProgress - mCurrentShift.value));
+ if (mRecentsView != null) {
+ duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+ }
+ if (mCurrentShift.value != endProgress || mInQuickSwitchMode) {
+ AnimationSuccessListener endListener = new AnimationSuccessListener() {
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ finishAnimationTargetSetAnimationComplete();
+ mFinishAnimation = null;
+ }
+ };
+
+ if (mEndTarget == HOME && !mRunningOverHome) {
+ RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
+ anim.addAnimatorListener(endListener);
+ anim.start(mEndVelocityPxPerMs);
+ mFinishAnimation = RunningWindowAnim.wrap(anim);
+ } else {
+
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(mLauncherAlpha.animateToValue(
+ mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
+ anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));
+
+ anim.setDuration(duration);
+ anim.addListener(endListener);
+ anim.start();
+ mFinishAnimation = RunningWindowAnim.wrap(anim);
+ }
+
+ } else {
+ finishAnimationTargetSetAnimationComplete();
+ }
}
@Override
public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
- mSwipeAnimationTargetSet = targetSet;
- Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx);
- RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
+ super.onRecentsAnimationStart(targetSet);
+ mRecentsAnimationWrapper.enableInputConsumer();
- mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class));
- if (runningTaskTarget != null) {
- mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
+ if (mRunningOverHome) {
+ mClipAnimationHelper.prepareAnimation(mDp, true);
}
- mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */);
+ applyTransformUnchecked();
- overviewStackBounds
- .inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5);
- mClipAnimationHelper.updateTargetRect(overviewStackBounds);
- mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams);
-
- if (mState != STATE_NOT_FINISHED) {
- finishAnimationTargetSet();
- }
+ setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
@Override
- public void onRecentsAnimationCanceled() { }
-
- private float getDisplacement(MotionEvent ev) {
- if (mNavBarPosition.isRightEdge()) {
- return ev.getX() - mDownPos.x;
- } else if (mNavBarPosition.isLeftEdge()) {
- return mDownPos.x - ev.getX();
- } else {
- return ev.getY() - mDownPos.y;
- }
+ public void onRecentsAnimationCanceled() {
+ mRecentsAnimationWrapper.setController(null);
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
- @Override
- public boolean allowInterceptByParent() {
- return !mPassedPilferInputSlop;
+ /**
+ * Creates an animation that transforms the current app window into the home app.
+ * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
+ */
+ private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) {
+ HomeAnimationFactory factory = new HomeAnimationFactory() {
+ @Override
+ public RectF getWindowTargetRect() {
+ return HomeAnimationFactory.getDefaultWindowTargetRect(mDp);
+ }
+
+ @Override
+ public AnimatorPlaybackController createActivityAnimationToHome() {
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1));
+ anim.setDuration(duration);
+ return AnimatorPlaybackController.wrap(anim, duration);
+ }
+ };
+ return createWindowAnimationToHome(startProgress, factory);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
index f5cf654..a1e5d47 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java
@@ -33,7 +33,6 @@
int TYPE_SCREEN_PINNED = 1 << 6;
int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7;
int TYPE_RESET_GESTURE = 1 << 8;
- int TYPE_FALLBACK_NO_BUTTON = 1 << 9;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -45,7 +44,6 @@
"TYPE_SCREEN_PINNED", // 6
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
- "TYPE_FALLBACK_NO_BUTTON", // 9
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 4c137d3..86766d9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -28,13 +28,13 @@
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.Intent;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
@@ -48,21 +48,16 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.ActivityControlHelper;
+import com.android.quickstep.BaseSwipeUpHandler;
import com.android.quickstep.OverviewCallbacks;
-import com.android.quickstep.RecentsModel;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
-import com.android.quickstep.WindowTransformSwipeHandler;
-import com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.RecentsAnimationListenerSet;
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 java.util.function.Consumer;
@@ -83,16 +78,14 @@
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final RunningTaskInfo mRunningTask;
- private final RecentsModel mRecentsModel;
- private final Intent mHomeIntent;
- private final ActivityControlHelper mActivityControlHelper;
private final OverviewCallbacks mOverviewCallbacks;
- private final InputConsumerController mInputConsumer;
private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
private final SysUINavigationMode.Mode mMode;
private final RectF mSwipeTouchRegion;
+ private final BaseSwipeUpHandler.Factory mHandlerFactory;
+
private final NavBarPosition mNavBarPosition;
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
@@ -100,7 +93,7 @@
private final float mMotionPauseMinDisplacement;
private VelocityTracker mVelocityTracker;
- private WindowTransformSwipeHandler mInteractionHandler;
+ private BaseSwipeUpHandler mInteractionHandler;
private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
@@ -114,7 +107,7 @@
private final boolean mDisableHorizontalSwipe;
// Slop used to check when we start moving window.
- private boolean mPaddedWindowMoveSlop;
+ private boolean mPassedWindowMoveSlop;
// Slop used to determine when we say that the gesture has started.
private boolean mPassedPilferInputSlop;
@@ -128,20 +121,18 @@
};
public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
- RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
- InputConsumerController inputConsumer,
Consumer<OtherActivityInputConsumer> onCompleteCallback,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat,
- RectF swipeTouchRegion, boolean disableHorizontalSwipe) {
+ RectF swipeTouchRegion, boolean disableHorizontalSwipe,
+ BaseSwipeUpHandler.Factory handlerFactory) {
super(base);
mMainThreadHandler = new Handler(Looper.getMainLooper());
mRunningTask = runningTaskInfo;
- mRecentsModel = recentsModel;
- mHomeIntent = homeIntent;
mMode = SysUINavigationMode.getMode(base);
mSwipeTouchRegion = swipeTouchRegion;
+ mHandlerFactory = handlerFactory;
mMotionPauseDetector = new MotionPauseDetector(base);
mMotionPauseMinDisplacement = base.getResources().getDimension(
@@ -150,11 +141,9 @@
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
- mActivityControlHelper = activityControl;
boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
mOverviewCallbacks = overviewCallbacks;
- mInputConsumer = inputConsumer;
mSwipeSharedState = swipeSharedState;
mNavBarPosition = new NavBarPosition(base);
@@ -163,7 +152,7 @@
float slop = QUICKSTEP_TOUCH_SLOP_RATIO * mTouchSlop;
mSquaredTouchSlop = slop * slop;
- mPassedPilferInputSlop = mPaddedWindowMoveSlop = continuingPreviousGesture;
+ mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
}
@@ -186,7 +175,7 @@
}
// Proxy events to recents view
- if (mPaddedWindowMoveSlop && mInteractionHandler != null
+ if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
mNavBarPosition.getRotationMode()));
@@ -213,7 +202,7 @@
// Start the window animation on down to give more time for launcher to draw if the
// user didn't start the gesture over the back button
if (!mIsDeferredDownTarget) {
- startTouchTrackingForWindowAnimation(ev.getEventTime());
+ startTouchTrackingForWindowAnimation(ev.getEventTime(), false);
}
RaceConditionTracker.onEvent(DOWN_EVT, EXIT);
@@ -251,17 +240,21 @@
float displacement = getDisplacement(ev);
float displacementX = mLastPos.x - mDownPos.x;
- if (!mPaddedWindowMoveSlop) {
+ if (!mPassedWindowMoveSlop) {
if (!mIsDeferredDownTarget) {
// Normal gesture, ensure we pass the drag slop before we start tracking
// the gesture
if (Math.abs(displacement) > mTouchSlop) {
- mPaddedWindowMoveSlop = true;
+ mPassedWindowMoveSlop = true;
mStartDisplacement = Math.min(displacement, -mTouchSlop);
}
}
}
+ float horizontalDist = Math.abs(displacementX);
+ float upDist = -displacement;
+ boolean isLikelyToStartNewTask = horizontalDist > upDist;
+
if (!mPassedPilferInputSlop) {
float displacementY = mLastPos.y - mDownPos.y;
if (squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop) {
@@ -277,10 +270,11 @@
if (mIsDeferredDownTarget) {
// Deferred gesture, start the animation and gesture tracking once
// we pass the actual touch slop
- startTouchTrackingForWindowAnimation(ev.getEventTime());
+ startTouchTrackingForWindowAnimation(
+ ev.getEventTime(), isLikelyToStartNewTask);
}
- if (!mPaddedWindowMoveSlop) {
- mPaddedWindowMoveSlop = true;
+ if (!mPassedWindowMoveSlop) {
+ mPassedWindowMoveSlop = true;
mStartDisplacement = Math.min(displacement, -mTouchSlop);
}
@@ -289,15 +283,12 @@
}
if (mInteractionHandler != null) {
- if (mPaddedWindowMoveSlop) {
+ if (mPassedWindowMoveSlop) {
// Move
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
if (mMode == Mode.NO_BUTTON) {
- float horizontalDist = Math.abs(displacementX);
- float upDist = -displacement;
- boolean isLikelyToStartNewTask = horizontalDist > upDist;
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
@@ -329,16 +320,14 @@
mInteractionHandler.onGestureStarted();
}
- private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
+ private void startTouchTrackingForWindowAnimation(
+ long touchTimeMs, boolean isLikelyToStartNewTask) {
TOUCH_INTERACTION_LOG.addLog("startRecentsAnimation");
RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
- final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
- mRunningTask, this, touchTimeMs, mActivityControlHelper,
- listenerSet != null, mInputConsumer);
+ final BaseSwipeUpHandler handler = mHandlerFactory.newHandler(mRunningTask, touchTimeMs,
+ listenerSet != null, isLikelyToStartNewTask);
- // Preload the plan
- mRecentsModel.getTasks(null);
mInteractionHandler = handler;
handler.setGestureEndCallback(this::onInteractionGestureFinished);
mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
@@ -352,9 +341,7 @@
RecentsAnimationListenerSet newListenerSet =
mSwipeSharedState.newRecentsAnimationListenerSet();
newListenerSet.addListener(handler);
- BackgroundExecutor.get().submit(
- () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
- mHomeIntent, null, newListenerSet, null, null));
+ startRecentsActivityAsync(handler.getLaunchIntent(), newListenerSet);
}
}
@@ -366,7 +353,7 @@
RaceConditionTracker.onEvent(UP_EVT, ENTER);
TraceHelper.endSection("TouchInt");
- if (mPaddedWindowMoveSlop && mInteractionHandler != null) {
+ if (mPassedWindowMoveSlop && mInteractionHandler != null) {
if (ev.getActionMasked() == ACTION_CANCEL) {
mInteractionHandler.onGestureCancelled();
} else {
@@ -409,14 +396,7 @@
// The consumer is being switched while we are active. Set up the shared state to be
// used by the next animation
removeListener();
- GestureEndTarget endTarget = mInteractionHandler.getGestureEndTarget();
- mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
- mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
- if (mSwipeSharedState.canGestureBeContinued) {
- mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState);
- } else {
- mInteractionHandler.reset();
- }
+ mInteractionHandler.onConsumerAboutToBeSwitched(mSwipeSharedState);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 56db209..e553891 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -19,6 +19,7 @@
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -26,6 +27,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.OverviewCallbacks;
import com.android.systemui.shared.system.ActivityManagerWrapper;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 425b8b6..05cbb78 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -30,7 +30,13 @@
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogUtils;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.OverviewCallbacks;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -131,12 +137,14 @@
? -velocityX : (mNavBarPosition.isLeftEdge() ? velocityX : -velocityY);
final boolean triggerQuickstep;
+ int touch = Touch.FLING;
if (Math.abs(velocity) >= ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) {
triggerQuickstep = velocity > 0;
} else {
float displacementX = mDisableHorizontalSwipe ? 0 : (ev.getX() - mDownPos.x);
float displacementY = ev.getY() - mDownPos.y;
triggerQuickstep = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop;
+ touch = Touch.SWIPE;
}
if (triggerQuickstep) {
@@ -144,6 +152,13 @@
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
+ BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
+ int pageIndex = -1; // This number doesn't reflect workspace page index.
+ // It only indicates that launcher client screen was shown.
+ int containerType = StatsLogUtils.getContainerTypeFromState(activity.getCurrentState());
+ activity.getUserEventDispatcher().logActionOnContainer(
+ touch, Direction.UP, containerType, pageIndex);
+ activity.getUserEventDispatcher().setPreviousHomeGesture(true);
} else {
// ignore
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index de671e0..cae273a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -99,8 +99,8 @@
// Whether to boost the opening animation target layers, or the closing
private int mBoostModeTargetLayers = -1;
- private BiFunction<RemoteAnimationTargetCompat, Float, Float> mTaskAlphaCallback =
- (t, a1) -> a1;
+ private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
+ private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
public ClipAnimationHelper(Context context) {
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
@@ -119,8 +119,12 @@
}
public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
- mHomeStackBounds.set(homeStackBounds);
updateSourceStack(target);
+ updateHomeBounds(homeStackBounds);
+ }
+
+ public void updateHomeBounds(Rect homeStackBounds) {
+ mHomeStackBounds.set(homeStackBounds);
}
public void updateTargetRect(Rect targetRect) {
@@ -187,12 +191,12 @@
Rect crop = mTmpRect;
crop.set(app.sourceContainerBounds);
crop.offsetTo(0, 0);
- float alpha = 1f;
+ float alpha;
int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
float cornerRadius = 0f;
float scale = Math.max(params.currentRect.width(), mTargetRect.width()) / crop.width();
if (app.mode == targetSet.targetMode) {
- alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
+ alpha = mTaskAlphaCallback.getAlpha(app, params.targetAlpha);
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
mTmpMatrix.setRectToRect(mSourceRect, params.currentRect, ScaleToFit.FILL);
mTmpMatrix.postTranslate(app.position.x, app.position.y);
@@ -214,9 +218,12 @@
// home target.
alpha = 1 - (progress * params.targetAlpha);
}
- } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) {
- crop = null;
- layer = Integer.MAX_VALUE;
+ } else {
+ alpha = mBaseAlphaCallback.getAlpha(app, progress);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && launcherOnTop) {
+ crop = null;
+ layer = Integer.MAX_VALUE;
+ }
}
// Since radius is in Surface space, but we draw the rounded corners in screen space, we
@@ -247,11 +254,14 @@
}
}
- public void setTaskAlphaCallback(
- BiFunction<RemoteAnimationTargetCompat, Float, Float> callback) {
+ public void setTaskAlphaCallback(TargetAlphaProvider callback) {
mTaskAlphaCallback = callback;
}
+ public void setBaseAlphaCallback(TargetAlphaProvider callback) {
+ mBaseAlphaCallback = callback;
+ }
+
public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
fromTaskThumbnailView(ttv, rv, null);
}
@@ -357,6 +367,10 @@
return mCurrentCornerRadius;
}
+ public interface TargetAlphaProvider {
+ float getAlpha(RemoteAnimationTargetCompat target, float expectedAlpha);
+ }
+
public static class TransformParams {
float progress;
public float offsetX;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
new file mode 100644
index 0000000..abfe3ad
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ObjectWrapper.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package com.android.quickstep.util;
+
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Utility class to pass non-parcealable objects within same process using parcealable payload.
+ *
+ * It wraps the object in a binder as binders are singleton within a process
+ */
+public class ObjectWrapper<T> extends Binder {
+
+ private final T mObject;
+
+ public ObjectWrapper(T object) {
+ mObject = object;
+ }
+
+ public T get() {
+ return mObject;
+ }
+
+ public static IBinder wrap(Object obj) {
+ return new ObjectWrapper<>(obj);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
index 77dc6f3..c6eafe6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -225,7 +225,18 @@
}
}
+ public void cancel() {
+ if (mAnimsStarted) {
+ for (OnUpdateListener onUpdateListener : mOnUpdateListeners) {
+ onUpdateListener.onCancel();
+ }
+ }
+ end();
+ }
+
public interface OnUpdateListener {
void onUpdate(RectF currentRect, float progress);
+
+ default void onCancel() { }
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 07e9686..1069bed 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -15,7 +15,14 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.view.View;
import android.view.ViewGroup;
@@ -29,18 +36,15 @@
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Workspace;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.graphics.OverviewScrim;
import java.util.ArrayList;
import java.util.List;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
/**
* Creates an animation where all the workspace items are moved into their final location,
* staggered row by row from the bottom up.
@@ -79,9 +83,19 @@
.getDimensionPixelSize(R.dimen.swipe_up_max_workspace_trans_y);
DeviceProfile grid = launcher.getDeviceProfile();
- ShortcutAndWidgetContainer currentPage = ((CellLayout) launcher.getWorkspace()
- .getChildAt(launcher.getWorkspace().getCurrentPage()))
- .getShortcutsAndWidgets();
+ Workspace workspace = launcher.getWorkspace();
+ CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
+ ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
+
+ boolean workspaceClipChildren = workspace.getClipChildren();
+ boolean workspaceClipToPadding = workspace.getClipToPadding();
+ boolean cellLayoutClipChildren = cellLayout.getClipChildren();
+ boolean cellLayoutClipToPadding = cellLayout.getClipToPadding();
+
+ workspace.setClipChildren(false);
+ workspace.setClipToPadding(false);
+ cellLayout.setClipChildren(false);
+ cellLayout.setClipToPadding(false);
// Hotseat and QSB takes up two additional rows.
int totalRows = grid.inv.numRows + (grid.isVerticalBarLayout() ? 0 : 2);
@@ -109,8 +123,29 @@
addStaggeredAnimationForView(qsb, grid.inv.numRows + 2, totalRows);
}
- addWorkspaceScrimAnimationForState(launcher, BACKGROUND_APP, 0);
- addWorkspaceScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+ addScrimAnimationForState(launcher, BACKGROUND_APP, 0);
+ addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
+
+ AnimatorListener resetClipListener = new AnimatorListenerAdapter() {
+ int numAnimations = mAnimators.size();
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ numAnimations--;
+ if (numAnimations > 0) {
+ return;
+ }
+
+ workspace.setClipChildren(workspaceClipChildren);
+ workspace.setClipToPadding(workspaceClipToPadding);
+ cellLayout.setClipChildren(cellLayoutClipChildren);
+ cellLayout.setClipToPadding(cellLayoutClipToPadding);
+ }
+ };
+
+ for (Animator a : mAnimators) {
+ a.addListener(resetClipListener);
+ }
}
/**
@@ -134,10 +169,6 @@
* @param totalRows Total number of rows.
*/
private void addStaggeredAnimationForView(View v, int row, int totalRows) {
- if (v == mViewToIgnore) {
- return;
- }
-
// Invert the rows, because we stagger starting from the bottom of the screen.
int invertedRow = totalRows - row;
// Add 1 to the inverted row so that the bottom most row has a start delay.
@@ -149,6 +180,10 @@
springTransY.setStartDelay(startDelay);
mAnimators.add(springTransY);
+ if (v == mViewToIgnore) {
+ return;
+ }
+
v.setAlpha(0);
ObjectAnimator alpha = ObjectAnimator.ofFloat(v, View.ALPHA, 0f, 1f);
alpha.setInterpolator(LINEAR);
@@ -157,13 +192,17 @@
mAnimators.add(alpha);
}
- private void addWorkspaceScrimAnimationForState(Launcher launcher, LauncherState state,
- long duration) {
+ private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder();
AnimationConfig scrimAnimConfig = new AnimationConfig();
scrimAnimConfig.duration = duration;
PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder);
launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state);
mAnimators.add(scrimAnimBuilder.build());
+ Animator fadeOverviewScrim = ObjectAnimator.ofFloat(
+ launcher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
+ state.getOverviewScrimAlpha(launcher));
+ fadeOverviewScrim.setDuration(duration);
+ mAnimators.add(fadeOverviewScrim);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index a98df0f..9b157d1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -37,6 +37,7 @@
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -72,6 +73,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ListView;
@@ -108,7 +110,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.LauncherEventUtil;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
@@ -226,7 +227,7 @@
return;
}
- BackgroundExecutor.get().submit(() -> {
+ BACKGROUND_EXECUTOR.execute(() -> {
TaskView taskView = getTaskView(taskId);
if (taskView == null) {
return;
@@ -269,7 +270,7 @@
private int mTaskListChangeId = -1;
// Only valid until the launcher state changes to NORMAL
- private int mRunningTaskId = -1;
+ protected int mRunningTaskId = -1;
private boolean mRunningTaskTileHidden;
private Task mTmpRunningTask;
@@ -289,7 +290,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
private float mContentAlpha = 1;
@ViewDebug.ExportedProperty(category = "launcher")
- private float mFullscreenProgress = 0;
+ protected float mFullscreenProgress = 0;
// Keeps track of task id whose visual state should not be reset
private int mIgnoreResetTaskId = -1;
@@ -527,7 +528,7 @@
return true;
}
- private void applyLoadPlan(ArrayList<Task> tasks) {
+ protected void applyLoadPlan(ArrayList<Task> tasks) {
if (mPendingAnimation != null) {
mPendingAnimation.addEndListener((onEndListener) -> applyLoadPlan(tasks));
return;
@@ -599,6 +600,7 @@
TaskView taskView = (TaskView) getChildAt(i);
if (mIgnoreResetTaskId != taskView.getTask().key.id) {
taskView.resetVisualProperties();
+ taskView.setStableAlpha(mContentAlpha);
}
}
if (mRunningTaskTileHidden) {
@@ -783,6 +785,7 @@
unloadVisibleTaskData();
setCurrentPage(0);
mDwbToastShown = false;
+ mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, 0);
}
public @Nullable TaskView getRunningTaskView() {
@@ -848,12 +851,14 @@
* is called. Also scrolls the view to this task.
*/
public void showCurrentTask(int runningTaskId) {
- if (getChildCount() == 0) {
+ if (getTaskView(runningTaskId) == null) {
+ boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
- addView(taskView);
- addView(mClearAllButton);
-
+ addView(taskView, 0);
+ if (wasEmpty) {
+ addView(mClearAllButton);
+ }
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(),
@@ -1687,6 +1692,9 @@
* @return How many pixels the running task is offset on the x-axis due to the current scrollX.
*/
public float getScrollOffset() {
+ if (getRunningTaskIndex() == -1) {
+ return 0;
+ }
int startScroll = getScrollForPage(getRunningTaskIndex());
int offsetX = startScroll - getScrollX();
offsetX *= getScaleX();
@@ -1733,4 +1741,14 @@
updateEnabledOverlays();
}
}
+
+ public int getLeftGestureMargin() {
+ final WindowInsets insets = getRootWindowInsets();
+ return Math.max(insets.getSystemGestureInsets().left, insets.getSystemWindowInsetLeft());
+ }
+
+ public int getRightGestureMargin() {
+ final WindowInsets insets = getRootWindowInsets();
+ return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight());
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index d55a520..7f1e898 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -410,4 +410,11 @@
return new ColorMatrixColorFilter(COLOR_MATRIX);
}
+
+ public Bitmap getThumbnail() {
+ if (mThumbnailData == null) {
+ return null;
+ }
+ return mThumbnailData.thumbnail;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index b26fdce..2211eb4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -192,9 +192,6 @@
super(context, attrs, defStyleAttr);
mActivity = BaseDraggingActivity.fromContext(context);
setOnClickListener((view) -> {
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "TaskView onClick");
- }
if (getTask() == null) {
return;
}
@@ -291,9 +288,6 @@
public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "launchTask");
- }
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (isRunningTask()) {
getRecentsView().finishRecentsAnimation(false /* toRecents */,
@@ -308,9 +302,6 @@
private void launchTaskInternal(boolean animate, boolean freezeTaskList,
Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TASK_TAG, "launchTaskInternal");
- }
if (mTask != null) {
final ActivityOptions opts;
if (animate) {
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 78f6ffa..a8e2956 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.Utilities.postAsyncCallback;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.systemui.shared.recents.utilities.Utilities
.postAtFrontOfQueueAsynchronously;
@@ -24,6 +24,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.TargetApi;
+import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -66,7 +67,7 @@
/**
* Called on the UI thread when the animation targets are received. The implementation must
- * call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run.
+ * call {@link AnimationResult#setAnimation} with the target animation to be run.
*/
@UiThread
public abstract void onCreateAnimation(
@@ -110,7 +111,7 @@
}
@UiThread
- public void setAnimation(AnimatorSet animation) {
+ public void setAnimation(AnimatorSet animation, Context context) {
if (mInitialized) {
throw new IllegalStateException("Animation already initialized");
}
@@ -134,7 +135,7 @@
// Because t=0 has the app icon in its original spot, we can skip the
// first frame and have the same movement one frame earlier.
- mAnimator.setCurrentPlayTime(SINGLE_FRAME_MS);
+ mAnimator.setCurrentPlayTime(getSingleFrameMs(context));
}
}
}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index c5c5323..b9ce1ce 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -85,7 +85,7 @@
register();
- Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
+ Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
context.startActivity(addToIntent(new Intent((intent))), options);
}
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index b60a017..991408c 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -219,7 +219,7 @@
anim.addListener(mForceInvisibleListener);
}
- result.setAnimation(anim);
+ result.setAnimation(anim, mLauncher);
}
};
@@ -822,7 +822,7 @@
}
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
- result.setAnimation(anim);
+ result.setAnimation(anim, mLauncher);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index f0204b9..174e49b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -19,16 +19,20 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
import android.util.FloatProperty;
import android.view.View;
import android.view.animation.Interpolator;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
@@ -36,8 +40,7 @@
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
-
-import androidx.annotation.NonNull;
+import com.android.launcher3.graphics.OverviewScrim;
/**
* State handler for recents view. Manages UI changes and animations for recents view based off the
@@ -67,6 +70,8 @@
mRecentsView.setTranslationX(translationX);
mRecentsView.setTranslationY(scaleAndTranslation.translationY);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
+ OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
+ SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
}
@Override
@@ -110,6 +115,9 @@
translateYInterpolator);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+ OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
+ setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
+ builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 6030cea..a55f36b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -51,7 +51,6 @@
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
-import com.android.systemui.shared.system.QuickStepContract;
/**
* Touch controller for handling various state transitions in portrait UI.
@@ -63,7 +62,7 @@
/**
* The progress at which all apps content will be fully visible when swiping up from overview.
*/
- private static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
+ protected static final float ALL_APPS_CONTENT_FADE_THRESHOLD = 0.08f;
/**
* The progress at which recents will begin fading out when swiping up from overview.
@@ -296,9 +295,13 @@
* @return true if the event is over the hotseat
*/
static boolean isTouchOverHotseat(Launcher launcher, MotionEvent ev) {
+ return (ev.getY() >= getHotseatTop(launcher));
+ }
+
+ public static int getHotseatTop(Launcher launcher) {
DeviceProfile dp = launcher.getDeviceProfile();
int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
- return (ev.getY() >= (launcher.getDragLayer().getHeight() - hotseatHeight));
+ return launcher.getDragLayer().getHeight() - hotseatHeight;
}
private static class InterpolatorWrapper implements Interpolator {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index fee1820..11a8043 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -17,17 +17,25 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import android.graphics.PointF;
import android.os.RemoteException;
import android.util.Log;
+import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import android.view.Window;
+import android.view.WindowManager;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.touch.TouchEventTranslator;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.RecentsModel;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -36,18 +44,29 @@
/**
* 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.
+ * Once the event delta mDownY passes the touch slop, the events start getting forwarded.
* All events are offset by initial Y value of the pointer.
*/
public class StatusBarTouchController implements TouchController {
private static final String TAG = "StatusBarController";
+ /**
+ * Window flag: Enable touches to slide out of a window into neighboring
+ * windows in mid-gesture instead of being captured for the duration of
+ * the gesture.
+ *
+ * This flag changes the behavior of touch focus for this window only.
+ * Touches can slide out of the window but they cannot necessarily slide
+ * back in (unless the other window with touch focus permits it).
+ */
+ private static final int FLAG_SLIPPERY = 0x20000000;
+
protected final Launcher mLauncher;
- protected final TouchEventTranslator mTranslator;
private final float mTouchSlop;
private ISystemUiProxy mSysUiProxy;
private int mLastAction;
+ private final SparseArray<PointF> mDownEvents;
/* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/
private boolean mCanIntercept;
@@ -56,7 +75,7 @@
mLauncher = l;
// Guard against TAPs by increasing the touch slop.
mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop();
- mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev));
+ mDownEvents = new SparseArray<>();
}
@Override
@@ -64,7 +83,6 @@
writer.println(prefix + "mCanIntercept:" + mCanIntercept);
writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction));
writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null));
-
}
private void dispatchTouchEvent(MotionEvent ev) {
@@ -81,26 +99,31 @@
@Override
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
+ int idx = ev.getActionIndex();
+ int pid = ev.getPointerId(idx);
if (action == ACTION_DOWN) {
mCanIntercept = canInterceptTouch(ev);
if (!mCanIntercept) {
return false;
}
- mTranslator.reset();
- mTranslator.setDownParameters(0, ev);
+ mDownEvents.put(pid, new PointF(ev.getX(), ev.getY()));
} else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- // Check!! should only set it only when threshold is not entered.
- mTranslator.setDownParameters(ev.getActionIndex(), ev);
+ // Check!! should only set it only when threshold is not entered.
+ mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx)));
}
if (!mCanIntercept) {
return false;
}
if (action == ACTION_MOVE) {
- float dy = ev.getY() - mTranslator.getDownY();
- float dx = ev.getX() - mTranslator.getDownX();
- if (dy > mTouchSlop && dy > Math.abs(dx)) {
- mTranslator.dispatchDownEvents(ev);
- mTranslator.processMotionEvent(ev);
+ float dy = ev.getY(idx) - mDownEvents.get(pid).y;
+ float dx = ev.getX(idx) - mDownEvents.get(pid).x;
+ // Currently input dispatcher will not do touch transfer if there are more than
+ // one touch pointer. Hence, even if slope passed, only set the slippery flag
+ // when there is single touch event. (context: InputDispatcher.cpp line 1445)
+ if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) {
+ ev.setAction(ACTION_DOWN);
+ dispatchTouchEvent(ev);
+ setWindowSlippery(true);
return true;
}
if (Math.abs(dx) > mTouchSlop) {
@@ -110,13 +133,31 @@
return false;
}
-
@Override
public final boolean onControllerTouchEvent(MotionEvent ev) {
- mTranslator.processMotionEvent(ev);
+ int action = ev.getAction();
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ dispatchTouchEvent(ev);
+ mLauncher.getUserEventDispatcher().logActionOnContainer(action == ACTION_UP ?
+ Touch.FLING : Touch.SWIPE, Direction.DOWN, ContainerType.WORKSPACE,
+ mLauncher.getWorkspace().getCurrentPage());
+ setWindowSlippery(false);
+ return true;
+ }
return true;
}
+ private void setWindowSlippery(boolean enable) {
+ Window w = mLauncher.getWindow();
+ WindowManager.LayoutParams wlp = w.getAttributes();
+ if (enable) {
+ wlp.flags |= FLAG_SLIPPERY;
+ } else {
+ wlp.flags &= ~FLAG_SLIPPERY;
+ }
+ w.setAttributes(wlp);
+ }
+
private boolean canInterceptTouch(MotionEvent ev) {
if (!mLauncher.isInState(LauncherState.NORMAL) ||
AbstractFloatingView.getTopOpenViewWithType(mLauncher,
@@ -132,4 +173,4 @@
mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy();
return mSysUiProxy != null;
}
-}
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index cd2c9cb..5c9c7d4 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -152,5 +152,15 @@
default void playAtomicAnimation(float velocity) {
// No-op
}
+
+ static RectF getDefaultWindowTargetRect(DeviceProfile dp) {
+ final int halfIconSize = dp.iconSizePx / 2;
+ final float targetCenterX = dp.availableWidthPx / 2f;
+ final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
+ // Fallback to animate to center of screen.
+ return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
+ targetCenterX + halfIconSize, targetCenterY + halfIconSize);
+ }
+
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 0738aff..88a4eb6 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -29,11 +29,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.util.SparseIntArray;
import com.android.systemui.shared.system.PackageManagerWrapper;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Class to keep track of the current overview component based off user preferences and app updates
@@ -53,22 +57,41 @@
}
};
private final Context mContext;
- private final ComponentName mMyHomeComponent;
+ private final Intent mCurrentHomeIntent;
+ private final Intent mMyHomeIntent;
+ private final Intent mFallbackIntent;
+ private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private String mUpdateRegisteredPackage;
private ActivityControlHelper mActivityControlHelper;
private Intent mOverviewIntent;
- private Intent mHomeIntent;
private int mSystemUiStateFlags;
private boolean mIsHomeAndOverviewSame;
+ private boolean mIsDefaultHome;
public OverviewComponentObserver(Context context) {
mContext = context;
- Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
+ mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
- .setPackage(mContext.getPackageName());
- ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
- mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
+ ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
+ ComponentName myHomeComponent =
+ new ComponentName(context.getPackageName(), info.activityInfo.name);
+ mMyHomeIntent.setComponent(myHomeComponent);
+ mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
+
+ ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
+ mFallbackIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_DEFAULT)
+ .setComponent(fallbackComponent)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
+ mFallbackIntent.getComponent(), 0 /* flags */);
+ mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
+ } catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
mContext.registerReceiver(mUserPreferenceChangeReceiver,
new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
@@ -92,17 +115,22 @@
ComponentName defaultHome = PackageManagerWrapper.getInstance()
.getHomeActivities(new ArrayList<>());
- final String overviewIntentCategory;
- ComponentName overviewComponent;
- mHomeIntent = null;
+ mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
- if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 &&
- (defaultHome == null || mMyHomeComponent.equals(defaultHome))) {
+ // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
+ // launcher made invisible become visible again before the new activity control helper
+ // becomes active.
+ if (mActivityControlHelper != null) {
+ mActivityControlHelper.onAssistantVisibilityChanged(0.f);
+ }
+
+ if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
+ && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- overviewComponent = mMyHomeComponent;
mActivityControlHelper = new LauncherActivityControllerHelper();
mIsHomeAndOverviewSame = true;
- overviewIntentCategory = Intent.CATEGORY_HOME;
+ mOverviewIntent = mMyHomeIntent;
+ mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
if (mUpdateRegisteredPackage != null) {
// Remove any update listener as we don't care about other packages.
@@ -111,14 +139,12 @@
}
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- overviewComponent = new ComponentName(mContext, RecentsActivity.class);
+
mActivityControlHelper = new FallbackActivityControllerHelper();
mIsHomeAndOverviewSame = false;
- overviewIntentCategory = Intent.CATEGORY_DEFAULT;
+ mOverviewIntent = mFallbackIntent;
+ mCurrentHomeIntent.setComponent(defaultHome);
- mHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setComponent(defaultHome);
// User's default home app can change as a result of package updates of this app (such
// as uninstalling the app or removing the "Launcher" feature in an update).
// Listen for package updates of this app (and remove any previously attached
@@ -138,14 +164,6 @@
ACTION_PACKAGE_REMOVED));
}
}
-
- mOverviewIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(overviewIntentCategory)
- .setComponent(overviewComponent)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (mHomeIntent == null) {
- mHomeIntent = mOverviewIntent;
- }
}
/**
@@ -161,6 +179,32 @@
}
/**
+ * @return {@code true} if the overview component is able to handle the configuration changes.
+ */
+ boolean canHandleConfigChanges(ComponentName component, int changes) {
+ final int orientationChange =
+ ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE;
+ if ((changes & orientationChange) == orientationChange) {
+ // This is just an approximate guess for simple orientation change because the changes
+ // may contain non-public bits (e.g. window configuration).
+ return true;
+ }
+
+ int configMask = mConfigChangesMap.get(component.hashCode());
+ return configMask != 0 && (~configMask & changes) == 0;
+ }
+
+ /**
+ * Get the intent for overview activity. It is used when lockscreen is shown and home was died
+ * in background, we still want to restart the one that will be used after unlock.
+ *
+ * @return the overview intent
+ */
+ Intent getOverviewIntentIgnoreSysUiState() {
+ return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
+ }
+
+ /**
* Get the current intent for going to the overview activity.
*
* @return the overview intent
@@ -173,7 +217,7 @@
* Get the current intent for going to the home activity.
*/
public Intent getHomeIntent() {
- return mHomeIntent;
+ return mCurrentHomeIntent;
}
/**
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 7bfa9a0..befeee0 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep;
-import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
@@ -23,29 +22,17 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
-import com.android.launcher3.Utilities;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
public class QuickstepProcessInitializer extends MainProcessInitializer {
private static final String TAG = "QuickstepProcessInitializer";
- private static final int HEAP_LIMIT_MB = 250;
public QuickstepProcessInitializer(Context context) { }
@Override
protected void init(Context context) {
- if (Utilities.IS_DEBUG_DEVICE) {
- try {
- // Trigger a heap dump if the PSS reaches beyond the target heap limit
- final ActivityManager am = context.getSystemService(ActivityManager.class);
- am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024);
- } catch (SecurityException e) {
- // Do nothing
- }
- }
-
// Workaround for b/120550382, an external app can cause the launcher process to start for
// a work profile user which we do not support. Disable the application immediately when we
// detect this to be the case.
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
deleted file mode 100644
index 8951363..0000000
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.quickstep;
-
-import android.content.Context;
-import android.os.Bundle;
-
-import com.android.launcher3.testing.TestInformationHandler;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.quickstep.util.LayoutUtils;
-
-public class QuickstepTestInformationHandler extends TestInformationHandler {
-
- public QuickstepTestInformationHandler(Context context) { }
-
- @Override
- public Bundle call(String method) {
- final Bundle response = new Bundle();
- switch (method) {
- case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
- final float swipeHeight =
- OverviewState.getDefaultSwipeHeight(mDeviceProfile);
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
- return response;
- }
-
- case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
- final float swipeHeight =
- LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile);
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
- return response;
- }
- }
-
- return super.call(method);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index f27ba85..e41dba9 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
+
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Context;
@@ -25,10 +27,7 @@
import com.android.launcher3.MainThreadExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.KeyguardManagerCompat;
-import com.android.systemui.shared.system.RecentTaskInfoCompat;
-import com.android.systemui.shared.system.TaskDescriptionCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.Collections;
@@ -43,7 +42,6 @@
private final KeyguardManagerCompat mKeyguardManager;
private final MainThreadExecutor mMainThreadExecutor;
- private final BackgroundExecutor mBgThreadExecutor;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -56,7 +54,6 @@
public RecentTasksList(Context context) {
mMainThreadExecutor = new MainThreadExecutor();
- mBgThreadExecutor = BackgroundExecutor.get();
mKeyguardManager = new KeyguardManagerCompat(context);
mChangeId = 1;
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
@@ -67,7 +64,7 @@
*/
public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) {
// Kick off task loading in the background
- mBgThreadExecutor.submit(() -> {
+ BACKGROUND_EXECUTOR.execute(() -> {
ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */);
mMainThreadExecutor.execute(() -> callback.accept(tasks));
});
@@ -87,13 +84,14 @@
: () -> callback.accept(copyOf(mTasks));
if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
- // The list is up to date, callback with the same list
- mMainThreadExecutor.execute(resultCallback);
+ // The list is up to date, send the callback on the next frame,
+ // so that requestID can be returned first.
+ mMainThreadExecutor.getHandler().post(resultCallback);
return requestLoadId;
}
// Kick off task loading in the background
- mBgThreadExecutor.submit(() -> {
+ BACKGROUND_EXECUTOR.execute(() -> {
ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly);
mMainThreadExecutor.execute(() -> {
@@ -121,12 +119,7 @@
@Override
public void onTaskRemoved(int taskId) {
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- if (mTasks.get(i).key.id == taskId) {
- mTasks.remove(i);
- return;
- }
- }
+ mTasks = loadTasksInBackground(Integer.MAX_VALUE, false);
}
@Override
@@ -166,15 +159,11 @@
int taskCount = rawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo rawTask = rawTasks.get(i);
- RecentTaskInfoCompat t = new RecentTaskInfoCompat(rawTask);
Task.TaskKey taskKey = new Task.TaskKey(rawTask);
Task task;
if (!loadKeysOnly) {
- ActivityManager.TaskDescription rawTd = t.getTaskDescription();
- TaskDescriptionCompat td = new TaskDescriptionCompat(rawTd);
- boolean isLocked = tmpLockedUsers.get(t.getUserId());
- task = new Task(taskKey, td.getPrimaryColor(), td.getBackgroundColor(),
- t.supportsSplitScreenMultiWindow(), isLocked, rawTd, t.getTopActivity());
+ boolean isLocked = tmpLockedUsers.get(taskKey.userId);
+ task = Task.from(taskKey, rawTask, isLocked);
} else {
task = new Task(taskKey);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
index 0822e61..f9d2f11 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
@@ -68,7 +68,7 @@
Context context, Handler handler, long duration) {
register();
- Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
+ Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
context.startActivity(intent, options);
}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index a7e6d74..4503a43 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -17,6 +17,7 @@
import android.animation.AnimatorSet;
import android.app.ActivityOptions;
+import android.content.Context;
import android.os.Handler;
import com.android.launcher3.LauncherAnimationRunner;
@@ -32,14 +33,14 @@
AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
- default ActivityOptions toActivityOptions(Handler handler, long duration) {
+ default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) {
@Override
public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
AnimationResult result) {
- result.setAnimation(createWindowAnimation(targetCompats));
+ result.setAnimation(createWindowAnimation(targetCompats), context);
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 63c8023..3747f9a 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -74,6 +74,9 @@
private int mMidAlpha;
private float mMidProgress;
+ // The progress at which the drag handle starts moving up with the shelf.
+ private float mDragHandleProgress;
+
private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
@@ -95,7 +98,7 @@
public ShelfScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
- mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
+ mMaxScrimAlpha = Math.round(OVERVIEW.getOverviewScrimAlpha(mLauncher) * 255);
mEndAlpha = Color.alpha(mEndScrim);
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
@@ -150,15 +153,16 @@
if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
mMidProgress = 1;
+ mDragHandleProgress = 1;
mMidAlpha = 0;
} else {
mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+ mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
Rect hotseatPadding = dp.getHotseatLayoutPadding();
int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
- hotseatPadding.bottom - hotseatPadding.top;
- float arrowTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
- mMidProgress = 1 - (arrowTop / mShiftRange);
-
+ float dragHandleTop = Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(dp));
+ mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
}
mTopOffset = dp.getInsets().top - mShelfOffset;
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
@@ -199,8 +203,6 @@
mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator));
mShelfColor = setColorAlphaBound(mEndScrim, alpha);
} else {
- mDragHandleOffset += mShiftRange * (mMidProgress - mProgress);
-
// Note that these ranges and interpolators are inverted because progress goes 1 to 0.
int alpha = Math.round(
Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha,
@@ -212,6 +214,10 @@
(float) 0, LINEAR));
mRemainingScreenColor = setColorAlphaBound(mScrimColor, remainingScrimAlpha);
}
+
+ if (mProgress < mDragHandleProgress) {
+ mDragHandleOffset += mShiftRange * (mDragHandleProgress - mProgress);
+ }
}
@Override
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index d9fcf4d..d0956d1 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -88,7 +88,7 @@
*/
@Test
public void testPredictionExistsInAllApps() {
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
mLauncher.pressHome().switchToAllApps();
// Dispatch an update
@@ -103,7 +103,7 @@
*/
@Test
public void testPredictionsDeferredUntilHome() {
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
sendPredictionUpdate(mSampleApp1, mSampleApp2);
mLauncher.pressHome().switchToAllApps();
waitForLauncherCondition("Predictions were not updated in loading state",
@@ -120,7 +120,7 @@
@Test
public void testPredictionsDisabled() {
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
sendPredictionUpdate();
mLauncher.pressHome().switchToAllApps();
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index 0c5a6f5..ec3d49a 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -51,7 +51,7 @@
mLauncher.pressHome();
final DigitalWellBeingToast toast = getToast();
- assertTrue("Toast is not visible", toast.hasLimit());
+ waitForLauncherCondition("Toast is not visible", launcher -> toast.hasLimit());
assertEquals("Toast text: ", "5 minutes left today", toast.getText());
// Unset time limit for app.
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 3b35c86..f27f400 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -27,6 +27,7 @@
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.util.Log;
import androidx.test.uiautomator.UiDevice;
@@ -80,6 +81,7 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ mLauncher.enableDebugTracing();
final Context context = getInstrumentation().getContext();
final int currentInteractionMode =
LauncherInstrumentation.getCurrentInteractionMode(context);
@@ -101,35 +103,55 @@
if (mode == THREE_BUTTON || mode == ALL) {
evaluateWithThreeButtons();
}
+ } catch (Exception e) {
+ Log.e(TAG, "Exception", e);
+ throw e;
} finally {
- setActiveOverlay(prevOverlayPkg, originalMode);
+ Assert.assertTrue(setActiveOverlay(prevOverlayPkg, originalMode));
}
- }
-
- public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
- base.evaluate();
+ mLauncher.disableDebugTracing();
}
private void evaluateWithThreeButtons() throws Throwable {
- setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
- LauncherInstrumentation.NavigationModel.THREE_BUTTON);
- evaluateWithoutChangingSetting(base);
+ if (setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
+ LauncherInstrumentation.NavigationModel.THREE_BUTTON)) {
+ base.evaluate();
+ }
}
private void evaluateWithTwoButtons() throws Throwable {
- setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
- LauncherInstrumentation.NavigationModel.TWO_BUTTON);
- base.evaluate();
+ if (setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
+ LauncherInstrumentation.NavigationModel.TWO_BUTTON)) {
+ base.evaluate();
+ }
}
private void evaluateWithZeroButtons() throws Throwable {
- setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
- LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
- base.evaluate();
+ if (setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
+ LauncherInstrumentation.NavigationModel.ZERO_BUTTON)) {
+ base.evaluate();
+ }
}
- private void setActiveOverlay(String overlayPackage,
+ private boolean packageExists(String packageName) {
+ try {
+ PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ if (pm.getApplicationInfo(packageName, 0 /* flags */) == null) {
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean setActiveOverlay(String overlayPackage,
LauncherInstrumentation.NavigationModel expectedMode) throws Exception {
+ if (!packageExists(overlayPackage)) {
+ Log.d(TAG, "setActiveOverlay: " + overlayPackage + " pkg does not exist");
+ return false;
+ }
+
setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
@@ -173,6 +195,7 @@
Assert.assertTrue("Switching nav mode: " + error, error == null);
Thread.sleep(5000);
+ return true;
}
private void setOverlayPackageEnabled(String overlayPackage, boolean enable)
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 2111e2c..c5b560c 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -25,6 +25,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.RaceConditionReproducer;
import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
@@ -79,6 +80,8 @@
@Test
@NavigationModeSwitch
public void testStressPressHome() {
+ if (LauncherInstrumentation.isAvd()) return; // b/136278866
+
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
closeLauncherActivity();
@@ -91,6 +94,8 @@
@Test
@NavigationModeSwitch
public void testStressSwipeToOverview() {
+ if (LauncherInstrumentation.isAvd()) return; // b/136278866
+
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
closeLauncherActivity();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 9e3bf2f..885fdbf 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -17,7 +17,6 @@
package com.android.quickstep;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -208,7 +207,7 @@
@Test
@NavigationModeSwitch
-// @PortraitLandscape
+ @PortraitLandscape
public void testBackground() throws Exception {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
final Background background = mLauncher.getBackground();
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 881f65d..339aef5 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -104,6 +104,7 @@
</style>
<style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
+ <item name="android:colorControlHighlight">#75212121</item>
<item name="allAppsInterimScrimAlpha">25</item>
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index d949141..e3ef5d6 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -11,6 +11,7 @@
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.provider.RestoreDbTask;
@@ -18,6 +19,8 @@
import androidx.annotation.WorkerThread;
+import static android.os.Process.myUserHandle;
+
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
private static final String TAG = "AWRestoredReceiver";
@@ -77,9 +80,14 @@
state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
}
- String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+ // b/135926478: Work profile widget restore is broken in platform. This forces us to
+ // recreate the widget during loading with the correct host provider.
+ long mainProfileId = UserManagerCompat.getInstance(context)
+ .getSerialNumberForUser(myUserHandle());
+ String oldWidgetId = Integer.toString(oldWidgetIds[i]);
int result = new ContentWriter(context, new ContentWriter.CommitParams(
- "appWidgetId=? and (restored & 1) = 1", widgetIdParams))
+ "appWidgetId=? and (restored & 1) = 1 and profileId=?",
+ new String[] { oldWidgetId, Long.toString(mainProfileId) }))
.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
.put(LauncherSettings.Favorites.RESTORED, state)
.commit();
@@ -87,7 +95,7 @@
if (result == 0) {
Cursor cursor = cr.query(Favorites.CONTENT_URI,
new String[] {Favorites.APPWIDGET_ID},
- "appWidgetId=?", widgetIdParams, null);
+ "appWidgetId=?", new String[] { oldWidgetId }, null);
try {
if (!cursor.moveToFirst()) {
// The widget no long exists.
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f69b172..f0b3afd 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -135,10 +135,6 @@
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
@Nullable String sourceContainer) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "startActivitySafely 1");
- }
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
@@ -162,10 +158,6 @@
startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "startActivitySafely 2");
- }
startActivity(intent, optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
@@ -178,7 +170,7 @@
getUserEventDispatcher().logAppLaunch(v, intent);
getStatsLogManager().logAppLaunch(v, intent);
return true;
- } catch (ActivityNotFoundException|SecurityException e) {
+ } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 1619e36..22c69f5 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -319,9 +319,6 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_START_TAG, "BubbleTextView.onTouchEvent " + event);
- }
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
boolean result = super.onTouchEvent(event);
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index c967a96..6c5bc40 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -108,17 +108,20 @@
// For the second frame, if the first frame took more than 16ms,
// adjust the start time and pretend it took only 16ms anyway. This
// prevents a large jump in the animation due to an expensive first frame
- } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
- !mAdjustedSecondFrameTime &&
- currentTime > mStartTime + SINGLE_FRAME_MS &&
- currentPlayTime > SINGLE_FRAME_MS) {
- animation.setCurrentPlayTime(SINGLE_FRAME_MS);
- mAdjustedSecondFrameTime = true;
} else {
- if (frameNum > 1) {
- mRootView.post(() -> animation.removeUpdateListener(this));
+ int singleFrameMS = getSingleFrameMs(mRootView.getContext());
+ if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
+ !mAdjustedSecondFrameTime &&
+ currentTime > mStartTime + singleFrameMS &&
+ currentPlayTime > singleFrameMS) {
+ animation.setCurrentPlayTime(singleFrameMS);
+ mAdjustedSecondFrameTime = true;
+ } else {
+ if (frameNum > 1) {
+ mRootView.post(() -> animation.removeUpdateListener(this));
+ }
+ if (DEBUG) print(animation);
}
- if (DEBUG) print(animation);
}
mHandlingOnAnimationUpdate = false;
} else {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index bc3aa7e..5b38261 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -890,9 +890,6 @@
@Override
protected void onStart() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_OVERVIEW_EVENT_TAG, "Launcher.onStart");
- }
RaceConditionTracker.onEvent(ON_START_EVT, ENTER);
super.onStart();
if (mLauncherCallbacks != null) {
@@ -1809,11 +1806,6 @@
public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
@Nullable String sourceContainer) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "startActivitySafely outer");
- }
-
if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
@@ -1904,6 +1896,10 @@
if (mPendingExecutor != null) {
mPendingExecutor.markCompleted();
mPendingExecutor = null;
+
+ // We might have set this flag previously and forgot to clear it.
+ mAppsView.getAppsStore()
+ .disableDeferUpdatesSilently(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
}
}
@@ -2257,9 +2253,7 @@
@Override
public void executeOnNextDraw(ViewOnDrawExecutor executor) {
- if (mPendingExecutor != null) {
- mPendingExecutor.markCompleted();
- }
+ clearPendingBinds();
mPendingExecutor = executor;
if (!isInState(ALL_APPS)) {
mAppsView.getAppsStore().enableDeferUpdates(AllAppsStore.DEFER_UPDATES_NEXT_DRAW);
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index dcfd272..6e2626b 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -243,6 +243,10 @@
return 0;
}
+ public float getOverviewScrimAlpha(Launcher launcher) {
+ return 0;
+ }
+
public String getDescription(Launcher launcher) {
return launcher.getWorkspace().getCurrentPageDescription();
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 2c8c208..d66e581 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -403,10 +403,6 @@
}
private void onStateTransitionStart(LauncherState state) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "onStateTransitionStart");
- }
if (mState != state) {
mState.onStateDisabled(mLauncher);
}
@@ -429,11 +425,6 @@
// 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;
}
@@ -580,10 +571,6 @@
private final AnimatorSet mAnim;
public StartAnimRunnable(AnimatorSet anim) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "StartAnimRunnable");
- }
mAnim = anim;
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 2eeb132..d2b8d4e 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.Utilities.shouldDisableGestures;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
@@ -48,6 +47,7 @@
import android.widget.ScrollView;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.touch.OverScroll;
@@ -368,6 +368,7 @@
*/
protected void onPageEndTransition() {
mWasInOverscroll = false;
+ AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
}
protected int getUnboundedScrollX() {
@@ -847,7 +848,7 @@
*/
// Skip touch handling if there are no pages to swipe
- if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
+ if (getChildCount() <= 0) return false;
acquireVelocityTrackerAndAddMovement(ev);
@@ -1095,7 +1096,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
// Skip touch handling if there are no pages to swipe
- if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false;
+ if (getChildCount() <= 0) return false;
acquireVelocityTrackerAndAddMovement(ev);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 65aa3a7..fc5cd8a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -92,8 +94,6 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
-
/**
* Various utilities shared amongst the Launcher's classes.
*/
@@ -128,16 +128,6 @@
public static final int EDGE_NAV_BAR = 1 << 8;
/**
- * Set on a motion event do disallow any gestures and only handle touch.
- * See {@link MotionEvent#setEdgeFlags(int)}.
- */
- public static final int FLAG_NO_GESTURES = 1 << 9;
-
- public static boolean shouldDisableGestures(MotionEvent ev) {
- return (ev.getEdgeFlags() & FLAG_NO_GESTURES) == FLAG_NO_GESTURES;
- }
-
- /**
* Indicates if the device has a debug build. Should only be used to store additional info or
* add extra logging and not for changing the app behavior.
*/
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 9f846bb..269a591 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -81,10 +81,10 @@
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.graphics.RotationMode;
+import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -370,10 +370,6 @@
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "onDragStart 1");
- }
if (ENFORCE_DRAG_EVENT_ORDER) {
enforceDragParity("onDragStart", 0, 0);
}
@@ -424,10 +420,6 @@
}
// Always enter the spring loaded mode
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "onDragStart 2");
- }
mLauncher.getStateManager().goToState(SPRING_LOADED);
}
@@ -1061,8 +1053,11 @@
// Not announcing the overlay page for accessibility since it announces itself.
} else if (Float.compare(scroll, 0f) == 0) {
if (mOverlayShown) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+ UserEventDispatcher ued = mLauncher.getUserEventDispatcher();
+ if (!ued.isPreviousHomeGesture()) {
+ mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+ }
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
// accessibility since default announcements were disabled while in overscroll
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index ea9b077..4a2109e 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -50,6 +50,7 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiValueAlpha;
@@ -297,7 +298,11 @@
@Override
public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
- // This is filled in {@link AllAppsRecyclerView}
+ if (getApps().hasFilter()) {
+ targetParent.containerType = ContainerType.SEARCHRESULT;
+ } else {
+ targetParent.containerType = ContainerType.ALLAPPS;
+ }
}
@Override
@@ -626,9 +631,6 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_START_TAG, "AllAppsContainerView.dispatchTouchEvent " + ev);
- }
final boolean result = super.dispatchTouchEvent(ev);
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 160042e..267363f 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -80,6 +80,10 @@
}
}
+ public void disableDeferUpdatesSilently(int flag) {
+ mDeferUpdatesFlags &= ~flag;
+ }
+
public int getDeferUpdatesFlags() {
return mDeferUpdatesFlags;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4683893..5b3beec 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -7,6 +7,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
@@ -168,10 +169,6 @@
@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);
@@ -212,13 +209,14 @@
PropertySetter setter = config == null ? NO_ANIM_PROPERTY_SETTER
: config.getPropertySetter(builder);
boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
- boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0;
+ boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
- setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, allAppsFade);
- mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter,
- allAppsFade);
+ Interpolator headerFade = builder.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
+ setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
+ setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
+ mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasAllAppsContent,
+ setter, headerFade, allAppsFade);
mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 2ad92e1..1369441 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -301,11 +301,6 @@
}
private void refreshRecyclerView() {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "refreshRecyclerView @ " + android.util.Log.getStackTraceString(
- new Throwable()));
- }
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 922e4f1..f899587 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -46,8 +46,8 @@
*/
boolean hasVisibleContent();
- void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
- PropertySetter setter, Interpolator fadeInterpolator);
+ void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+ PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade);
/**
* Scrolls the content vertically.
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 66dced9..42a0eee 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -27,6 +27,10 @@
import android.view.animation.Interpolator;
import android.widget.LinearLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
@@ -40,10 +44,6 @@
import java.util.ArrayList;
import java.util.Map;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
-
public class FloatingHeaderView extends LinearLayout implements
ValueAnimator.AnimatorUpdateListener, PluginListener<AllAppsRow>, Insettable,
OnHeightUpdatedListener {
@@ -363,14 +363,14 @@
p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
}
- public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter,
- Interpolator fadeInterpolator) {
+ public void setContentVisibility(boolean hasHeader, boolean hasAllAppsContent,
+ PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
for (FloatingHeaderRow row : mAllRows) {
- row.setContentVisibility(hasHeader, hasContent, setter, fadeInterpolator);
+ row.setContentVisibility(hasHeader, hasAllAppsContent, setter, headerFade, allAppsFade);
}
- allowTouchForwarding(hasContent);
- setter.setFloat(mTabLayout, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+ allowTouchForwarding(hasAllAppsContent);
+ setter.setFloat(mTabLayout, ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
}
protected void allowTouchForwarding(boolean allow) {
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index b283ff4..535ef54 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -64,10 +64,10 @@
}
@Override
- public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
- PropertySetter setter, Interpolator fadeInterpolator) {
+ public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
+ PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
// Don't use setViewAlpha as we want to control the visibility ourselves.
- setter.setFloat(mView, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+ setter.setFloat(mView, ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
}
@Override
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index 52a896e..cd30dea 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -39,6 +39,8 @@
public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
public static final int ANIM_OVERVIEW_FADE = 9;
public static final int ANIM_ALL_APPS_FADE = 10;
+ public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
+ public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
public static final int FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0;
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index b169cb8..c45cd85 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -16,8 +16,9 @@
package com.android.launcher3.anim;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import android.content.Context;
import android.graphics.Path;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
@@ -144,7 +145,8 @@
public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
if (upperBound <= lowerBound) {
- throw new IllegalArgumentException("lowerBound must be less than upperBound");
+ throw new IllegalArgumentException(String.format(
+ "lowerBound (%f) must be less than upperBound (%f)", lowerBound, upperBound));
}
return t -> {
if (t < lowerBound) {
@@ -187,13 +189,13 @@
* @param totalDistancePx The distance against which progress is calculated.
*/
public OvershootParams(float startProgress, float overshootPastProgress,
- float endProgress, float velocityPxPerMs, int totalDistancePx) {
+ float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) {
velocityPxPerMs = Math.abs(velocityPxPerMs);
start = startProgress;
int startPx = (int) (start * totalDistancePx);
// Overshoot by about half a frame.
float overshootBy = OVERSHOOT_FACTOR * velocityPxPerMs *
- SINGLE_FRAME_MS / totalDistancePx / 2;
+ getSingleFrameMs(context) / totalDistancePx / 2;
overshootBy = Utilities.boundToRange(overshootBy, 0.02f, 0.15f);
end = overshootPastProgress + overshootBy;
int endPx = (int) (end * totalDistancePx);
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index 395fed2..91a3106 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -96,7 +96,10 @@
}
});
- mSpring.addUpdateListener((animation, value, velocity) -> mSpringEnded = false);
+ mSpring.addUpdateListener((animation, value, velocity) -> {
+ mSpringEnded = false;
+ mEnded = false;
+ });
mSpring.addEndListener((animation, canceled, value, velocity) -> {
mSpringEnded = true;
tryEnding();
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 43ae651..81c95cb 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -53,9 +53,6 @@
}
public static void sendStateEventToTest(Context context, int stateOrdinal) {
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG, "sendStateEventToTest");
- }
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 7dad7e9..4f4d641 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInstaller;
+import android.os.UserHandle;
import java.util.HashMap;
import java.util.List;
@@ -48,6 +49,11 @@
*/
public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
+ /**
+ * @return an active SessionInfo for {@param pkg} or null if none exists.
+ */
+ public abstract PackageInstaller.SessionInfo getActiveSessionInfo(UserHandle user, String pkg);
+
public abstract void onStop();
public static final class PackageInstallInfo {
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index a34ca50..8a5eabc 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -64,9 +64,9 @@
@Override
public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
HashMap<String, SessionInfo> activePackages = new HashMap<>();
- UserHandle user = Process.myUserHandle();
+ UserHandle primaryUser = Process.myUserHandle();
for (SessionInfo info : getAllVerifiedSessions()) {
- addSessionInfoToCache(info, user);
+ addSessionInfoToCache(info, Utilities.ATLEAST_Q ? info.getUser() : primaryUser);
if (info.getAppPackageName() != null) {
activePackages.put(info.getAppPackageName(), info);
mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
@@ -75,6 +75,19 @@
return activePackages;
}
+ public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
+ for (SessionInfo info : getAllVerifiedSessions()) {
+ boolean match = pkg.equals(info.getAppPackageName());
+ if (Utilities.ATLEAST_Q && !user.equals(info.getUser())) {
+ match = false;
+ }
+ if (match) {
+ return info;
+ }
+ }
+ return null;
+ }
+
@Thunk void addSessionInfoToCache(SessionInfo info, UserHandle user) {
String packageName = info.getAppPackageName();
if (packageName != null) {
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 45639e0..54efcb7 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -105,7 +105,7 @@
"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", false,
+ "ENABLE_HINTS_IN_OVERVIEW", true,
"Show chip hints and gleams on the overview screen");
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index a72089d..a2dcbf8 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -153,16 +153,6 @@
PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
img.getBitmap().getWidth(), img.getWidth());
- Intent homeIntent = listener.addToIntent(
- new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(getPackageName())
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-
- listener.initWhenReady();
- startActivity(homeIntent,
- ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
- mFinishOnPause = true;
// Start a system drag and drop. We use a transparent bitmap as preview for system drag
// as the preview is handled internally by launcher.
@@ -179,6 +169,18 @@
outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
}
}, null, View.DRAG_FLAG_GLOBAL);
+
+
+ Intent homeIntent = listener.addToIntent(
+ new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(getPackageName())
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+
+ listener.initWhenReady();
+ startActivity(homeIntent,
+ ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+ mFinishOnPause = true;
return false;
}
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index c719c1c..1b08723 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -137,9 +137,6 @@
@Override
public boolean shouldStartDrag(double distanceDragged) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_DRAG_TAG, "BIDL.shouldStartDrag");
- }
// Stay in pre-drag mode, if workspace is locked.
return !mLauncher.isWorkspaceLocked();
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 72a1abb..d32dd2e 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -474,10 +474,6 @@
}
private void handleMoveEvent(int x, int y) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "handleMoveEvent 1");
- }
mDragObject.dragView.move(x, y);
// Drop on someone?
@@ -492,22 +488,8 @@
mLastTouch[0] = x;
mLastTouch[1] = y;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_DRAG_TAG,
- "handleMoveEvent Conditions " +
- mIsInPreDrag + ", " +
- (mIsInPreDrag && mOptions.preDragCondition != null) + ", " +
- (mIsInPreDrag && mOptions.preDragCondition != null
- && mOptions.preDragCondition.shouldStartDrag(
- mDistanceSinceScroll)));
- }
-
if (mIsInPreDrag && mOptions.preDragCondition != null
&& mOptions.preDragCondition.shouldStartDrag(mDistanceSinceScroll)) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "handleMoveEvent 2");
- }
callOnDragStart();
}
}
@@ -545,10 +527,6 @@
* Call this from a drag source view.
*/
public boolean onControllerTouchEvent(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "onControllerTouchEvent");
- }
if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
return false;
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6ba015b..b59164a 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -48,12 +48,13 @@
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.uioverrides.UiFactory;
@@ -92,7 +93,8 @@
// Related to adjacent page hints
private final ViewGroupFocusHelper mFocusIndicatorHelper;
- private final WorkspaceAndHotseatScrim mScrim;
+ private final WorkspaceAndHotseatScrim mWorkspaceScrim;
+ private final OverviewScrim mOverviewScrim;
/**
* Used to create a new DragLayer from XML.
@@ -108,12 +110,13 @@
setChildrenDrawingOrderEnabled(true);
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
- mScrim = new WorkspaceAndHotseatScrim(this);
+ mWorkspaceScrim = new WorkspaceAndHotseatScrim(this);
+ mOverviewScrim = new OverviewScrim(this);
}
public void setup(DragController dragController, Workspace workspace) {
mDragController = dragController;
- mScrim.setWorkspace(workspace);
+ mWorkspaceScrim.setWorkspace(workspace);
recreateControllers();
}
@@ -529,25 +532,39 @@
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the background below children.
- mScrim.draw(canvas);
+ mWorkspaceScrim.draw(canvas);
+ mOverviewScrim.updateCurrentScrimmedView(this);
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
}
@Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (child == mOverviewScrim.getScrimmedView()) {
+ mOverviewScrim.draw(canvas);
+ }
+ return super.drawChild(canvas, child, drawingTime);
+ }
+
+ @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mScrim.setSize(w, h);
+ mWorkspaceScrim.setSize(w, h);
}
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- mScrim.onInsetsChanged(insets);
+ mWorkspaceScrim.onInsetsChanged(insets);
+ mOverviewScrim.onInsetsChanged(insets);
}
public WorkspaceAndHotseatScrim getScrim() {
- return mScrim;
+ return mWorkspaceScrim;
+ }
+
+ public OverviewScrim getOverviewScrim() {
+ return mOverviewScrim;
}
@Override
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 0c5a1fc..d8a1f99 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -28,6 +28,8 @@
import android.os.Build;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
@@ -66,15 +68,19 @@
return mBadge;
}
- public static FolderAdaptiveIcon createFolderAdaptiveIcon(
+ public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
Launcher launcher, int folderId, Point dragViewSize) {
Preconditions.assertNonUiThread();
int margin = launcher.getResources()
.getDimensionPixelSize(R.dimen.blur_size_medium_outline);
// Allocate various bitmaps on the background thread, because why not!
- final Bitmap badge = Bitmap.createBitmap(
- dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
+ int width = dragViewSize.x - margin;
+ int height = dragViewSize.y - margin;
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+ final Bitmap badge = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Create the actual drawable on the UI thread to avoid race conditions with
// FolderIcon draw pass
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 7eb4015..9263a2a 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -29,6 +29,7 @@
import android.view.View;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
@@ -87,6 +88,9 @@
Rect bounds = getDrawableBounds(d);
destCanvas.translate(blurSizeOutline / 2 - bounds.left,
blurSizeOutline / 2 - bounds.top);
+ if (d instanceof FastBitmapDrawable) {
+ ((FastBitmapDrawable) d).setScale(1);
+ }
d.draw(destCanvas);
} else {
final Rect clipRect = mTempRect;
diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java
new file mode 100644
index 0000000..d707403
--- /dev/null
+++ b/src/com/android/launcher3/graphics/OverviewScrim.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher3.graphics;
+
+import static android.view.View.VISIBLE;
+
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * View scrim which draws behind overview (recent apps).
+ */
+public class OverviewScrim extends Scrim {
+
+ private @NonNull View mStableScrimmedView;
+ // Might be higher up if mStableScrimmedView is invisible.
+ private @Nullable View mCurrentScrimmedView;
+
+ public OverviewScrim(View view) {
+ super(view);
+ mStableScrimmedView = mCurrentScrimmedView = mLauncher.getOverviewPanel();
+
+ onExtractedColorsChanged(mWallpaperColorInfo);
+ }
+
+ public void onInsetsChanged(Rect insets) {
+ mStableScrimmedView = (OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0
+ ? mLauncher.getHotseat()
+ : mLauncher.getOverviewPanel();
+ }
+
+ public void updateCurrentScrimmedView(ViewGroup root) {
+ // Find the lowest view that is at or above the view we want to show the scrim behind.
+ mCurrentScrimmedView = mStableScrimmedView;
+ int currentIndex = root.indexOfChild(mCurrentScrimmedView);
+ final int childCount = root.getChildCount();
+ while (mCurrentScrimmedView.getVisibility() != VISIBLE && currentIndex < childCount) {
+ currentIndex++;
+ mCurrentScrimmedView = root.getChildAt(currentIndex);
+ }
+ }
+
+ /**
+ * @return The view to draw the scrim behind.
+ */
+ public View getScrimmedView() {
+ return mCurrentScrimmedView;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/Scrim.java b/src/com/android/launcher3/graphics/Scrim.java
new file mode 100644
index 0000000..5c14f8d
--- /dev/null
+++ b/src/com/android/launcher3/graphics/Scrim.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher3.graphics;
+
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.graphics.Canvas;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.uioverrides.WallpaperColorInfo;
+
+/**
+ * Contains general scrim properties such as wallpaper-extracted color that subclasses can use.
+ */
+public class Scrim implements View.OnAttachStateChangeListener,
+ WallpaperColorInfo.OnChangeListener {
+
+ public static Property<Scrim, Float> SCRIM_PROGRESS =
+ new Property<Scrim, Float>(Float.TYPE, "scrimProgress") {
+ @Override
+ public Float get(Scrim scrim) {
+ return scrim.mScrimProgress;
+ }
+
+ @Override
+ public void set(Scrim scrim, Float value) {
+ scrim.setScrimProgress(value);
+ }
+ };
+
+ protected final Launcher mLauncher;
+ protected final WallpaperColorInfo mWallpaperColorInfo;
+ protected final View mRoot;
+
+ protected float mScrimProgress;
+ protected int mScrimColor;
+ protected int mScrimAlpha = 0;
+
+ public Scrim(View view) {
+ mRoot = view;
+ mLauncher = Launcher.getLauncher(view.getContext());
+ mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
+
+ view.addOnAttachStateChangeListener(this);
+ }
+
+ public void draw(Canvas canvas) {
+ canvas.drawColor(setColorAlphaBound(mScrimColor, mScrimAlpha));
+ }
+
+ private void setScrimProgress(float progress) {
+ if (mScrimProgress != progress) {
+ mScrimProgress = progress;
+ mScrimAlpha = Math.round(255 * mScrimProgress);
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ mWallpaperColorInfo.addOnChangeListener(this);
+ onExtractedColorsChanged(mWallpaperColorInfo);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ mWallpaperColorInfo.removeOnChangeListener(this);
+ }
+
+ @Override
+ public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+ mScrimColor = wallpaperColorInfo.getMainColor();
+ if (mScrimAlpha > 0) {
+ invalidate();
+ }
+ }
+
+ public void invalidate() {
+ mRoot.invalidate();
+ }
+}
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index c0aa75f..6740fa1 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -40,34 +40,19 @@
import android.util.Property;
import android.view.View;
+import androidx.core.graphics.ColorUtils;
+
import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Workspace;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.Themes;
-import androidx.core.graphics.ColorUtils;
-
/**
* View scrim which draws behind hotseat and workspace
*/
-public class WorkspaceAndHotseatScrim implements
- View.OnAttachStateChangeListener, WallpaperColorInfo.OnChangeListener {
-
- public static Property<WorkspaceAndHotseatScrim, Float> SCRIM_PROGRESS =
- new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "scrimProgress") {
- @Override
- public Float get(WorkspaceAndHotseatScrim scrim) {
- return scrim.mScrimProgress;
- }
-
- @Override
- public void set(WorkspaceAndHotseatScrim scrim, Float value) {
- scrim.setScrimProgress(value);
- }
- };
+public class WorkspaceAndHotseatScrim extends Scrim {
public static Property<WorkspaceAndHotseatScrim, Float> SYSUI_PROGRESS =
new Property<WorkspaceAndHotseatScrim, Float>(Float.TYPE, "sysUiProgress") {
@@ -117,9 +102,6 @@
private static final int ALPHA_MASK_WIDTH_DP = 2;
private final Rect mHighlightRect = new Rect();
- private final Launcher mLauncher;
- private final WallpaperColorInfo mWallpaperColorInfo;
- private final View mRoot;
private Workspace mWorkspace;
@@ -132,11 +114,6 @@
private final Drawable mTopScrim;
- private int mFullScrimColor;
-
- private float mScrimProgress;
- private int mScrimAlpha = 0;
-
private float mSysUiProgress = 1;
private boolean mHideSysUiScrim;
@@ -144,9 +121,7 @@
private float mSysUiAnimMultiplier = 1;
public WorkspaceAndHotseatScrim(View view) {
- mRoot = view;
- mLauncher = Launcher.getLauncher(view.getContext());
- mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
+ super(view);
mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
view.getResources().getDisplayMetrics());
@@ -154,7 +129,6 @@
mBottomMask = mTopScrim == null ? null : createDitheredAlphaMask();
mHideSysUiScrim = mTopScrim == null;
- view.addOnAttachStateChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
}
@@ -176,7 +150,7 @@
canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
}
- canvas.drawColor(setColorAlphaBound(mFullScrimColor, mScrimAlpha));
+ super.draw(canvas);
canvas.restore();
}
@@ -190,11 +164,8 @@
mSysUiAnimMultiplier = 0;
reapplySysUiAlphaNoInvalidate();
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, 1);
- anim.setAutoCancel(true);
- anim.setDuration(600);
- anim.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
- anim.start();
+ animateToSysuiMultiplier(1, 600,
+ mLauncher.getWindow().getTransitionBackgroundFadeDuration());
mAnimateScrimOnNextDraw = false;
}
@@ -207,24 +178,24 @@
}
}
+ public void animateToSysuiMultiplier(float toMultiplier, long duration,
+ long startDelay) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, toMultiplier);
+ anim.setAutoCancel(true);
+ anim.setDuration(duration);
+ anim.setStartDelay(startDelay);
+ anim.start();
+ }
+
public void onInsetsChanged(Rect insets) {
mDrawTopScrim = mTopScrim != null && insets.top > 0;
mDrawBottomScrim = mBottomMask != null &&
!mLauncher.getDeviceProfile().isVerticalBarLayout();
}
- private void setScrimProgress(float progress) {
- if (mScrimProgress != progress) {
- mScrimProgress = progress;
- mScrimAlpha = Math.round(255 * mScrimProgress);
- invalidate();
- }
- }
-
@Override
public void onViewAttachedToWindow(View view) {
- mWallpaperColorInfo.addOnChangeListener(this);
- onExtractedColorsChanged(mWallpaperColorInfo);
+ super.onViewAttachedToWindow(view);
if (mTopScrim != null) {
IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
@@ -235,7 +206,7 @@
@Override
public void onViewDetachedFromWindow(View view) {
- mWallpaperColorInfo.removeOnChangeListener(this);
+ super.onViewDetachedFromWindow(view);
if (mTopScrim != null) {
mRoot.getContext().unregisterReceiver(mReceiver);
}
@@ -248,10 +219,7 @@
mBottomMaskPaint.setColor(ColorUtils.compositeColors(DARK_SCRIM_COLOR,
wallpaperColorInfo.getMainColor()));
reapplySysUiAlpha();
- mFullScrimColor = wallpaperColorInfo.getMainColor();
- if (mScrimAlpha > 0) {
- invalidate();
- }
+ super.onExtractedColorsChanged(wallpaperColorInfo);
}
public void setSize(int w, int h) {
@@ -291,10 +259,6 @@
}
}
- public void invalidate() {
- mRoot.invalidate();
- }
-
public Bitmap createDitheredAlphaMask() {
DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics();
int width = ResourceUtils.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
index 647f255..b02a050 100644
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ b/src/com/android/launcher3/logging/StatsLogUtils.java
@@ -1,9 +1,12 @@
package com.android.launcher3.logging;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.DEFAULT_CONTAINERTYPE;
+
import android.view.View;
import android.view.ViewParent;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import androidx.annotation.Nullable;
@@ -64,4 +67,20 @@
}
return null;
}
+
+ public static int getContainerTypeFromState(int state) {
+ int containerType = DEFAULT_CONTAINERTYPE;
+ switch (state) {
+ case StatsLogUtils.LAUNCHER_STATE_ALLAPPS:
+ containerType = ContainerType.ALLAPPS;
+ break;
+ case StatsLogUtils.LAUNCHER_STATE_HOME:
+ containerType = ContainerType.WORKSPACE;
+ break;
+ case StatsLogUtils.LAUNCHER_STATE_OVERVIEW:
+ containerType = ContainerType.OVERVIEW;
+ break;
+ }
+ return containerType;
+ }
}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index bd785a1..d81020e 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -115,6 +115,7 @@
protected InstantAppResolver mInstantAppResolver;
private boolean mAppOrTaskLaunch;
private UserEventDelegate mDelegate;
+ private boolean mPreviousHomeGesture;
// APP_ICON SHORTCUT WIDGET
// --------------------------------------------------------------
@@ -399,11 +400,22 @@
mElapsedContainerMillis = SystemClock.uptimeMillis();
}
+ public final void setPreviousHomeGesture(boolean homeGesture) {
+ mPreviousHomeGesture = homeGesture;
+ }
+
+ public final boolean isPreviousHomeGesture() {
+ return mPreviousHomeGesture;
+ }
+
public final void resetActionDurationMillis() {
mActionDurationMillis = SystemClock.uptimeMillis();
}
public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
+ if (mPreviousHomeGesture) {
+ mPreviousHomeGesture = false;
+ }
mAppOrTaskLaunch = false;
ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
@@ -426,6 +438,7 @@
ev.actionDurationMillis);
log += "\n\n";
Log.d(TAG, log);
+ return;
}
private static String getTargetsStr(Target[] targets) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 9719a18..25d9f79 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -449,11 +449,6 @@
@Override
public boolean shouldStartDrag(double distanceDragged) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_DRAG_TAG,
- "createPreDragCondition().shouldStartDrag " + distanceDragged + ", "
- + mStartDragThreshold);
- }
return distanceDragged > mStartDragThreshold;
}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index d2e1961..bab454f 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -74,6 +74,11 @@
break;
}
+ case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, true);
+ break;
+ }
+
case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
TestProtocol.sDebugTracing = true;
break;
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 6ffc2d9..9846a04 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -66,16 +66,17 @@
"all-apps-to-overview-swipe-height";
public static final String REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT =
"home-to-all-apps-swipe-height";
+ public static final String REQUEST_HOTSEAT_TOP = "hotseat-top";
+ public static final String REQUEST_IS_LAUNCHER_INITIALIZED = "is-launcher-initialized";
public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
+ public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
+ public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
public static boolean sDebugTracing = false;
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
- public static final String NO_ALLAPPS_EVENT_TAG = "b/133867119";
- public static final String NO_DRAG_TAG = "b/133009122";
- public static final String NO_START_TAG = "b/132900132";
- public static final String NO_START_TASK_TAG = "b/133765434";
- public static final String NO_OVERVIEW_EVENT_TAG = "b/134532571";
+
+ public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 6f53140..0545344 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -22,16 +22,15 @@
import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
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;
@@ -43,7 +42,6 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.testing.TestProtocol;
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;
@@ -57,8 +55,6 @@
public abstract class AbstractStateChangeTouchController
implements TouchController, SwipeDetector.Listener {
- private static final String TAG = "ASCTouchController";
-
// Progress after which the transition is assumed to be a success in case user does not fling
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
@@ -118,9 +114,6 @@
@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) {
@@ -150,9 +143,6 @@
return false;
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onControllerInterceptTouchEvent 2 ");
- }
onControllerTouchEvent(ev);
return mDetector.isDraggingOrSettling();
}
@@ -240,9 +230,6 @@
@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;
@@ -252,9 +239,6 @@
mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
}
if (mCurrentAnimation == null) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragStart 2");
- }
mFromState = mStartState;
mToState = null;
cancelAnimationControllers();
@@ -376,9 +360,6 @@
@Override
public void onDragEnd(float velocity, boolean fling) {
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onDragEnd");
- }
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -413,8 +394,8 @@
duration = 0;
startProgress = 1;
} else {
- startProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
+ startProgress = Utilities.boundToRange(progress
+ + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
duration = SwipeDetector.calculateDuration(velocity,
endProgress - Math.max(progress, 0)) * durationMultiplier;
}
@@ -431,8 +412,8 @@
duration = 0;
startProgress = 0;
} else {
- startProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
+ startProgress = Utilities.boundToRange(progress
+ + velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
duration = SwipeDetector.calculateDuration(velocity,
Math.min(progress, 1) - endProgress) * durationMultiplier;
}
@@ -515,9 +496,6 @@
}
protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG, "onSwipeInteractionCompleted 1");
- }
if (mAtomicComponentsController != null) {
mAtomicComponentsController.getAnimationPlayer().end();
mAtomicComponentsController = null;
@@ -531,18 +509,18 @@
shouldGoToTargetState = !reachedTarget;
}
if (shouldGoToTargetState) {
- if (targetState != mStartState) {
- logReachedState(logAction, targetState);
- }
- mLauncher.getStateManager().goToState(targetState, false /* animated */);
-
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.e(
- TestProtocol.NO_ALLAPPS_EVENT_TAG, "onSwipeInteractionCompleted 2");
- }
+ goToTargetState(targetState, logAction);
}
}
+ protected void goToTargetState(LauncherState targetState, int logAction) {
+ if (targetState != mStartState) {
+ logReachedState(logAction, targetState);
+ }
+ mLauncher.getStateManager().goToState(targetState, false /* animated */);
+ mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(1, 0, 0);
+ }
+
private void logReachedState(int logAction, LauncherState targetState) {
// Transition complete. log the action
mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
@@ -563,9 +541,6 @@
}
private void cancelAnimationControllers() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_ALLAPPS_EVENT_TAG, "cancelAnimationControllers");
- }
mCurrentAnimation = null;
cancelAtomicComponentsController();
mDetector.finishedScrolling();
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 85f763d..03493a5 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -25,9 +25,15 @@
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageInstaller.SessionInfo;
import android.os.Process;
+import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
@@ -43,11 +49,12 @@
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.widget.PendingAppWidgetHostView;
@@ -58,6 +65,8 @@
*/
public class ItemClickHandler {
+ private static final String TAG = ItemClickHandler.class.getSimpleName();
+
/**
* Instance used for click handling on items
*/
@@ -68,28 +77,12 @@
}
private static void onClick(View v, String sourceContainer) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "onClick 1");
- }
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
- if (v.getWindowToken() == null) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "onClick 2");
- }
- return;
- }
+ if (v.getWindowToken() == null) return;
Launcher launcher = Launcher.getLauncher(v.getContext());
- if (!launcher.getWorkspace().isFinishedSwitchingState()) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "onClick 3");
- }
- return;
- }
+ if (!launcher.getWorkspace().isFinishedSwitchingState()) return;
Object tag = v.getTag();
if (tag instanceof WorkspaceItemInfo) {
@@ -99,10 +92,6 @@
onClickFolderIcon(v);
}
} else if (tag instanceof AppInfo) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "onClick 4");
- }
startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
} else if (tag instanceof LauncherAppWidgetInfo) {
@@ -166,6 +155,8 @@
startMarketIntentForPackage(v, launcher, packageName);
return;
}
+ UserHandle user = v.getTag() instanceof ItemInfo
+ ? ((ItemInfo) v.getTag()).user : Process.myUserHandle();
new AlertDialog.Builder(launcher)
.setTitle(R.string.abandoned_promises_title)
.setMessage(R.string.abandoned_promise_explanation)
@@ -173,12 +164,28 @@
(d, i) -> startMarketIntentForPackage(v, launcher, packageName))
.setNeutralButton(R.string.abandoned_clean_this,
(d, i) -> launcher.getWorkspace()
- .removeAbandonedPromise(packageName, Process.myUserHandle()))
+ .removeAbandonedPromise(packageName, user))
.create().show();
}
private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
ItemInfo item = (ItemInfo) v.getTag();
+ if (Utilities.ATLEAST_Q) {
+ PackageInstallerCompat pkgInstaller = PackageInstallerCompat.getInstance(launcher);
+ SessionInfo sessionInfo = pkgInstaller.getActiveSessionInfo(item.user, packageName);
+ if (sessionInfo != null) {
+ LauncherApps launcherApps = launcher.getSystemService(LauncherApps.class);
+ try {
+ launcherApps.startPackageInstallerSessionDetailsActivity(sessionInfo, null,
+ launcher.getActivityLaunchOptionsAsBundle(v));
+ return;
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to launch market intent for package=" + packageName, e);
+ }
+ }
+ }
+
+ // Fallback to using custom market intent.
Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
launcher.startActivitySafely(v, intent, item, null);
}
@@ -234,10 +241,6 @@
private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
@Nullable String sourceContainer) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_START_TAG,
- "startAppShortcutOrInfoActivity");
- }
Intent intent;
if (item instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 3d45404..3777a41 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -158,9 +158,6 @@
// 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);
}
@@ -168,9 +165,6 @@
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 */);
@@ -181,11 +175,6 @@
}
mState = newState;
- if (com.android.launcher3.testing.TestProtocol.sDebugTracing) {
- android.util.Log.e(TestProtocol.NO_ALLAPPS_EVENT_TAG,
- "setState: " + newState + " @ " + android.util.Log.getStackTraceString(
- new Throwable()));
- }
}
public boolean isDraggingOrSettling() {
@@ -324,15 +313,9 @@
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) {
diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java
deleted file mode 100644
index 3fcda90..0000000
--- a/src/com/android/launcher3/touch/TouchEventTranslator.java
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.touch;
-
-import android.graphics.PointF;
-import android.util.Log;
-import android.util.Pair;
-import android.util.SparseArray;
-import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
-
-import java.util.function.Consumer;
-
-/**
- * To minimize the size of the MotionEvent, historic events are not copied and passed via the
- * listener.
- */
-public class TouchEventTranslator {
-
- private static final String TAG = "TouchEventTranslator";
- private static final boolean DEBUG = false;
-
- private class DownState {
- long timeStamp;
- float downX;
- float downY;
- public DownState(long timeStamp, float downX, float downY) {
- this.timeStamp = timeStamp;
- this.downX = downX;
- this.downY = downY;
- }
- };
- private final DownState ZERO = new DownState(0, 0f, 0f);
-
- private final Consumer<MotionEvent> mListener;
-
- private final SparseArray<DownState> mDownEvents;
- private final SparseArray<PointF> mFingers;
-
- private final SparseArray<Pair<PointerProperties[], PointerCoords[]>> mCache;
-
- public TouchEventTranslator(Consumer<MotionEvent> listener) {
- mDownEvents = new SparseArray<>();
- mFingers = new SparseArray<>();
- mCache = new SparseArray<>();
-
- mListener = listener;
- }
-
- public void reset() {
- mDownEvents.clear();
- mFingers.clear();
- }
-
- public float getDownX() {
- return mDownEvents.get(0).downX;
- }
-
- public float getDownY() {
- return mDownEvents.get(0).downY;
- }
-
- public void setDownParameters(int idx, MotionEvent e) {
- DownState ev = new DownState(e.getEventTime(), e.getX(idx), e.getY(idx));
- mDownEvents.append(idx, ev);
- }
-
- public void dispatchDownEvents(MotionEvent ev) {
- for(int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) {
- int pid = ev.getPointerId(i);
- put(pid, i, ev.getX(i), 0, mDownEvents.get(i).timeStamp, ev);
- }
- }
-
- public void processMotionEvent(MotionEvent ev) {
- if (DEBUG) {
- printSamples(TAG + " processMotionEvent", ev);
- }
- int index = ev.getActionIndex();
- float x = ev.getX(index);
- float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY;
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_POINTER_DOWN:
- int pid = ev.getPointerId(index);
- if(mFingers.get(pid, null) != null) {
- for(int i=0; i < ev.getPointerCount(); i++) {
- pid = ev.getPointerId(i);
- position(pid, x, y);
- }
- generateEvent(ev.getAction(), ev);
- } else {
- put(pid, index, x, y, ev);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- for(int i=0; i < ev.getPointerCount(); i++) {
- pid = ev.getPointerId(i);
- position(pid, x, y);
- }
- generateEvent(ev.getAction(), ev);
- break;
- case MotionEvent.ACTION_POINTER_UP:
- case MotionEvent.ACTION_UP:
- pid = ev.getPointerId(index);
- lift(pid, index, x, y, ev);
- break;
- case MotionEvent.ACTION_CANCEL:
- cancel(ev);
- break;
- default:
- Log.v(TAG, "Didn't process ");
- printSamples(TAG, ev);
-
- }
- }
-
- private TouchEventTranslator put(int id, int index, float x, float y, MotionEvent ev) {
- return put(id, index, x, y, ev.getEventTime(), ev);
- }
-
- private TouchEventTranslator put(int id, int index, float x, float y, long ms, MotionEvent ev) {
- checkFingerExistence(id, false);
- boolean isInitialDown = (mFingers.size() == 0);
-
- mFingers.put(id, new PointF(x, y));
- int n = mFingers.size();
-
- if (mCache.get(n) == null) {
- PointerProperties[] properties = new PointerProperties[n];
- PointerCoords[] coords = new PointerCoords[n];
- for (int i = 0; i < n; i++) {
- properties[i] = new PointerProperties();
- coords[i] = new PointerCoords();
- }
- mCache.put(n, new Pair(properties, coords));
- }
-
- int action;
- if (isInitialDown) {
- action = MotionEvent.ACTION_DOWN;
- } else {
- action = MotionEvent.ACTION_POINTER_DOWN;
- // Set the id of the changed pointer.
- action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
- generateEvent(action, ms, ev);
- return this;
- }
-
- public TouchEventTranslator position(int id, float x, float y) {
- checkFingerExistence(id, true);
- mFingers.get(id).set(x, y);
- return this;
- }
-
- private TouchEventTranslator lift(int id, int index, MotionEvent ev) {
- checkFingerExistence(id, true);
- boolean isFinalUp = (mFingers.size() == 1);
- int action;
- if (isFinalUp) {
- action = MotionEvent.ACTION_UP;
- } else {
- action = MotionEvent.ACTION_POINTER_UP;
- // Set the id of the changed pointer.
- action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
- generateEvent(action, ev);
- mFingers.remove(id);
- return this;
- }
-
- private TouchEventTranslator lift(int id, int index, float x, float y, MotionEvent ev) {
- checkFingerExistence(id, true);
- mFingers.get(id).set(x, y);
- return lift(id, index, ev);
- }
-
- public TouchEventTranslator cancel(MotionEvent ev) {
- generateEvent(MotionEvent.ACTION_CANCEL, ev);
- mFingers.clear();
- return this;
- }
-
- private void checkFingerExistence(int id, boolean shouldExist) {
- if (shouldExist != (mFingers.get(id, null) != null)) {
- throw new IllegalArgumentException(
- shouldExist ? "Finger does not exist" : "Finger already exists");
- }
- }
-
-
- /**
- * Used to debug MotionEvents being sent/received.
- */
- public void printSamples(String msg, MotionEvent ev) {
- System.out.printf("%s %s", msg, MotionEvent.actionToString(ev.getActionMasked()));
- final int pointerCount = ev.getPointerCount();
- System.out.printf("#%d/%d", ev.getActionIndex(), pointerCount);
- System.out.printf(" t=%d:", ev.getEventTime());
- for (int p = 0; p < pointerCount; p++) {
- System.out.printf(" id=%d: (%f,%f)",
- ev.getPointerId(p), ev.getX(p), ev.getY(p));
- }
- System.out.println();
- }
-
- private void generateEvent(int action, MotionEvent ev) {
- generateEvent(action, ev.getEventTime(), ev);
- }
-
- private void generateEvent(int action, long ms, MotionEvent ev) {
- Pair<PointerProperties[], PointerCoords[]> state = getFingerState();
- MotionEvent event = MotionEvent.obtain(
- mDownEvents.get(0).timeStamp,
- ms,
- action,
- state.first.length,
- state.first,
- state.second,
- ev.getMetaState(),
- ev.getButtonState() /* buttonState */,
- ev.getXPrecision() /* xPrecision */,
- ev.getYPrecision() /* yPrecision */,
- ev.getDeviceId(),
- ev.getEdgeFlags(),
- ev.getSource(),
- ev.getFlags() /* flags */);
- if (DEBUG) {
- printSamples(TAG + " generateEvent", event);
- }
- if (event.getPointerId(event.getActionIndex()) < 0) {
- printSamples(TAG + "generateEvent", event);
- throw new IllegalStateException(event.getActionIndex() + " not found in MotionEvent");
- }
- mListener.accept(event);
- event.recycle();
- }
-
- /**
- * Returns the description of the fingers' state expected by MotionEvent.
- */
- private Pair<PointerProperties[], PointerCoords[]> getFingerState() {
- int nFingers = mFingers.size();
-
- Pair<PointerProperties[], PointerCoords[]> result = mCache.get(nFingers);
- PointerProperties[] properties = result.first;
- PointerCoords[] coordinates = result.second;
-
- int index = 0;
- for (int i = 0; i < mFingers.size(); i++) {
- int id = mFingers.keyAt(i);
- PointF location = mFingers.get(id);
-
- PointerProperties property = properties[i];
- property.id = id;
- property.toolType = MotionEvent.TOOL_TYPE_FINGER;
- properties[index] = property;
-
- PointerCoords coordinate = coordinates[i];
- coordinate.x = location.x;
- coordinate.y = location.y;
- coordinate.pressure = 1.0f;
- coordinates[index] = coordinate;
-
- index++;
- }
- return mCache.get(nFingers);
- }
-}
diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java
new file mode 100644
index 0000000..7719f08
--- /dev/null
+++ b/src/com/android/launcher3/util/DefaultDisplay.java
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to cache properties of default display to avoid a system RPC on every call.
+ */
+public class DefaultDisplay implements DisplayListener {
+
+ public static final MainThreadInitializedObject<DefaultDisplay> INSTANCE =
+ new MainThreadInitializedObject<>(DefaultDisplay::new);
+
+ private static final String TAG = "DefaultDisplay";
+
+ public static final int CHANGE_SIZE = 1 << 0;
+ public static final int CHANGE_ROTATION = 1 << 1;
+ public static final int CHANGE_FRAME_DELAY = 1 << 2;
+
+ private final Context mContext;
+ private final int mId;
+ private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
+ private final Handler mChangeHandler;
+ private Info mInfo;
+
+ private DefaultDisplay(Context context) {
+ mContext = context;
+ mInfo = new Info(context);
+ mId = mInfo.id;
+ mChangeHandler = new Handler(this::onChange);
+
+ context.getSystemService(DisplayManager.class)
+ .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
+ }
+
+ @Override
+ public final void onDisplayAdded(int displayId) { }
+
+ @Override
+ public final void onDisplayRemoved(int displayId) { }
+
+ @Override
+ public final void onDisplayChanged(int displayId) {
+ if (displayId != mId) {
+ return;
+ }
+
+ Info oldInfo = mInfo;
+ Info info = new Info(mContext);
+
+ int change = 0;
+ if (info.hasDifferentSize(oldInfo)) {
+ change |= CHANGE_SIZE;
+ }
+ if (oldInfo.rotation != info.rotation) {
+ change |= CHANGE_ROTATION;
+ }
+ if (info.singleFrameMs != oldInfo.singleFrameMs) {
+ change |= CHANGE_FRAME_DELAY;
+ }
+
+ if (change != 0) {
+ mInfo = info;
+ mChangeHandler.sendEmptyMessage(change);
+ }
+ }
+
+ public static int getSingleFrameMs(Context context) {
+ return INSTANCE.get(context).getInfo().singleFrameMs;
+ }
+
+ public Info getInfo() {
+ return mInfo;
+ }
+
+ public void addChangeListener(DisplayInfoChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeChangeListener(DisplayInfoChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ private boolean onChange(Message msg) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onDisplayInfoChanged(mInfo, msg.what);
+ }
+ return true;
+ }
+
+ public static class Info {
+
+ public final int id;
+ public final int rotation;
+ public final int singleFrameMs;
+
+ public final Point realSize;
+ public final Point smallestSize;
+ public final Point largestSize;
+
+ private Info(Context context) {
+ Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
+
+ id = display.getDisplayId();
+ rotation = display.getRotation();
+
+ float refreshRate = display.getRefreshRate();
+ singleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+
+ realSize = new Point();
+ smallestSize = new Point();
+ largestSize = new Point();
+ display.getRealSize(realSize);
+ display.getCurrentSizeRange(smallestSize, largestSize);
+ }
+
+ private boolean hasDifferentSize(Info info) {
+ if (!realSize.equals(info.realSize)
+ && !realSize.equals(info.realSize.y, info.realSize.x)) {
+ Log.d(TAG, String.format("Display size changed from %s to %s",
+ info.realSize, realSize));
+ return true;
+ }
+
+ if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
+ Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
+ smallestSize, largestSize, info.smallestSize, info.largestSize));
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ /**
+ * Interface for listening for display changes
+ */
+ public interface DisplayInfoChangeListener {
+
+ void onDisplayInfoChanged(Info info, int flags);
+ }
+}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index c1ba780..799762d 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -20,8 +20,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
-import static com.android.launcher3.Utilities.shouldDisableGestures;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.annotation.TargetApi;
import android.content.Context;
@@ -30,7 +29,6 @@
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
@@ -43,7 +41,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.TouchController;
@@ -152,8 +149,6 @@
}
private TouchController findControllerToHandleTouch(MotionEvent ev) {
- if (shouldDisableGestures(ev)) return null;
-
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
return topView;
@@ -221,21 +216,15 @@
if (child instanceof AbstractFloatingView) {
// Handles the case where the view is removed without being properly closed.
// This can happen if something goes wrong during a state change/transition.
- postDelayed(() -> {
- AbstractFloatingView floatingView = (AbstractFloatingView) child;
- if (floatingView.isOpen()) {
- floatingView.close(false);
- }
- }, SINGLE_FRAME_MS);
+ AbstractFloatingView floatingView = (AbstractFloatingView) child;
+ if (floatingView.isOpen()) {
+ postDelayed(() -> floatingView.close(false), getSingleFrameMs(getContext()));
+ }
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "onTouchEvent " + ev);
- }
int action = ev.getAction();
if (action == ACTION_UP || action == ACTION_CANCEL) {
if (mTouchCompleteListener != null) {
@@ -245,10 +234,6 @@
}
if (mActiveController != null) {
- if (TestProtocol.sDebugTracing) {
- android.util.Log.d(TestProtocol.NO_DRAG_TAG,
- "onTouchEvent 1");
- }
return mActiveController.onControllerTouchEvent(ev);
} else {
// In case no child view handled the touch event, we may not get onIntercept anymore
@@ -258,9 +243,6 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.NO_START_TAG, "BaseDragLayer.dispatchTouchEvent " + ev);
- }
switch (ev.getAction()) {
case ACTION_DOWN: {
float x = ev.getX();
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index b6c4fed..e09a9e8 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
+import static com.android.launcher3.Utilities.isRtl;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -129,6 +130,7 @@
private final Launcher mLauncher;
private final int mBlurSizeOutline;
+ private final boolean mIsRtl;
private boolean mIsVerticalBarLayout = false;
private boolean mIsAdaptiveIcon = false;
@@ -174,6 +176,7 @@
mLauncher = Launcher.getLauncher(context);
mBlurSizeOutline = getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline);
+ mIsRtl = Utilities.isRtl(getResources());
mListenerView = new ListenerView(context, attrs);
mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
@@ -213,7 +216,10 @@
setAlpha(alpha);
LayoutParams lp = (LayoutParams) getLayoutParams();
- float dX = rect.left - lp.leftMargin;
+ float dX = mIsRtl
+ ? rect.left
+ - (mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width)
+ : rect.left - lp.getMarginStart();
float dY = rect.top - lp.topMargin;
setTranslationX(dX);
setTranslationY(dY);
@@ -323,14 +329,18 @@
mPositionOut.set(position);
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
- lp.leftMargin = Math.round(position.left);
lp.topMargin = Math.round(position.top);
-
+ if (mIsRtl) {
+ lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - position.right));
+ } else {
+ lp.setMarginStart(Math.round(position.left));
+ }
// Set the properties here already to make sure they are available when running the first
// animation frame.
- layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
- + lp.height);
-
+ int left = mIsRtl
+ ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+ : lp.leftMargin;
+ layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
}
/**
@@ -514,8 +524,11 @@
} else {
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
}
- layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
- + lp.height);
+
+ int left = mIsRtl
+ ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+ : lp.leftMargin;
+ layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
float scale = Math.max((float) lp.height / originalHeight,
(float) lp.width / originalWidth);
@@ -574,17 +587,17 @@
if (cancellationSignal.isCanceled()) {
return;
}
- if (mIconLoadResult.isIconLoaded) {
- setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
- mIconLoadResult.iconOffset);
- }
+
+ setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
+ mIconLoadResult.iconOffset);
+
// Delay swapping views until the icon is loaded to prevent a flash.
setVisibility(VISIBLE);
originalView.setVisibility(INVISIBLE);
};
+ mLoadIconSignal = cancellationSignal;
}
}
- mLoadIconSignal = cancellationSignal;
}
private void setBackgroundDrawableBounds(float scale) {
@@ -656,8 +669,7 @@
canvas.restoreToCount(count);
}
- public void onListenerViewClosed() {
- // Fast finish here.
+ public void fastFinish() {
if (mEndRunnable != null) {
mEndRunnable.run();
mEndRunnable = null;
@@ -757,7 +769,7 @@
view.setVisibility(INVISIBLE);
parent.addView(view);
dragLayer.addView(view.mListenerView);
- view.mListenerView.setListener(view::onListenerViewClosed);
+ view.mListenerView.setListener(view::fastFinish);
view.mEndRunnable = () -> {
view.mEndRunnable = null;
diff --git a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
index fa23b8d..6a6916e 100644
--- a/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
+++ b/tests/src/com/android/launcher3/testcomponent/TestCommandReceiver.java
@@ -32,13 +32,14 @@
import android.os.ParcelFileDescriptor;
import android.util.Base64;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.launcher3.tapl.TestHelpers;
+
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
import java.io.IOException;
-import androidx.test.InstrumentationRegistry;
-
/**
* Content provider to receive commands from tests
*/
@@ -47,6 +48,7 @@
public static final String ENABLE_TEST_LAUNCHER = "enable-test-launcher";
public static final String DISABLE_TEST_LAUNCHER = "disable-test-launcher";
public static final String KILL_PROCESS = "kill-process";
+ public static final String GET_SYSTEM_HEALTH_MESSAGE = "get-system-health-message";
@Override
public boolean onCreate() {
@@ -99,6 +101,12 @@
killBackgroundProcesses(arg);
return null;
}
+
+ case GET_SYSTEM_HEALTH_MESSAGE: {
+ final Bundle response = new Bundle();
+ response.putString("result", TestHelpers.getSystemHealthMessage(getContext()));
+ return response;
+ }
}
return super.call(method, arg, extras);
}
@@ -122,7 +130,8 @@
// Create an empty file so that we can pass its descriptor
try {
file.createNewFile();
- } catch (IOException e) { }
+ } catch (IOException e) {
+ }
}
return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 4a0ca5c..8dc8cea 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -17,10 +17,10 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static java.lang.System.exit;
@@ -38,10 +38,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
-import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
import com.android.launcher3.Launcher;
@@ -49,13 +46,16 @@
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
@@ -86,8 +86,7 @@
public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
- public static final long SHORT_UI_TIMEOUT = 300;
- public static final long DEFAULT_UI_TIMEOUT = 10000;
+ public static final long DEFAULT_UI_TIMEOUT = 60000; // b/136278866
private static final String TAG = "AbstractLauncherUiTest";
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -103,7 +102,15 @@
} catch (RemoteException e) {
throw new RuntimeException(e);
}
- if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
+ if (TestHelpers.isInLauncherProcess()) {
+ Utilities.enableRunningInTestHarnessForTests();
+ mLauncher.setSystemHealthSupplier(() -> TestCommandReceiver.callCommand(
+ TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE).getString("result"));
+ mLauncher.setOnSettledStateAction(
+ containerType -> executeOnLauncher(
+ launcher ->
+ checkLauncherIntegrity(launcher, containerType)));
+ }
}
protected final LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
@@ -116,6 +123,23 @@
public ShellCommandRule mDisableHeadsUpNotification =
ShellCommandRule.disableHeadsUpNotification();
+ protected void clearPackageData(String pkg) throws IOException, InterruptedException {
+ final CountDownLatch count = new CountDownLatch(2);
+ final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ count.countDown();
+ }
+ };
+ mTargetContext.registerReceiver(broadcastReceiver,
+ PackageManagerHelper.getPackageFilter(pkg,
+ Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED));
+
+ mDevice.executeShellCommand("pm clear " + pkg);
+ assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS));
+ mTargetContext.unregisterReceiver(broadcastReceiver);
+ }
+
// Annotation for tests that need to be run in portrait and landscape modes.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@@ -170,38 +194,13 @@
}
}
- protected void clearLauncherData() throws IOException {
+ protected void clearLauncherData() throws IOException, InterruptedException {
if (TestHelpers.isInLauncherProcess()) {
LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
resetLoaderState();
} else {
- mDevice.executeShellCommand("pm clear " + mDevice.getLauncherPackageName());
- }
- }
-
- /**
- * Scrolls the {@param container} until it finds an object matching {@param condition}.
- *
- * @return the matching object.
- */
- protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
- final int margin = ResourceUtils.getNavbarSize(
- ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
- container.setGestureMargins(0, 0, 0, margin);
-
- int i = 0;
- for (; ; ) {
- // findObject can only execute after spring settles.
- mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT);
- UiObject2 widget = container.findObject(condition);
- if (widget != null && widget.getVisibleBounds().intersects(
- 0, 0, mDevice.getDisplayWidth(),
- mDevice.getDisplayHeight() - margin)) {
- return widget;
- }
- if (++i > 40) fail("Too many attempts");
- container.scroll(Direction.DOWN, 1f);
+ clearPackageData(mDevice.getLauncherPackageName());
}
}
@@ -387,4 +386,68 @@
protected int getAllAppsScroll(Launcher launcher) {
return launcher.getAppsView().getActiveRecyclerView().getCurrentScrollY();
}
+
+ private static void checkLauncherIntegrity(
+ Launcher launcher, ContainerType expectedContainerType) {
+ if (launcher != null) {
+ final LauncherStateManager stateManager = launcher.getStateManager();
+ final LauncherState stableState = stateManager.getCurrentStableState();
+
+ assertTrue("Stable state != state: " + stableState.getClass().getSimpleName() + ", "
+ + stateManager.getState().getClass().getSimpleName(),
+ stableState == stateManager.getState());
+
+ final boolean isResumed = launcher.hasBeenResumed();
+ assertTrue("hasBeenResumed() != isStarted(), hasBeenResumed(): " + isResumed,
+ isResumed == launcher.isStarted());
+ assertTrue("hasBeenResumed() != isUserActive(), hasBeenResumed(): " + isResumed,
+ isResumed == launcher.isUserActive());
+
+ final int ordinal = stableState.ordinal;
+
+ switch (expectedContainerType) {
+ case WORKSPACE:
+ case WIDGETS: {
+ assertTrue(
+ "Launcher is not resumed in state: " + expectedContainerType,
+ isResumed);
+ assertTrue(TestProtocol.stateOrdinalToString(ordinal),
+ ordinal == TestProtocol.NORMAL_STATE_ORDINAL);
+ break;
+ }
+ case ALL_APPS: {
+ assertTrue(
+ "Launcher is not resumed in state: " + expectedContainerType,
+ isResumed);
+ assertTrue(TestProtocol.stateOrdinalToString(ordinal),
+ ordinal == TestProtocol.ALL_APPS_STATE_ORDINAL);
+ break;
+ }
+ case OVERVIEW: {
+ assertTrue(
+ "Launcher is not resumed in state: " + expectedContainerType,
+ isResumed);
+ assertTrue(TestProtocol.stateOrdinalToString(ordinal),
+ ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
+ break;
+ }
+ case BACKGROUND: {
+ assertTrue("Launcher is resumed in state: " + expectedContainerType,
+ !isResumed);
+ assertTrue(TestProtocol.stateOrdinalToString(ordinal),
+ ordinal == TestProtocol.NORMAL_STATE_ORDINAL);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Illegal container: " + expectedContainerType);
+ }
+ } else {
+ assertTrue(
+ "Container type is not BACKGROUND or FALLBACK_OVERVIEW: "
+ + expectedContainerType,
+ expectedContainerType == ContainerType.BACKGROUND ||
+ expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index 58c74ce..a76b4a4 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -72,7 +72,7 @@
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
// Launch the home activity
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
waitForModelLoaded();
mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString());
@@ -88,7 +88,7 @@
info.getComponent().getClassName(), 2, 2));
// Launch the home activity
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
waitForModelLoaded();
// Verify widget present
@@ -105,7 +105,7 @@
.build());
// Launch the home activity
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
waitForModelLoaded();
mLauncher.getWorkspace().getHotseatFolder("Folder: Copy");
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 0f36292..ddcb4da 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -1,5 +1,6 @@
package com.android.launcher3.ui;
+import android.util.Log;
import android.view.Surface;
import com.android.launcher3.tapl.TestHelpers;
@@ -9,6 +10,7 @@
import org.junit.runners.model.Statement;
class PortraitLandscapeRunner implements TestRule {
+ private static final String TAG = "PortraitLandscapeRunner";
private AbstractLauncherUiTest mTest;
public PortraitLandscapeRunner(AbstractLauncherUiTest test) {
@@ -36,6 +38,9 @@
evaluateInPortrait();
evaluateInLandscape();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception", e);
+ throw e;
} finally {
mTest.mDevice.setOrientationNatural();
mTest.executeOnLauncher(launcher ->
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index c3168f8..4dab44f 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -216,7 +216,8 @@
final AppIcon app = allApps.getAppIcon("TestActivity7");
assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName()));
test.executeOnLauncher(launcher -> assertTrue(
- "Launcher activity is the top activity; expecting another activity to be the top "
+ "Launcher activity is the top activity; expecting another activity to be the "
+ + "top "
+ "one",
test.isInBackground(launcher)));
} finally {
@@ -304,11 +305,8 @@
switchToAllApps();
allApps.freeze();
try {
- allApps.
- getAppIcon(APP_NAME).
- dragToWorkspace().
- getWorkspaceAppIcon(APP_NAME).
- launch(getAppPackageName());
+ allApps.getAppIcon(APP_NAME).dragToWorkspace();
+ mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
} finally {
allApps.unfreeze();
}
@@ -335,13 +333,8 @@
getMenuItem(0);
final String shortcutName = menuItem.getText();
- // 4. Drag the first shortcut to the home screen.
- // 5. Verify that the shortcut works on home screen
- // (the app opens and has the same text as the shortcut).
- menuItem.
- dragToWorkspace().
- getWorkspaceAppIcon(shortcutName).
- launch(getAppPackageName());
+ menuItem.dragToWorkspace();
+ mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
} finally {
allApps.unfreeze();
}
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index d13d319..d0df664 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -18,28 +18,15 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.InstrumentationRegistry.getTargetContext;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Point;
import android.os.Process;
-import android.os.SystemClock;
import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.R;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
@@ -50,21 +37,6 @@
public class TestViewHelpers {
private static final String TAG = "TestViewHelpers";
- private static UiDevice getDevice() {
- return UiDevice.getInstance(getInstrumentation());
- }
-
- public static UiObject2 findViewById(int id) {
- return getDevice().wait(Until.findObject(getSelectorForId(id)),
- AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
- }
-
- public static BySelector getSelectorForId(int id) {
- final Context targetContext = getTargetContext();
- String name = targetContext.getResources().getResourceEntryName(id);
- return By.res(targetContext.getPackageName(), name);
- }
-
/**
* Finds a widget provider which can fit on the home screen.
*
@@ -91,92 +63,6 @@
return info;
}
- /**
- * Drags an icon to the center of homescreen.
- *
- * @param icon object that is either app icon or shortcut icon
- */
- public static void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) {
- Point center = icon.getVisibleCenter();
-
- // Action Down
- final long downTime = SystemClock.uptimeMillis();
- sendPointer(downTime, MotionEvent.ACTION_DOWN, center);
-
- UiObject2 dragLayer = findViewById(R.id.drag_layer);
-
- if (expectedToShowShortcuts) {
- // Make sure shortcuts show up, and then move a bit to hide them.
- assertNotNull(findViewById(R.id.deep_shortcuts_container));
-
- Point moveLocation = new Point(center);
- int distanceToMove =
- getTargetContext().getResources().getDimensionPixelSize(
- R.dimen.deep_shortcuts_start_drag_threshold) + 50;
- if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) {
- moveLocation.y -= distanceToMove;
- } else {
- moveLocation.y += distanceToMove;
- }
- movePointer(downTime, center, moveLocation);
-
- assertNull(findViewById(R.id.deep_shortcuts_container));
- }
-
- // Wait until Remove/Delete target is visible
- assertNotNull(findViewById(R.id.delete_target_text));
-
- Point moveLocation = dragLayer.getVisibleCenter();
-
- // Move to center
- movePointer(downTime, center, moveLocation);
- sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation);
-
- // Wait until remove target is gone.
- getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)),
- AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT);
- }
-
- private static void movePointer(long downTime, Point from, Point to) {
- while (!from.equals(to)) {
- try {
- Thread.sleep(20);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- from.x = getNextMoveValue(to.x, from.x);
- from.y = getNextMoveValue(to.y, from.y);
- sendPointer(downTime, MotionEvent.ACTION_MOVE, from);
- }
- }
-
- private static int getNextMoveValue(int targetValue, int oldValue) {
- if (targetValue - oldValue > 10) {
- return oldValue + 10;
- } else if (targetValue - oldValue < -10) {
- return oldValue - 10;
- } else {
- return targetValue;
- }
- }
-
- public static void sendPointer(long downTime, int action, Point point) {
- MotionEvent event = MotionEvent.obtain(downTime,
- SystemClock.uptimeMillis(), action, point.x, point.y, 0);
- getInstrumentation().sendPointerSync(event);
- event.recycle();
- }
-
- /**
- * Opens widget tray and returns the recycler view.
- */
- public static UiObject2 openWidgetsTray() {
- final UiDevice device = getDevice();
- device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
- device.waitForIdle();
- return findViewById(R.id.widgets_list_view);
- }
-
public static View findChildView(ViewGroup parent, Function<View, Boolean> condition) {
for (int i = 0; i < parent.getChildCount(); ++i) {
final View child = parent.getChildAt(i);
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index c93c20a..d9edc35 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -53,8 +53,8 @@
@Test
public void workTabExists() {
- mActivityMonitor.startLauncher();
-
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
/*
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 5eb5f19..3206a69 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -27,20 +27,18 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Workspace;
+import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.testcomponent.WidgetConfigActivity;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.Condition;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.WidgetCell;
import org.junit.Before;
import org.junit.Ignore;
@@ -71,7 +69,6 @@
}
@Test
- // Convert test to TAPL b/131116002
public void testWidgetConfig() throws Throwable {
runTest(false, true);
}
@@ -83,7 +80,6 @@
}
@Test
- // Convert test to TAPL b/131116002
public void testConfigCancelled() throws Throwable {
runTest(false, false);
}
@@ -102,17 +98,15 @@
lockRotation(true);
clearHomescreen();
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
- // Open widget tray and wait for load complete.
- final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray();
- Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
+ final Widgets widgets = mLauncher.getWorkspace().openAllWidgets();
// Drag widget to homescreen
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
- UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
- .hasDescendant(By.text(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))));
- TestViewHelpers.dragToWorkspace(widget, false);
+ widgets.
+ getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())).
+ dragToWorkspace();
// Widget id for which the config activity was opened
mWidgetId = monitor.getWidgetId();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 0061568..276c614 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -19,20 +19,12 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import android.view.View;
-import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
-import com.android.launcher3.util.Condition;
-import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ShellCommandRule;
-import com.android.launcher3.widget.WidgetCell;
import org.junit.Ignore;
import org.junit.Rule;
@@ -61,30 +53,22 @@
performTest();
}
- // Convert to TAPL b/131116002
private void performTest() throws Throwable {
clearHomescreen();
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
final LauncherAppWidgetProviderInfo widgetInfo =
TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */);
- // Open widget tray and wait for load complete.
- final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray();
- Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT);
+ mLauncher.
+ getWorkspace().
+ openAllWidgets().
+ getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
+ dragToWorkspace();
- // Drag widget to homescreen
- UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
- .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
- TestViewHelpers.dragToWorkspace(widget, false);
-
- assertTrue(mActivityMonitor.itemExists(new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- return info instanceof LauncherAppWidgetInfo &&
+ assertTrue(mActivityMonitor.itemExists(
+ (info, view) -> info instanceof LauncherAppWidgetInfo &&
((LauncherAppWidgetInfo) info).providerName.getClassName().equals(
- widgetInfo.provider.getClassName());
- }
- }).call());
+ widgetInfo.provider.getClassName())).call());
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index d36126b..3a7df64 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -267,7 +267,7 @@
resetLoaderState();
// Launch the home activity
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
waitForModelLoaded();
}
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 6122dae..a9a5090 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -131,7 +131,7 @@
lockRotation(true);
clearHomescreen();
- mActivityMonitor.startLauncher();
+ mDevice.pressHome();
// Open Pin item activity
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
index 2aba7a5..2042403 100644
--- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java
@@ -15,11 +15,6 @@
*/
package com.android.launcher3.util.rule;
-import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static androidx.test.InstrumentationRegistry.getTargetContext;
-
import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
@@ -52,26 +47,15 @@
}
public Callable<Boolean> itemExists(final ItemOperator op) {
- return new Callable<Boolean>() {
-
- @Override
- public Boolean call() throws Exception {
- Launcher launcher = getActivity();
- if (launcher == null) {
- return false;
- }
- return launcher.getWorkspace().getFirstMatch(op) != null;
+ return () -> {
+ Launcher launcher = getActivity();
+ if (launcher == null) {
+ return false;
}
+ return launcher.getWorkspace().getFirstMatch(op) != null;
};
}
- /**
- * Starts the launcher activity in the target package.
- */
- public void startLauncher() {
- getInstrumentation().startActivitySync(getHomeIntentInPackage(getTargetContext()));
- }
-
private class MyStatement extends Statement implements ActivityLifecycleCallbacks {
private final Statement mBase;
diff --git a/tests/tapl/AndroidManifest.xml b/tests/tapl/AndroidManifest.xml
index 0207e2b..1065446 100644
--- a/tests/tapl/AndroidManifest.xml
+++ b/tests/tapl/AndroidManifest.xml
@@ -23,4 +23,6 @@
>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <uses-permission android:name="android.permission.READ_LOGS"/>
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
</manifest>
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index 7f561a2..03d1600 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -16,10 +16,16 @@
package com.android.launcher3.tapl;
+import static java.util.regex.Pattern.CASE_INSENSITIVE;
+
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
+import java.util.regex.Pattern;
+
public class AddToHomeScreenPrompt {
+ private static final Pattern ADD_AUTOMATICALLY =
+ Pattern.compile("^Add automatically$", CASE_INSENSITIVE);
private final LauncherInstrumentation mLauncher;
private final UiObject2 mWidgetCell;
@@ -33,9 +39,6 @@
public void addAutomatically() {
mLauncher.waitForObjectInContainer(
mWidgetCell.getParent().getParent().getParent().getParent(),
- By.text(LauncherInstrumentation.isAvd()
- ? "ADD AUTOMATICALLY"
- : "Add automatically")).
- click();
+ By.text(ADD_AUTOMATICALLY)).click();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index c9eaf27..bcce8ef 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -54,12 +54,12 @@
"want to switch from background to overview")) {
verifyActiveContainer();
goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
- return new BaseOverview(mLauncher);
+ return mLauncher.isFallbackOverview() ?
+ new BaseOverview(mLauncher) : new Overview(mLauncher);
}
}
protected void goToOverviewUnchecked(int expectedState) {
- mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
switch (mLauncher.getNavigationModel()) {
case ZERO_BUTTON: {
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
@@ -112,7 +112,6 @@
mLauncher.waitForSystemUiObject("recent_apps").click();
break;
}
- mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index ace49e9..bbd2c29 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -16,11 +16,15 @@
package com.android.launcher3.tapl;
+import android.graphics.Rect;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.Collections;
import java.util.List;
@@ -28,7 +32,6 @@
* Common overview pane for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
- private static final int FLING_SPEED = LauncherInstrumentation.isAvd() ? 500 : 1500;
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
BaseOverview(LauncherInstrumentation launcher) {
@@ -38,7 +41,7 @@
@Override
protected LauncherInstrumentation.ContainerType getContainerType() {
- return LauncherInstrumentation.ContainerType.BASE_OVERVIEW;
+ return LauncherInstrumentation.ContainerType.FALLBACK_OVERVIEW;
}
/**
@@ -49,9 +52,10 @@
mLauncher.addContextLayer("want to fling forward in overview")) {
LauncherInstrumentation.log("Overview.flingForward before fling");
final UiObject2 overview = verifyActiveContainer();
- overview.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
- overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
- mLauncher.waitForIdle();
+ final int leftMargin = mLauncher.getTestInfo(
+ TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ mLauncher.scroll(overview, Direction.LEFT, 1, new Rect(leftMargin, 0, 0, 0), 20);
verifyActiveContainer();
}
}
@@ -86,9 +90,10 @@
mLauncher.addContextLayer("want to fling backward in overview")) {
LauncherInstrumentation.log("Overview.flingBackward before fling");
final UiObject2 overview = verifyActiveContainer();
- overview.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
- overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
- mLauncher.waitForIdle();
+ final int rightMargin = mLauncher.getTestInfo(
+ TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ mLauncher.scroll(overview, Direction.RIGHT, 1, new Rect(0, 0, rightMargin, 0), 20);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index d4bdafa..82af7b0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -53,11 +53,9 @@
private Background launch(BySelector selector) {
LauncherInstrumentation.log("Launchable.launch before click " +
mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
- mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
mLauncher.assertTrue(
"Launching an app didn't open a new window: " + mObject.getText(),
mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
- mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
mLauncher.assertTrue(
"App didn't start: " + selector,
mLauncher.getDevice().wait(Until.hasObject(selector),
@@ -68,7 +66,7 @@
/**
* Drags an object to the center of homescreen.
*/
- public Workspace dragToWorkspace() {
+ public void dragToWorkspace() {
final Point launchableCenter = getObject().getVisibleCenter();
final Point displaySize = mLauncher.getRealDisplaySize();
final int width = displaySize.x / 2;
@@ -80,10 +78,6 @@
launchableCenter.x - width / 2 : launchableCenter.x + width / 2,
displaySize.y / 2),
getLongPressIndicator());
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "dragged launchable to workspace")) {
- return new Workspace(mLauncher);
- }
}
protected abstract String getLongPressIndicator();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index a7e6336..fe7401c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -72,6 +72,8 @@
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
/**
* The main tapl object. The only object that can be explicitly constructed by the using code. It
@@ -85,8 +87,8 @@
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
- enum ContainerType {
- WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, BASE_OVERVIEW
+ public enum ContainerType {
+ WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, FALLBACK_OVERVIEW
}
public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
@@ -132,6 +134,9 @@
private int mExpectedRotation = Surface.ROTATION_0;
private final Uri mTestProviderUri;
private final Deque<String> mDiagnosticContext = new LinkedList<>();
+ private Supplier<String> mSystemHealthSupplier;
+
+ private Consumer<ContainerType> mOnSettledStateAction;
/**
* Constructs the root of TAPL hierarchy. You get all other objects from it.
@@ -206,7 +211,7 @@
try {
// Workaround, use constructed context because both the instrumentation context and the
// app context are not constructed with resources that take overlays into account
- final Context ctx = baseContext.createPackageContext("android", 0);
+ final Context ctx = baseContext.createPackageContext(getLauncherPackageName(), 0);
for (int i = 0; i < 100; ++i) {
final int currentInteractionMode = getCurrentInteractionMode(ctx);
final NavigationModel model = getNavigationModel(currentInteractionMode);
@@ -263,10 +268,79 @@
}
}
+ private String getAnomalyMessage() {
+ UiObject2 object = mDevice.findObject(By.res("android", "alertTitle"));
+ if (object != null) {
+ return "System alert popup is visible: " + object.getText();
+ }
+
+ object = mDevice.findObject(By.res("android", "message"));
+ if (object != null) {
+ return "Message popup by " + object.getApplicationPackage() + " is visible: "
+ + object.getText();
+ }
+
+ if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked";
+
+ if (!mDevice.hasObject(By.textStartsWith(""))) return "Screen is empty";
+
+ return null;
+ }
+
+ private String getVisibleStateMessage() {
+ if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
+ if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
+ if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
+ if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
+ return "Background";
+ }
+
+ public void setSystemHealthSupplier(Supplier<String> supplier) {
+ this.mSystemHealthSupplier = supplier;
+ }
+
+ public void setOnSettledStateAction(Consumer<ContainerType> onSettledStateAction) {
+ mOnSettledStateAction = onSettledStateAction;
+ }
+
+ private String getSystemHealthMessage() {
+ final String testPackage = getContext().getPackageName();
+ try {
+ mDevice.executeShellCommand("pm grant " + testPackage +
+ " android.permission.READ_LOGS");
+ mDevice.executeShellCommand("pm grant " + testPackage +
+ " android.permission.PACKAGE_USAGE_STATS");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return mSystemHealthSupplier != null
+ ? mSystemHealthSupplier.get()
+ : TestHelpers.getSystemHealthMessage(getContext());
+ }
+
private void fail(String message) {
- log("Hierarchy dump for: " + getContextDescription() + message);
+ message = "http://go/tapl : " + getContextDescription() + message;
+
+ final String anomaly = getAnomalyMessage();
+ if (anomaly != null) {
+ message = anomaly + ", which causes:\n" + message;
+ } else {
+ message = message + " (visible state: " + getVisibleStateMessage() + ")";
+ }
+
+ final String systemHealth = getSystemHealthMessage();
+ if (systemHealth != null) {
+ message = message
+ + ", which might be a consequence of system health "
+ + "problems:\n<<<<<<<<<<<<<<<<<<\n"
+ + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
+ }
+
+ log("Hierarchy dump for: " + message);
dumpViewHierarchy();
- Assert.fail("http://go/tapl : " + getContextDescription() + message);
+
+ Assert.fail(message);
}
private String getContextDescription() {
@@ -331,12 +405,33 @@
}
private UiObject2 verifyContainerType(ContainerType containerType) {
+ waitForLauncherInitialized();
+
assertEquals("Unexpected display rotation",
mExpectedRotation, mDevice.getDisplayRotation());
+
+ // b/136278866
+ for (int i = 0; i != 100; ++i) {
+ if (getNavigationModeMismatchError() == null) break;
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
final String error = getNavigationModeMismatchError();
assertTrue(error, error == null);
log("verifyContainerType: " + containerType);
+ final UiObject2 container = verifyVisibleObjects(containerType);
+
+ if (mOnSettledStateAction != null) mOnSettledStateAction.accept(containerType);
+
+ return container;
+ }
+
+ private UiObject2 verifyVisibleObjects(ContainerType containerType) {
try (Closable c = addContextLayer(
"but the current state is not " + containerType.name())) {
switch (containerType) {
@@ -373,7 +468,7 @@
return waitForLauncherObject(OVERVIEW_RES_ID);
}
- case BASE_OVERVIEW: {
+ case FALLBACK_OVERVIEW: {
return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
}
case BACKGROUND: {
@@ -390,6 +485,18 @@
}
}
+ private void waitForLauncherInitialized() {
+ for (int i = 0; i < 100; ++i) {
+ if (getTestInfo(
+ TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED).
+ getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD)) {
+ return;
+ }
+ SystemClock.sleep(100);
+ }
+ fail("Launcher didn't initialize");
+ }
+
Parcelable executeAndWaitForEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, String message) {
try {
@@ -614,12 +721,12 @@
@NonNull
UiObject2 waitForLauncherObject(BySelector selector) {
- return waitForObjectBySelector(selector.pkg(getLauncherPackageName()));
+ return waitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName()));
}
@NonNull
UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) {
- return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout);
+ return tryWaitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName()), timeout);
}
@NonNull
@@ -649,6 +756,10 @@
return mDevice.getLauncherPackageName();
}
+ boolean isFallbackOverview() {
+ return !getOverviewPackageName().equals(getLauncherPackageName());
+ }
+
@NonNull
public UiDevice getDevice() {
return mDevice;
@@ -684,7 +795,7 @@
startX = endX = rect.centerX();
final int vertCenter = rect.centerY();
final float halfGestureHeight = rect.height() * percent / 2.0f;
- startY = (int) (vertCenter - halfGestureHeight);
+ startY = (int) (vertCenter - halfGestureHeight) + 1;
endY = (int) (vertCenter + halfGestureHeight);
}
break;
@@ -692,10 +803,26 @@
startX = endX = rect.centerX();
final int vertCenter = rect.centerY();
final float halfGestureHeight = rect.height() * percent / 2.0f;
- startY = (int) (vertCenter + halfGestureHeight);
+ startY = (int) (vertCenter + halfGestureHeight) - 1;
endY = (int) (vertCenter - halfGestureHeight);
}
break;
+ case LEFT: {
+ startY = endY = rect.centerY();
+ final int horizCenter = rect.centerX();
+ final float halfGestureWidth = rect.width() * percent / 2.0f;
+ startX = (int) (horizCenter - halfGestureWidth) + 1;
+ endX = (int) (horizCenter + halfGestureWidth);
+ }
+ break;
+ case RIGHT: {
+ startY = endY = rect.centerY();
+ final int horizCenter = rect.centerX();
+ final float halfGestureWidth = rect.width() * percent / 2.0f;
+ startX = (int) (horizCenter + halfGestureWidth) - 1;
+ endX = (int) (horizCenter - halfGestureWidth);
+ }
+ break;
default:
fail("Unsupported direction");
return;
@@ -711,6 +838,7 @@
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
// fixed interval each time.
void linearGesture(int startX, int startY, int endX, int endY, int steps) {
+ log("linearGesture: " + startX + ", " + startY + " -> " + endX + ", " + endY);
final long downTime = SystemClock.uptimeMillis();
final Point start = new Point(startX, startY);
final Point end = new Point(endX, endY);
@@ -812,7 +940,7 @@
int getEdgeSensitivityWidth() {
try {
final Context context = mInstrumentation.getTargetContext().createPackageContext(
- "android", 0);
+ getLauncherPackageName(), 0);
return context.getResources().getDimensionPixelSize(
getSystemDimensionResId(context, "config_backGestureInset")) + 1;
} catch (PackageManager.NameNotFoundException e) {
@@ -826,4 +954,12 @@
getContext().getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(size);
return size;
}
+
+ public void enableDebugTracing() {
+ getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
+ }
+
+ public void disableDebugTracing() {
+ getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
+ }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 058831f..da68da3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -19,9 +19,9 @@
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import androidx.annotation.NonNull;
-import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
+import com.android.launcher3.testing.TestProtocol;
/**
* Overview pane.
@@ -51,11 +51,15 @@
// Swipe from an app icon to the top.
LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
- final UiObject2 allApps = mLauncher.waitForLauncherObject("apps_view");
- mLauncher.swipeToState(mLauncher.getDevice().getDisplayWidth() / 2,
- allApps.getVisibleBounds().top,
+ mLauncher.swipeToState(
mLauncher.getDevice().getDisplayWidth() / 2,
- 0, 50, ALL_APPS_STATE_ORDINAL);
+ mLauncher.getTestInfo(
+ TestProtocol.REQUEST_HOTSEAT_TOP).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD),
+ mLauncher.getDevice().getDisplayWidth() / 2,
+ 0,
+ 50,
+ ALL_APPS_STATE_ORDINAL);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"swiped all way up from overview")) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 641c413..6e33322 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -64,14 +64,12 @@
*/
public Background open() {
verifyActiveContainer();
- mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"clicking an overview task")) {
mLauncher.assertTrue("Launching task didn't open a new window: " +
mTask.getParent().getContentDescription(),
mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
}
- mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
return new Background(mLauncher);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index 93554d2..399c59d 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -26,7 +26,11 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.os.DropBoxManager;
+import org.junit.Assert;
+
+import java.util.Date;
import java.util.List;
public class TestHelpers {
@@ -81,4 +85,70 @@
}
return "com.android.systemui";
}
+
+ private static String truncateCrash(String text, int maxLines) {
+ String[] lines = text.split("\\r?\\n");
+ StringBuilder ret = new StringBuilder();
+ for (int i = 0; i < maxLines && i < lines.length; i++) {
+ ret.append(lines[i]);
+ ret.append('\n');
+ }
+ if (lines.length > maxLines) {
+ ret.append("... ");
+ ret.append(lines.length - maxLines);
+ ret.append(" more lines truncated ...\n");
+ }
+ return ret.toString();
+ }
+
+ private static String checkCrash(Context context, String label) {
+ DropBoxManager dropbox = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
+ Assert.assertNotNull("Unable access the DropBoxManager service", dropbox);
+
+ long timestamp = System.currentTimeMillis() - 5 * 60000;
+ DropBoxManager.Entry entry;
+ StringBuilder errorDetails = new StringBuilder();
+ while (null != (entry = dropbox.getNextEntry(label, timestamp))) {
+ if (errorDetails.length() != 0) errorDetails.append("------------------------------");
+ timestamp = entry.getTimeMillis();
+ errorDetails.append(new Date(timestamp));
+ errorDetails.append(": ");
+ errorDetails.append(entry.getTag());
+ errorDetails.append(": ");
+ final String dropboxSnippet = entry.getText(4096);
+ if (dropboxSnippet != null) errorDetails.append(truncateCrash(dropboxSnippet, 40));
+ errorDetails.append(" ...\n");
+ entry.close();
+ }
+ return errorDetails.length() != 0 ? errorDetails.toString() : null;
+ }
+
+ public static String getSystemHealthMessage(Context context) {
+ try {
+ StringBuilder errors = new StringBuilder();
+
+ final String[] labels = {
+ "system_app_anr",
+ "system_app_crash",
+ "system_app_native_crash",
+ "system_app_wtf",
+ "system_server_anr",
+ "system_server_crash",
+ "system_server_native_crash",
+ "system_server_watchdog",
+ };
+
+ for (String label : labels) {
+ final String crash = checkCrash(context, label);
+ if (crash != null) errors.append(crash);
+ }
+
+ return errors.length() != 0
+ ? "Current time: " + new Date(System.currentTimeMillis()) + "\n" + errors
+ : null;
+ } catch (Exception e) {
+ return "Failed to get system health diags, maybe build your test via .bp instead of "
+ + ".mk? " + android.util.Log.getStackTraceString(e);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 128789d..1b6d8c4 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -18,7 +18,16 @@
import androidx.test.uiautomator.UiObject2;
-public class Widget {
- Widget(LauncherInstrumentation launcher, UiObject2 widget) {
+/**
+ * Widget in workspace or a widget list.
+ */
+public final class Widget extends Launchable {
+ Widget(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected String getLongPressIndicator() {
+ return "drop_target_bar";
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 94003be..2495933 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -16,6 +16,12 @@
package com.android.launcher3.tapl;
+import static org.junit.Assert.fail;
+
+import android.graphics.Point;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
@@ -75,4 +81,27 @@
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.WIDGETS;
}
+
+ public Widget getWidget(String label) {
+ final int margin = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1;
+ final UiObject2 widgetsContainer = verifyActiveContainer();
+ widgetsContainer.setGestureMargins(0, 0, 0, margin);
+
+ final Point displaySize = mLauncher.getRealDisplaySize();
+
+ int i = 0;
+ final BySelector selector = By.
+ clazz("com.android.launcher3.widget.WidgetCell").
+ hasDescendant(By.text(label));
+
+ for (; ; ) {
+ final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300);
+ if (widget != null && widget.getVisibleBounds().bottom <= displaySize.y - margin) {
+ return new Widget(mLauncher, widget);
+ }
+ if (++i > 40) fail("Too many attempts");
+ widgetsContainer.scroll(Direction.DOWN, 1f);
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index b01b6f3..07f8b64 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -67,7 +67,6 @@
"switchToAllApps: swipeHeight = " + swipeHeight + ", slop = "
+ mLauncher.getTouchSlop());
- mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
mLauncher.swipeToState(
start.x,
start.y,
@@ -75,7 +74,6 @@
start.y - swipeHeight - mLauncher.getTouchSlop(),
60,
ALL_APPS_STATE_ORDINAL);
- mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"swiped to all apps")) {
@@ -157,7 +155,6 @@
static void dragIconToWorkspace(
LauncherInstrumentation launcher, Launchable launchable, Point dest,
String longPressIndicator) {
- launcher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
LauncherInstrumentation.log("dragIconToWorkspace: begin");
final Point launchableCenter = launchable.getObject().getVisibleCenter();
final long downTime = SystemClock.uptimeMillis();
@@ -172,7 +169,6 @@
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
LauncherInstrumentation.log("dragIconToWorkspace: end");
launcher.waitUntilGone("drop_target_bar");
- launcher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
}
/**