Merge "Do not show Style & Wallpaper if the corresponding apk does not exist." into ub-launcher3-master
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index ee67d49..048e9c5 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -21,15 +21,15 @@
android:orientation="horizontal">
<FrameLayout
android:id="@+id/task_icon_and_thumbnail"
- android:layout_width="@dimen/task_item_height"
- android:layout_height="@dimen/task_item_height"
+ android:layout_width="@dimen/task_thumbnail_and_icon_view_size"
+ android:layout_height="@dimen/task_thumbnail_and_icon_view_size"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="@dimen/task_item_half_vert_margin">
<ImageView
android:id="@+id/task_thumbnail"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_width="@dimen/task_thumbnail_width"
+ android:layout_height="@dimen/task_thumbnail_height"
android:layout_gravity="top|start"/>
<ImageView
android:id="@+id/task_icon"
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
index 28cc1eb..7269704 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/values/dimens.xml
@@ -15,7 +15,9 @@
limitations under the License.
-->
<resources>
- <dimen name="task_item_height">60dp</dimen>
<dimen name="task_item_half_vert_margin">8dp</dimen>
+ <dimen name="task_thumbnail_and_icon_view_size">60dp</dimen>
+ <dimen name="task_thumbnail_height">60dp</dimen>
+ <dimen name="task_thumbnail_width">36dp</dimen>
<dimen name="task_icon_size">36dp</dimen>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f199643..defed84 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -15,15 +15,29 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.NonNull;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.views.IconRecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
/**
* Provider for the atomic remote window animation from the app to the overview.
@@ -33,13 +47,17 @@
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
RemoteAnimationProvider {
- private static final long RECENTS_LAUNCH_DURATION = 250;
+ private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50;
+ private static final long APP_SCALE_DOWN_DURATION = 400;
private static final String TAG = "AppToOverviewAnimationProvider";
private final ActivityControlHelper<T> mHelper;
+ private final int mTargetTaskId;
+ private IconRecentsView mRecentsView;
AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
mHelper = helper;
+ mTargetTaskId = targetTaskId;
}
/**
@@ -54,35 +72,157 @@
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer()
- .setDuration(RECENTS_LAUNCH_DURATION);
+ .setDuration(getRecentsLaunchDuration());
anim.setInterpolator(FAST_OUT_SLOW_IN);
anim.start();
});
factory.onRemoteAnimationReceived(null);
- factory.createActivityController(RECENTS_LAUNCH_DURATION);
+ factory.createActivityController(getRecentsLaunchDuration());
+ mRecentsView = activity.getOverviewPanel();
return false;
}
/**
- * Create remote window animation from the currently running app to the overview panel.
+ * Create remote window animation from the currently running app to the overview panel. Should
+ * be called after {@link #onActivityReady}.
*
* @param targetCompats the target apps
* @return animation from app to overview
*/
@Override
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
- //TODO: Implement the overview to app window animation for Go.
AnimatorSet anim = new AnimatorSet();
- anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+ if (mRecentsView == null) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No recents view. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+
+ RemoteAnimationTargetCompat recentsTarget = null;
+ RemoteAnimationTargetCompat closingAppTarget = null;
+
+ for (RemoteAnimationTargetCompat target : targetCompats) {
+ if (target.mode == MODE_OPENING) {
+ recentsTarget = target;
+ } else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
+ closingAppTarget = target;
+ }
+ }
+
+ if (closingAppTarget == null) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No closing app target. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+ if (recentsTarget == null) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No recents target. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+
+ View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId);
+ if (thumbnailView == null) {
+ // TODO: We should either 1) guarantee the view is loaded before attempting this
+ // or 2) have a backup animation.
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+
+ playAppScaleDownAnim(anim, closingAppTarget, recentsTarget, thumbnailView);
+
return anim;
}
/**
+ * Animate a closing app to scale down to the location of the thumbnail view in recents.
+ *
+ * @param anim animator set
+ * @param appTarget the app surface thats closing
+ * @param recentsTarget the surface containing recents
+ * @param thumbnailView the thumbnail view to animate to
+ */
+ private void playAppScaleDownAnim(@NonNull AnimatorSet anim,
+ @NonNull RemoteAnimationTargetCompat appTarget,
+ @NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) {
+
+ // Identify where the entering remote app should animate to.
+ Rect endRect = new Rect();
+ thumbnailView.getGlobalVisibleRect(endRect);
+
+ Rect appBounds = appTarget.sourceContainerBounds;
+
+ ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1);
+ valueAnimator.setDuration(APP_SCALE_DOWN_DURATION);
+
+ SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+ new SyncRtSurfaceTransactionApplierCompat(thumbnailView);
+
+ // Keep recents visible throughout the animation.
+ SurfaceParams[] params = new SurfaceParams[2];
+ params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
+ null /* windowCrop */, getLayer(recentsTarget, MODE_OPENING), 0 /* cornerRadius */);
+
+ valueAnimator.addUpdateListener(new MultiValueUpdateListener() {
+ private final FloatProp mScaleX;
+ private final FloatProp mScaleY;
+ private final FloatProp mTranslationX;
+ private final FloatProp mTranslationY;
+ private final FloatProp mAlpha;
+
+ {
+ // Scale down and move to view location.
+ float endScaleX = ((float) endRect.width()) / appBounds.width();
+ mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+ float endScaleY = ((float) endRect.height()) / appBounds.height();
+ mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+ float endTranslationX = endRect.left -
+ (appBounds.width() - thumbnailView.getWidth()) / 2.0f;
+ mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+ float endTranslationY = endRect.top -
+ (appBounds.height() - thumbnailView.getHeight()) / 2.0f;
+ mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+
+ // Fade out quietly near the end to be replaced by the real view.
+ mAlpha = new FloatProp(1.0f, 0,
+ APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION,
+ APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2);
+ }
+
+ @Override
+ public void onUpdate(float percent) {
+ Matrix m = new Matrix();
+ m.setScale(mScaleX.value, mScaleY.value,
+ appBounds.width() / 2.0f, appBounds.height() / 2.0f);
+ m.postTranslate(mTranslationX.value, mTranslationY.value);
+
+ params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m,
+ null /* windowCrop */, getLayer(appTarget, MODE_CLOSING),
+ 0 /* cornerRadius */);
+ surfaceApplier.scheduleApply(params);
+ }
+ });
+ anim.play(valueAnimator);
+ }
+
+ /**
* Get duration of animation from app to overview.
*
* @return duration of animation
*/
long getRecentsLaunchDuration() {
- return RECENTS_LAUNCH_DURATION;
+ return APP_SCALE_DOWN_DURATION;
}
}
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index d55d2e5..40db7dd 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -21,6 +21,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.views.IconRecentsView;
@@ -38,10 +39,14 @@
public AnimationFactory prepareRecentsUI(Launcher activity,
boolean activityVisible, boolean animateActivity,
Consumer<AnimatorPlaybackController> callback) {
+ LauncherState fromState = activity.getStateManager().getState();
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
return new AnimationFactory() {
@Override
- public void createActivityController(long transitionLength) {}
+ public void createActivityController(long transitionLength) {
+ callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
+ fromState, OVERVIEW, transitionLength));
+ }
@Override
public void onTransitionCancelled() {}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 4f3d1e4..9a2c0f8 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,10 +15,12 @@
*/
package com.android.quickstep;
+import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import com.android.launcher3.R;
@@ -36,6 +38,7 @@
private static final int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter";
private final TaskListLoader mLoader;
+ private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
private TaskInputController mInputController;
public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -46,6 +49,16 @@
mInputController = inputController;
}
+ /**
+ * Get task item view for a given task id if it's attached to the view.
+ *
+ * @param taskId task id to search for
+ * @return corresponding task item view if it's attached, null otherwise
+ */
+ public @Nullable TaskItemView getTaskItemView(int taskId) {
+ return mTaskIdToViewMap.get(taskId);
+ }
+
@Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
@@ -63,6 +76,17 @@
return;
}
holder.bindTask(tasks.get(position));
+
+ }
+
+ @Override
+ public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
+ mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
+ mTaskIdToViewMap.remove(holder.getTask().key.id);
}
@Override
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e294282..3fdaefe 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,6 +27,7 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -155,6 +156,20 @@
});
}
+ /**
+ * Get the thumbnail view associated with a task for the purposes of animation.
+ *
+ * @param taskId task id of thumbnail view to get
+ * @return the thumbnail view for the task if attached, null otherwise
+ */
+ public @Nullable View getThumbnailViewForTask(int taskId) {
+ TaskItemView view = mTaskAdapter.getTaskItemView(taskId);
+ if (view == null) {
+ return null;
+ }
+ return view.getThumbnailView();
+ }
+
public void setTranslationYFactor(float translationFactor) {
mTranslationYFactor = translationFactor;
setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 3818965..373f107 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -19,6 +19,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -76,4 +77,8 @@
public void setThumbnail(Bitmap thumbnail) {
mThumbnailView.setImageBitmap(thumbnail);
}
+
+ public View getThumbnailView() {
+ return mThumbnailView;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index 109a4c5..5494052 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -22,59 +22,70 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
-import android.view.Display;
import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.launcher3.R;
+import com.android.systemui.shared.system.NavigationBarCompat;
/**
* Touch consumer for handling events to launch assistant from launcher
*/
public class AssistantTouchConsumer implements InputConsumer {
private static final String TAG = "AssistantTouchConsumer";
+ private static final long RETRACT_ANIMATION_DURATION_MS = 300;
+
+ /* The assistant touch consume competes with quick switch InputConsumer gesture. The delegate
+ * can be chosen to run if the angle passing the slop is lower than the threshold angle. When
+ * this occurs, the state changes to {@link #STATE_DELEGATE_ACTIVE} where the next incoming
+ * motion events are handled by the delegate instead of the assistant touch consumer. If the
+ * angle is higher than the threshold, the state will change to {@link #STATE_ASSISTANT_ACTIVE}.
+ */
+ private static final int STATE_INACTIVE = 0;
+ private static final int STATE_ASSISTANT_ACTIVE = 1;
+ private static final int STATE_DELEGATE_ACTIVE = 2;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
+ private final PointF mStartDragPos = new PointF();
+
private int mActivePointerId = -1;
-
- private final int mDisplayRotation;
- private final Rect mStableInsets = new Rect();
-
- private final float mDragSlop;
- private final float mTouchSlop;
- private final float mThreshold;
-
- private float mStartDisplacement;
- private boolean mPassedDragSlop;
- private boolean mPassedTouchSlop;
- private long mPassedTouchSlopTime;
+ private boolean mPassedSlop;
private boolean mLaunchedAssistant;
+ private float mDistance;
+ private float mTimeFraction;
+ private long mDragTime;
private float mLastProgress;
+ private int mState;
+ private final float mDistThreshold;
+ private final long mTimeThreshold;
+ private final int mAngleThreshold;
+ private final float mSlop;
+ private final MotionPauseDetector mMotionPauseDetector;
private final ISystemUiProxy mSysUiProxy;
+ private final InputConsumer mConsumerDelegate;
- public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy) {
+ public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
+ InputConsumer delegate) {
+ final Resources res = context.getResources();
mSysUiProxy = systemUiProxy;
-
- mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
- mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
- mThreshold = context.getResources().getDimension(R.dimen.gestures_assistant_threshold);
-
- Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
- mDisplayRotation = display.getRotation();
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ mConsumerDelegate = delegate;
+ mMotionPauseDetector = new MotionPauseDetector(context);
+ mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
+ mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
+ mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
+ mSlop = NavigationBarCompat.getQuickScrubTouchSlopPx();
+ mState = STATE_INACTIVE;
}
@Override
@@ -83,14 +94,28 @@
}
@Override
+ public boolean isActive() {
+ return mState != STATE_INACTIVE;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
// TODO add logging
+
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
- mLastProgress = -1;
+ mTimeFraction = 0;
+
+ // Detect when the gesture decelerates to start the assistant
+ mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+ if (isPaused && mState == STATE_ASSISTANT_ACTIVE) {
+ mTimeFraction = 1;
+ updateAssistantProgress();
+ }
+ });
break;
}
case ACTION_POINTER_UP: {
@@ -107,94 +132,100 @@
break;
}
case ACTION_MOVE: {
+ if (mState == STATE_DELEGATE_ACTIVE) {
+ break;
+ }
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- float displacement = getDisplacement(ev);
- if (!mPassedDragSlop) {
- // Normal gesture, ensure we pass the drag slop before we start tracking
- // the gesture
- if (Math.abs(displacement) > mDragSlop) {
- mPassedDragSlop = true;
- mStartDisplacement = displacement;
- mPassedTouchSlopTime = SystemClock.uptimeMillis();
- }
- }
+ if (!mPassedSlop) {
+ // Normal gesture, ensure we pass the slop before we start tracking the gesture
+ if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) > mSlop) {
+ mPassedSlop = true;
+ mStartDragPos.set(mLastPos.x, mLastPos.y);
+ mDragTime = SystemClock.uptimeMillis();
- if (!mPassedTouchSlop) {
- if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
- mTouchSlop) {
- mPassedTouchSlop = true;
- if (!mPassedDragSlop) {
- mPassedDragSlop = true;
- mStartDisplacement = displacement;
- mPassedTouchSlopTime = SystemClock.uptimeMillis();
+ // Determine if angle is larger than threshold for assistant detection
+ float angle = (float) Math.toDegrees(
+ Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x));
+ angle = angle > 90 ? 180 - angle : angle;
+ if (angle > mAngleThreshold) {
+ mState = STATE_ASSISTANT_ACTIVE;
+
+ if (mConsumerDelegate != null) {
+ // Send cancel event
+ MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ mConsumerDelegate.onMotionEvent(event);
+ }
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
}
}
- }
-
- if (mPassedDragSlop) {
- // Move
- float distance = mStartDisplacement - displacement;
- if (distance >= 0) {
- onAssistantProgress(distance / mThreshold);
+ } else {
+ // Movement
+ mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
+ mLastPos.y - mStartDragPos.y);
+ mMotionPauseDetector.addPosition(mDistance, 0);
+ if (mDistance >= 0) {
+ final long diff = SystemClock.uptimeMillis() - mDragTime;
+ mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1);
+ updateAssistantProgress();
}
}
break;
}
case ACTION_CANCEL:
- break;
- case ACTION_UP: {
- if (ev.getEventTime() - mPassedTouchSlopTime < ViewConfiguration.getTapTimeout()) {
- onAssistantProgress(1);
+ case ACTION_UP:
+ if (mState != STATE_DELEGATE_ACTIVE && !mLaunchedAssistant) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mLastProgress, 0)
+ .setDuration(RETRACT_ANIMATION_DURATION_MS);
+ animator.addUpdateListener(valueAnimator -> {
+ float progress = (float) valueAnimator.getAnimatedValue();
+ try {
+ mSysUiProxy.onAssistantProgress(progress);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to send SysUI start/send assistant progress: "
+ + progress, e);
+ }
+ });
+ animator.setInterpolator(Interpolators.DEACCEL_2);
+ animator.start();
}
-
+ mMotionPauseDetector.clear();
break;
- }
+ }
+
+ if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
+ mConsumerDelegate.onMotionEvent(ev);
}
}
- private void onAssistantProgress(float progress) {
- if (mLastProgress == progress) {
- return;
- }
- try {
- mSysUiProxy.onAssistantProgress(Math.max(0, Math.min(1, progress)));
- if (progress >= 1 && !mLaunchedAssistant) {
- mSysUiProxy.startAssistant(new Bundle());
- mLaunchedAssistant = true;
- }
+ private void updateAssistantProgress() {
+ if (!mLaunchedAssistant) {
+ float progress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
mLastProgress = progress;
- } catch (RemoteException e) {
- Log.w(TAG, "Failed to notify SysUI to start/send assistant progress: " + progress, e);
+ try {
+ mSysUiProxy.onAssistantProgress(progress);
+
+ if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
+ mSysUiProxy.startAssistant(new Bundle());
+ mLaunchedAssistant = true;
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + progress, e);
+ }
}
}
- private boolean isNavBarOnRight() {
- return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
- }
-
- private boolean isNavBarOnLeft() {
- return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
- }
-
- private float getDisplacement(MotionEvent ev) {
- float eventX = ev.getX();
- float eventY = ev.getY();
- float displacement = eventY - mDownPos.y;
- if (isNavBarOnRight()) {
- displacement = eventX - mDownPos.x;
- } else if (isNavBarOnLeft()) {
- displacement = mDownPos.x - eventX;
- }
- return displacement;
- }
-
- static boolean withinTouchRegion(Context context, float x) {
- return x > context.getResources().getDisplayMetrics().widthPixels
- - context.getResources().getDimension(R.dimen.gestures_assistant_width);
+ static boolean withinTouchRegion(Context context, MotionEvent ev) {
+ final Resources res = context.getResources();
+ final int width = res.getDisplayMetrics().widthPixels;
+ final int height = res.getDisplayMetrics().heightPixels;
+ final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size);
+ return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size;
}
}
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 c281d2c..8fe0461 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -311,31 +311,36 @@
mSwipeSharedState.clearAllState();
}
+ final ActivityControlHelper activityControl =
+ mOverviewComponentObserver.getActivityControlHelper();
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
return InputConsumer.NO_OP;
} else if (mAssistantAvailable && mOverviewInteractionState.isSwipeUpGestureEnabled()
&& FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
- && AssistantTouchConsumer.withinTouchRegion(this, event.getX())) {
- return new AssistantTouchConsumer(this, mRecentsModel.getSystemUiProxy());
- } else if (mSwipeSharedState.goingToLauncher ||
- mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
- return OverviewInputConsumer.newInstance(
- mOverviewComponentObserver.getActivityControlHelper(), false);
+ && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+ return new AssistantTouchConsumer(this, mISystemUiProxy, !activityControl.isResumed()
+ ? createOtherActivityInputConsumer(event, runningTaskInfo) : null);
+ } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
+ return OverviewInputConsumer.newInstance(activityControl, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
- mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
- return OverviewInputConsumer.newInstance(
- mOverviewComponentObserver.getActivityControlHelper(), false);
+ activityControl.isInLiveTileMode()) {
+ return OverviewInputConsumer.newInstance(activityControl, false);
} else {
- ActivityControlHelper activityControl =
- mOverviewComponentObserver.getActivityControlHelper();
- boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
- return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
- mOverviewComponentObserver.getOverviewIntent(), activityControl,
- shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
- this::onConsumerInactive, mSwipeSharedState);
+ return createOtherActivityInputConsumer(event, runningTaskInfo);
}
}
+ private OtherActivityInputConsumer 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, mTaskOverlayFactory, mInputConsumer,
+ this::onConsumerInactive, mSwipeSharedState);
+ }
+
/**
* To be called by the consumer when it's no longer active.
*/
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index 19e9cb4..cf42a36 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -198,7 +198,7 @@
final ActivityOptions options = ActivityOptions.makeScaleUpAnimation(
this, 0, 0,
getWidth(), getHeight());
- launcher.startActivity(intent, options.toBundle());
+ launcher.startActivityForResult(intent, 0, options.toBundle());
launcher.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
LauncherLogProto.ControlType.APP_USAGE_SETTINGS, this);
} catch (ActivityNotFoundException e) {
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3fbfcdd..a966698 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -26,4 +26,8 @@
determines how many thumbnails will be fetched in the background. -->
<integer name="recentsThumbnailCacheSize">3</integer>
<integer name="recentsIconCacheSize">12</integer>
+
+ <!-- Assistant Gesture -->
+ <integer name="assistant_gesture_min_time_threshold">200</integer>
+ <integer name="assistant_gesture_corner_deg_threshold">30</integer>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index f5e5dd3..9c97c8c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -66,6 +66,6 @@
<dimen name="shelf_surface_offset">24dp</dimen>
<!-- Assistant Gestures -->
- <dimen name="gestures_assistant_width">70dp</dimen>
- <dimen name="gestures_assistant_threshold">200dp</dimen>
+ <dimen name="gestures_assistant_size">28dp</dimen>
+ <dimen name="gestures_assistant_drag_threshold">70dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/quickstep/TestInformationProvider.java b/quickstep/src/com/android/quickstep/TestInformationProvider.java
index 0c478d2..0c93dd6 100644
--- a/quickstep/src/com/android/quickstep/TestInformationProvider.java
+++ b/quickstep/src/com/android/quickstep/TestInformationProvider.java
@@ -25,6 +25,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.TestProtocol;
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.OverviewState;
@@ -82,6 +85,21 @@
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
break;
}
+ case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
+ final LauncherAppState launcherAppState =
+ LauncherAppState.getInstanceNoCreate();
+ if (launcherAppState == null) return null;
+
+ final Launcher launcher = (Launcher) launcherAppState.getModel().getCallback();
+ if (launcher == null) return null;
+
+ final float progress = LauncherState.OVERVIEW.getVerticalProgress(launcher)
+ - LauncherState.ALL_APPS.getVerticalProgress(launcher);
+ final float distance =
+ launcher.getAllAppsController().getShiftRange() * progress;
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
+ break;
+ }
}
return response;
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 6582df2..f081303 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -20,7 +20,10 @@
import static com.android.launcher3.Utilities.getDevicePrefs;
import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -114,6 +117,7 @@
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
private ConfigMonitor mConfigMonitor;
+ private OverlayMonitor mOverlayMonitor;
@VisibleForTesting
public InvariantDeviceProfile() {}
@@ -131,6 +135,7 @@
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
+ mOverlayMonitor = p.mOverlayMonitor;
}
@TargetApi(23)
@@ -138,8 +143,12 @@
initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null));
mConfigMonitor = new ConfigMonitor(context,
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
+ mOverlayMonitor = new OverlayMonitor(context);
}
+ /**
+ * This constructor should NOT have any monitors by design.
+ */
public InvariantDeviceProfile(Context context, String gridName) {
String newName = initGrid(context, gridName);
if (newName == null || !newName.equals(gridName)) {
@@ -555,4 +564,20 @@
return this;
}
}
+
+ private class OverlayMonitor extends BroadcastReceiver {
+
+ private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+
+ OverlayMonitor(Context context) {
+ IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+ filter.addDataScheme("package");
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ onConfigChanged(context);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index 5f752cc..a4e4227 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -36,4 +36,6 @@
"home-to-overview-swipe-height";
public static final String REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT =
"background-to-overview-swipe-height";
+ public static final String REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT =
+ "all-apps-to-overview-swipe-height";
}
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 12280f8..12d35e9 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -41,8 +41,6 @@
private static final String TAG = "ConfigMonitor";
- private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-
private final Point mTmpPoint1 = new Point();
private final Point mTmpPoint2 = new Point();
@@ -78,11 +76,6 @@
// Listen for configuration change
mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
- // Listen for {@link OverlayManager} change
- IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
-
// Listen for display manager change
mContext.getSystemService(DisplayManager.class)
.registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
@@ -91,12 +84,6 @@
@Override
public void onReceive(Context context, Intent intent) {
Configuration config = context.getResources().getConfiguration();
- // TODO: when overlay manager service encodes more information to the Uri such as category
- // of the overlay, only listen to the ones that are of interest to launcher.
- if (intent != null && ACTION_OVERLAY_CHANGED.equals(intent.getAction())) {
- Log.d(TAG, "Overlay changed.");
- notifyChange();
- }
if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
Log.d(TAG, "Configuration changed.");
notifyChange();
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
index 2642815..dcc51b5 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
@@ -23,6 +23,8 @@
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.TestProtocol;
+
/**
* Operations on AllApps opened from Overview.
*/
@@ -45,7 +47,11 @@
final UiObject2 qsb = mLauncher.waitForObjectInContainer(
allAppsContainer, "search_container_all_apps");
final Point start = qsb.getVisibleCenter();
- final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.6);
+ final int swipeHeight = mLauncher.getTestInfo(
+ TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT).
+ getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+
+ final int endY = start.y + swipeHeight + mLauncher.getTouchSlop();
LauncherInstrumentation.log("AllAppsFromOverview.switchBackToOverview before swipe");
mLauncher.swipe(start.x, start.y, start.x, endY, OVERVIEW_STATE_ORDINAL);
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 3220691..2cde8ec 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -22,8 +22,6 @@
import static org.junit.Assert.assertTrue;
-import android.view.ViewConfiguration;
-
import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Until;
@@ -67,11 +65,13 @@
final int swipeHeight = mLauncher.getTestInfo(
getSwipeHeightRequestName()).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
- final int slop = ViewConfiguration.get(mLauncher.getContext()).getScaledTouchSlop();
- mLauncher.swipe(centerX, startY, centerX, startY - swipeHeight - slop, expectedState);
+ mLauncher.swipe(
+ centerX, startY, centerX,
+ startY - swipeHeight - mLauncher.getTouchSlop(),
+ expectedState);
} else {
- mLauncher.getSystemUiObject("recent_apps").click();
+ mLauncher.waitForSystemUiObject("recent_apps").click();
}
}
@@ -80,6 +80,6 @@
}
protected int getSwipeStartY() {
- return mLauncher.getSystemUiObject("home").getVisibleBounds().centerY();
+ return mLauncher.waitForSystemUiObject("home").getVisibleBounds().centerY();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 926e470..e3850ff 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -32,6 +32,7 @@
import android.util.Log;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.NonNull;
@@ -288,7 +289,7 @@
executeAndWaitForEvent(
() -> {
log("LauncherInstrumentation.pressHome before clicking");
- getSystemUiObject("home").click();
+ waitForSystemUiObject("home").click();
},
event -> true,
"Pressing Home didn't produce any events");
@@ -298,7 +299,7 @@
executeAndWaitForEvent(
() -> {
log("LauncherInstrumentation.pressHome before clicking");
- getSystemUiObject("home").click();
+ waitForSystemUiObject("home").click();
},
event -> true,
"Pressing Home didn't produce any events");
@@ -394,8 +395,9 @@
}
@NonNull
- UiObject2 getSystemUiObject(String resId) {
- final UiObject2 object = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, resId));
+ UiObject2 waitForSystemUiObject(String resId) {
+ final UiObject2 object = mDevice.wait(
+ Until.findObject(By.res(SYSTEMUI_PACKAGE, resId)), WAIT_TIME_MS);
assertNotNull("Can't find a systemui object with id: " + resId, object);
return object;
}
@@ -467,4 +469,8 @@
float getDisplayDensity() {
return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
}
+
+ int getTouchSlop() {
+ return ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
}
\ 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 0208144..5c8d5eb 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -50,7 +50,7 @@
verifyActiveContainer();
// Swipe from navbar to the top.
- final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame");
+ final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
final Point start = navBar.getVisibleCenter();
LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
mLauncher.swipe(start.x, start.y, start.x, 0, ALL_APPS_STATE_ORDINAL);