Merge "Enabling quick-switch gesture in landscape mode (2 button UI)" into ub-launcher3-qt-dev
diff --git a/Android.mk b/Android.mk
index 6568a26..7956d28 100644
--- a/Android.mk
+++ b/Android.mk
@@ -298,7 +298,7 @@
LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PRODUCT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3QuickStepGo
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep Launcher3QuickStepGo
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
LOCAL_FULL_LIBS_MANIFEST_FILES := \
diff --git a/go/quickstep/res/layout/clear_all_button.xml b/go/quickstep/res/layout/clear_all_button.xml
index be76d53..2f7c8ae 100644
--- a/go/quickstep/res/layout/clear_all_button.xml
+++ b/go/quickstep/res/layout/clear_all_button.xml
@@ -14,22 +14,20 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.quickstep.views.ClearAllItemView
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/clear_all_item_view"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="@dimen/clear_all_item_view_height">
<Button
android:id="@+id/clear_all_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginVertical="16dp"
+ android:layout_width="@dimen/clear_all_button_width"
+ android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
- android:paddingHorizontal="32dp"
android:background="@drawable/clear_all_button"
android:gravity="center"
android:text="@string/recents_clear_all"
android:textAllCaps="false"
android:textColor="@color/clear_all_button_text"
android:textSize="14sp"/>
-</com.android.quickstep.views.ClearAllItemView>
+</FrameLayout>
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index 6fb7e19..b64b7fd 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -21,9 +21,11 @@
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recent_task_recycler_view"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/recents_list_width"
android:layout_height="match_parent"
- android:scrollbars="none"/>
+ android:layout_gravity="center_horizontal"
+ android:scrollbars="none"
+ android:clipToPadding="false"/>
<TextView
android:id="@+id/recent_task_empty_view"
android:layout_width="match_parent"
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index 1483d4c..699178d 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -17,14 +17,13 @@
<com.android.quickstep.views.TaskItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/task_item_height"
android:orientation="horizontal">
<com.android.quickstep.views.TaskThumbnailIconView
android:id="@+id/task_icon_and_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginHorizontal="8dp"
- android:layout_marginTop="16dp">
+ android:layout_marginHorizontal="@dimen/task_thumbnail_icon_horiz_margin">
<ImageView
android:id="@+id/task_thumbnail"
android:layout_width="match_parent"
@@ -39,7 +38,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginHorizontal="8dp"
android:singleLine="true"
android:textColor="@android:color/white"
android:textSize="24sp"/>
diff --git a/go/quickstep/res/values-sw480dp/dimens.xml b/go/quickstep/res/values-sw480dp/dimens.xml
new file mode 100644
index 0000000..b48dafb
--- /dev/null
+++ b/go/quickstep/res/values-sw480dp/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <dimen name="recents_list_width">480dp</dimen>
+
+ <dimen name="task_item_height">90dp</dimen>
+ <dimen name="task_item_top_margin">16dp</dimen>
+ <dimen name="task_thumbnail_icon_horiz_margin">20dp</dimen>
+
+ <dimen name="task_thumbnail_corner_radius">4dp</dimen>
+
+ <dimen name="clear_all_item_view_height">48dp</dimen>
+ <dimen name="clear_all_item_view_top_margin">28dp</dimen>
+ <dimen name="clear_all_item_view_bottom_margin">28dp</dimen>
+ <dimen name="clear_all_button_width">140dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
index ee154fc..91040f2 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/values/dimens.xml
@@ -15,5 +15,16 @@
limitations under the License.
-->
<resources>
+ <dimen name="recents_list_width">320dp</dimen>
+
+ <dimen name="task_item_height">60dp</dimen>
+ <dimen name="task_item_top_margin">16dp</dimen>
+ <dimen name="task_thumbnail_icon_horiz_margin">16dp</dimen>
+
<dimen name="task_thumbnail_corner_radius">3dp</dimen>
+
+ <dimen name="clear_all_item_view_height">36dp</dimen>
+ <dimen name="clear_all_item_view_top_margin">20dp</dimen>
+ <dimen name="clear_all_item_view_bottom_margin">20dp</dimen>
+ <dimen name="clear_all_button_width">106dp</dimen>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/views/ClearAllItemView.java b/go/quickstep/src/com/android/quickstep/views/ClearAllItemView.java
deleted file mode 100644
index 378dbf4..0000000
--- a/go/quickstep/src/com/android/quickstep/views/ClearAllItemView.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.views;
-
-import static com.android.quickstep.views.TaskLayoutUtils.getClearAllItemHeight;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-/**
- * Recycler view item that lays out the clear all button and measures the space it takes based on
- * the device height.
- */
-public final class ClearAllItemView extends FrameLayout {
-
- public ClearAllItemView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int buttonHeight = getClearAllItemHeight(getContext());
- int newHeightSpec = MeasureSpec.makeMeasureSpec(buttonHeight, MeasureSpec.EXACTLY);
- super.onMeasure(widthMeasureSpec, newHeightSpec);
- }
-}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index cf6eb6d..f951304 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -20,6 +20,8 @@
import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
import static com.android.quickstep.TaskAdapter.CHANGE_EVENT_TYPE_EMPTY_TO_CONTENT;
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_TASK;
import static com.android.quickstep.TaskAdapter.TASKS_START_POSITION;
import android.animation.Animator;
@@ -29,6 +31,8 @@
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -44,9 +48,11 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
+import androidx.recyclerview.widget.RecyclerView.ItemDecoration;
import androidx.recyclerview.widget.RecyclerView.OnChildAttachStateChangeListener;
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.quickstep.ContentFillItemAnimator;
import com.android.quickstep.RecentsModel;
@@ -67,7 +73,7 @@
* Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
* base.
*/
-public final class IconRecentsView extends FrameLayout {
+public final class IconRecentsView extends FrameLayout implements Insettable {
public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
new FloatProperty<IconRecentsView>("contentAlpha") {
@@ -117,6 +123,7 @@
private boolean mTransitionedFromApp;
private AnimatorSet mLayoutAnimation;
private final ArraySet<View> mLayingOutViews = new ArraySet<>();
+ private Rect mInsets;
private final RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
ArrayList<TaskItemView> itemViews = getTaskViews();
for (int i = 0, size = itemViews.size(); i < size; i++) {
@@ -174,6 +181,43 @@
mTaskRecyclerView.setItemAnimator(mDefaultItemAnimator);
mLoadingContentItemAnimator.setOnAnimationFinishedRunnable(
() -> mTaskRecyclerView.setItemAnimator(new DefaultItemAnimator()));
+ ItemDecoration marginDecorator = new ItemDecoration() {
+ @Override
+ public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
+ @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+ // TODO: Determine if current margins cause off screen item to be fully off
+ // screen and if so, modify them so that it is partially off screen.
+ int itemType = parent.getChildViewHolder(view).getItemViewType();
+ Resources res = getResources();
+ switch (itemType) {
+ case ITEM_TYPE_CLEAR_ALL:
+ outRect.top = (int) res.getDimension(
+ R.dimen.clear_all_item_view_top_margin);
+ int desiredBottomMargin = (int) res.getDimension(
+ R.dimen.clear_all_item_view_bottom_margin);
+ // Only add bottom margin if insets aren't enough.
+ if (mInsets.bottom < desiredBottomMargin) {
+ outRect.bottom = desiredBottomMargin - mInsets.bottom;
+ }
+ break;
+ case ITEM_TYPE_TASK:
+ int desiredTopMargin = (int) res.getDimension(
+ R.dimen.task_item_top_margin);
+ if (mTaskRecyclerView.getChildAdapterPosition(view) ==
+ state.getItemCount() - 1) {
+ // Only add top margin to top task view if insets aren't enough.
+ if (mInsets.top < desiredTopMargin) {
+ outRect.top = desiredTopMargin - mInsets.bottom;
+ }
+ return;
+ }
+ outRect.top = desiredTopMargin;
+ break;
+ default:
+ }
+ }
+ };
+ mTaskRecyclerView.addItemDecoration(marginDecorator);
mEmptyView = findViewById(R.id.recent_task_empty_view);
mContentView = mTaskRecyclerView;
@@ -188,7 +232,6 @@
updateContentViewVisibility();
}
});
- // TODO: Move layout param logic into onMeasure
}
}
@@ -482,4 +525,11 @@
});
mLayoutAnimation.start();
}
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets = insets;
+ mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ mTaskRecyclerView.invalidateItemDecorations();
+ }
}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 9019205..a5f5728 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.views;
-import static com.android.quickstep.views.TaskLayoutUtils.getTaskHeight;
-
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -46,6 +44,7 @@
private final Drawable mDefaultThumbnail;
private final TaskLayerDrawable mIconDrawable;
private final TaskLayerDrawable mThumbnailDrawable;
+ private View mTaskIconThumbnailView;
private TextView mLabelView;
private ImageView mIconView;
private ImageView mThumbnailView;
@@ -81,6 +80,7 @@
protected void onFinishInflate() {
super.onFinishInflate();
mLabelView = findViewById(R.id.task_label);
+ mTaskIconThumbnailView = findViewById(R.id.task_icon_and_thumbnail);
mThumbnailView = findViewById(R.id.task_thumbnail);
mIconView = findViewById(R.id.task_icon);
@@ -91,13 +91,6 @@
CONTENT_TRANSITION_PROGRESS.setValue(this, 1.0f);
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int taskHeight = getTaskHeight(getContext());
- int newHeightSpec = MeasureSpec.makeMeasureSpec(taskHeight,MeasureSpec.EXACTLY);
- super.onMeasure(widthMeasureSpec, newHeightSpec);
- }
-
/**
* Resets task item view to empty, loading UI.
*/
@@ -195,5 +188,6 @@
((ThumbnailDrawable) drawable).setRequestedOrientation(newConfig.orientation);
}
}
+ mTaskIconThumbnailView.forceLayout();
}
}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java b/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
deleted file mode 100644
index e28a9e0..0000000
--- a/go/quickstep/src/com/android/quickstep/views/TaskLayoutUtils.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.views;
-
-import static com.android.quickstep.TaskAdapter.MAX_TASKS_TO_DISPLAY;
-
-import android.content.Context;
-
-import com.android.launcher3.InvariantDeviceProfile;
-
-/**
- * Utils to determine dynamically task and view sizes based off the device height and width.
- */
-public final class TaskLayoutUtils {
-
- private static final float CLEAR_ALL_ITEM_TO_HEIGHT_RATIO = 7.0f / 64;
-
- private TaskLayoutUtils() {}
-
- /**
- * Calculate task height based off the available height in portrait mode such that when the
- * recents list is full, the total height fills in the available device height perfectly. In
- * landscape mode, we keep the same task height so that tasks scroll off the top.
- *
- * @param context current context
- * @return task height
- */
- public static int getTaskHeight(Context context) {
- final int availableHeight =
- InvariantDeviceProfile.INSTANCE.get(context).portraitProfile.availableHeightPx;
- final int availableTaskSpace = availableHeight - getClearAllItemHeight(context);
- return (int) (availableTaskSpace * 1.0f / MAX_TASKS_TO_DISPLAY);
- }
-
- /**
- * Calculate clear all item height scaled to available height in portrait mode.
- *
- * @param context current context
- * @return clear all item height
- */
- public static int getClearAllItemHeight(Context context) {
- final int availableHeight =
- InvariantDeviceProfile.INSTANCE.get(context).portraitProfile.availableHeightPx;
- return (int) (CLEAR_ALL_ITEM_TO_HEIGHT_RATIO * availableHeight);
- }
-}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
index 0bad77b..eaefa21 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskThumbnailIconView.java
@@ -56,7 +56,6 @@
int width = height;
setMeasuredDimension(width, height);
-
int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
if (mThumbnailView.getVisibility() != GONE) {
boolean isPortrait =
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index f991435..61c576e 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -21,4 +21,6 @@
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
+ <!-- Minimum distance to swipe to trigger accessibility gesture -->
+ <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java
new file mode 100644
index 0000000..8f8cd18
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AccessibilityInputConsumer.java
@@ -0,0 +1,156 @@
+/*
+ * 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.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 android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.R;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Touch consumer for two finger swipe actions for accessibility actions
+ */
+public class AccessibilityInputConsumer extends DelegateInputConsumer {
+
+ private static final String TAG = "A11yInputConsumer";
+
+ private final ISystemUiProxy mSystemUiProxy;
+ private final VelocityTracker mVelocityTracker;
+ private final MotionPauseDetector mMotionPauseDetector;
+ private final boolean mAllowLongClick;
+
+ private final float mMinGestureDistance;
+ private final float mMinFlingVelocity;
+
+ private int mActivePointerId = -1;
+ private float mDownY;
+ private float mTotalY;
+
+ public AccessibilityInputConsumer(Context context, ISystemUiProxy systemUiProxy,
+ boolean allowLongClick, InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
+ mSystemUiProxy = systemUiProxy;
+ mVelocityTracker = VelocityTracker.obtain();
+ mMinGestureDistance = context.getResources()
+ .getDimension(R.dimen.accessibility_gesture_min_swipe_distance);
+ mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+
+ mMotionPauseDetector = new MotionPauseDetector(context);
+ mAllowLongClick = allowLongClick;
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_ACCESSIBILITY | mDelegate.getType();
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ if (mState != STATE_DELEGATE_ACTIVE) {
+ mVelocityTracker.addMovement(ev);
+ }
+
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ break;
+ }
+ case ACTION_POINTER_UP: {
+ if (mState == STATE_ACTIVE) {
+ int pointerIndex = ev.getActionIndex();
+ int pointerId = ev.getPointerId(pointerIndex);
+ if (pointerId == mActivePointerId) {
+ final int newPointerIdx = pointerIndex == 0 ? 1 : 0;
+
+ mTotalY += (ev.getY(pointerIndex) - mDownY);
+ mDownY = ev.getY(newPointerIdx);
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ }
+ break;
+ }
+ case ACTION_POINTER_DOWN: {
+ if (mState == STATE_INACTIVE) {
+ if (mDelegate.allowInterceptByParent()) {
+ setActive(ev);
+
+ int pointerIndex = ev.getActionIndex();
+ mActivePointerId = ev.getPointerId(pointerIndex);
+ mDownY = ev.getY(pointerIndex);
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
+ }
+ }
+ break;
+ }
+ case ACTION_MOVE: {
+ if (mState == STATE_ACTIVE && mAllowLongClick) {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == -1) {
+ break;
+ }
+
+ mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
+ ev.getEventTime());
+ }
+ break;
+ }
+ case ACTION_UP:
+ if (mState == STATE_ACTIVE) {
+ try {
+ if (mAllowLongClick && mMotionPauseDetector.isPaused()) {
+ mSystemUiProxy.notifyAccessibilityButtonLongClicked();
+ } else {
+ mTotalY += (ev.getY() - mDownY);
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ if ((-mTotalY) > mMinGestureDistance
+ || (-mVelocityTracker.getYVelocity()) > mMinFlingVelocity) {
+ mSystemUiProxy.notifyAccessibilityButtonClicked(
+ Display.DEFAULT_DISPLAY);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to notify accessibility event", e);
+ }
+ }
+ // Follow through
+ case ACTION_CANCEL: {
+ mVelocityTracker.recycle();
+ mMotionPauseDetector.clear();
+ break;
+ }
+ }
+
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index e5747dc..624b3dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -137,7 +137,7 @@
Rect targetRect = new Rect();
mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity, targetRect);
clipHelper.updateTargetRect(targetRect);
- clipHelper.prepareAnimation(false /* isOpening */);
+ clipHelper.prepareAnimation(mActivity.getDeviceProfile(), false /* isOpening */);
ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
.setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
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 5e7faf7..829e478 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -50,20 +50,10 @@
/**
* Touch consumer for handling events to launch assistant from launcher
*/
-public class AssistantTouchConsumer implements InputConsumer {
+public class AssistantTouchConsumer extends DelegateInputConsumer {
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 static final String INVOCATION_TYPE_KEY = "invocation_type";
private static final int INVOCATION_TYPE_GESTURE = 1;
@@ -78,7 +68,6 @@
private float mTimeFraction;
private long mDragTime;
private float mLastProgress;
- private int mState;
private int mDirection;
private ActivityControlHelper mActivityControlHelper;
@@ -87,46 +76,25 @@
private final int mAngleThreshold;
private final float mSlop;
private final ISystemUiProxy mSysUiProxy;
- private final InputConsumer mConsumerDelegate;
private final Context mContext;
- private final InputMonitorCompat mInputMonitorCompat;
-
-
public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
- InputConsumer delegate, InputMonitorCompat inputMonitorCompat,
- ActivityControlHelper activityControlHelper) {
+ ActivityControlHelper activityControlHelper, InputConsumer delegate,
+ InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
final Resources res = context.getResources();
mContext = context;
mSysUiProxy = systemUiProxy;
- mConsumerDelegate = delegate;
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 = QuickStepContract.getQuickStepDragSlopPx();
- mInputMonitorCompat = inputMonitorCompat;
mActivityControlHelper = activityControlHelper;
- mState = STATE_INACTIVE;
}
@Override
public int getType() {
- return TYPE_ASSISTANT;
- }
-
- @Override
- public boolean useSharedSwipeState() {
- if (mConsumerDelegate != null) {
- return mConsumerDelegate.useSharedSwipeState();
- }
- return false;
- }
-
- @Override
- public void onConsumerAboutToBeSwitched() {
- if (mConsumerDelegate != null) {
- mConsumerDelegate.onConsumerAboutToBeSwitched();
- }
+ return TYPE_ASSISTANT | mDelegate.getType();
}
@Override
@@ -158,6 +126,10 @@
if (mState == STATE_DELEGATE_ACTIVE) {
break;
}
+ if (!mDelegate.allowInterceptByParent()) {
+ mState = STATE_DELEGATE_ACTIVE;
+ break;
+ }
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
@@ -168,9 +140,6 @@
// 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) {
- // Cancel touches to other windows (intercept)
- mInputMonitorCompat.pilferPointers();
-
mPassedSlop = true;
mStartDragPos.set(mLastPos.x, mLastPos.y);
mDragTime = SystemClock.uptimeMillis();
@@ -182,15 +151,7 @@
angle = angle > 90 ? 180 - angle : angle;
if (angle > mAngleThreshold && angle < 90) {
- mState = STATE_ASSISTANT_ACTIVE;
-
- if (mConsumerDelegate != null) {
- // Send cancel event
- MotionEvent event = MotionEvent.obtain(ev);
- event.setAction(MotionEvent.ACTION_CANCEL);
- mConsumerDelegate.onMotionEvent(event);
- event.recycle();
- }
+ setActive(ev);
} else {
mState = STATE_DELEGATE_ACTIVE;
}
@@ -232,8 +193,8 @@
break;
}
- if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
- mConsumerDelegate.onMotionEvent(ev);
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
}
}
@@ -249,7 +210,8 @@
Bundle args = new Bundle();
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
- BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity();
+ BaseDraggingActivity launcherActivity =
+ mActivityControlHelper.getCreatedActivity();
if (launcherActivity != null) {
launcherActivity.getRootView().
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java
new file mode 100644
index 0000000..d36162f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/DelegateInputConsumer.java
@@ -0,0 +1,49 @@
+package com.android.quickstep;
+
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+public abstract class DelegateInputConsumer implements InputConsumer {
+
+ protected static final int STATE_INACTIVE = 0;
+ protected static final int STATE_ACTIVE = 1;
+ protected static final int STATE_DELEGATE_ACTIVE = 2;
+
+ protected final InputConsumer mDelegate;
+ protected final InputMonitorCompat mInputMonitor;
+
+ protected int mState;
+
+ public DelegateInputConsumer(InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ mDelegate = delegate;
+ mInputMonitor = inputMonitor;
+ mState = STATE_INACTIVE;
+ }
+
+ @Override
+ public boolean useSharedSwipeState() {
+ return mDelegate.useSharedSwipeState();
+ }
+
+ @Override
+ public boolean allowInterceptByParent() {
+ return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
+ }
+
+ @Override
+ public void onConsumerAboutToBeSwitched() {
+ mDelegate.onConsumerAboutToBeSwitched();
+ }
+
+ protected void setActive(MotionEvent ev) {
+ mState = STATE_ACTIVE;
+ mInputMonitor.pilferPointers();
+
+ // Send cancel event
+ MotionEvent event = MotionEvent.obtain(ev);
+ event.setAction(MotionEvent.ACTION_CANCEL);
+ mDelegate.onMotionEvent(event);
+ event.recycle();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
index e3f9e02..37b7288 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -24,11 +24,12 @@
@TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer {
- int TYPE_NO_OP = 0;
- int TYPE_OVERVIEW = 1;
- int TYPE_OTHER_ACTIVITY = 2;
- int TYPE_ASSISTANT = 3;
- int TYPE_DEVICE_LOCKED = 4;
+ int TYPE_NO_OP = 1 << 0;
+ int TYPE_OVERVIEW = 1 << 1;
+ int TYPE_OTHER_ACTIVITY = 1 << 2;
+ int TYPE_ASSISTANT = 1 << 3;
+ int TYPE_DEVICE_LOCKED = 1 << 4;
+ int TYPE_ACCESSIBILITY = 1 << 5;
InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -39,6 +40,13 @@
}
/**
+ * Returns true if the user has crossed the threshold for it to be an explicit action.
+ */
+ default boolean allowInterceptByParent() {
+ return true;
+ }
+
+ /**
* Called by the event queue when the consumer is about to be switched to a new consumer.
*/
default void onConsumerAboutToBeSwitched() { }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 90af521..833a468 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -334,18 +334,21 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mPassedDragSlop && mInteractionHandler != null) {
+ if (ev.getActionMasked() == ACTION_CANCEL) {
+ mInteractionHandler.onGestureCancelled();
+ } else {
+ mVelocityTracker.computeCurrentVelocity(1000,
+ ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+ float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
+ float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
+ float velocity = isNavBarOnRight() ? velocityX
+ : isNavBarOnLeft() ? -velocityX
+ : velocityY;
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
- float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
- float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
- float velocity = isNavBarOnRight() ? velocityX
- : isNavBarOnLeft() ? -velocityX
- : velocityY;
-
- mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
- mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
- mDownPos);
+ mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
+ mInteractionHandler.onGestureEnded(velocity, new PointF(velocityX, velocityY),
+ mDownPos);
+ }
} else {
// Since we start touch tracking on DOWN, we may reach this state without actually
// starting the gesture. In that case, just cleanup immediately.
@@ -376,7 +379,7 @@
mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
if (mSwipeSharedState.canGestureBeContinued) {
- mInteractionHandler.cancel();
+ mInteractionHandler.cancelCurrentAnimation();
} else {
mInteractionHandler.reset();
}
@@ -414,4 +417,9 @@
public boolean useSharedSwipeState() {
return mInteractionHandler != null;
}
+
+ @Override
+ public boolean allowInterceptByParent() {
+ return !mPassedTouchSlop;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
index 32e0e48..b48e3de 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -65,6 +65,11 @@
}
@Override
+ public boolean allowInterceptByParent() {
+ return !mTargetHandledTouch;
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
if (!mProxyTouch) {
return;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index 7c6638a..f393387 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -51,9 +51,16 @@
mLastAnimationRunning = true;
}
+ private void clearAnimationTarget() {
+ if (mLastAnimationTarget != null) {
+ mLastAnimationTarget.release();
+ mLastAnimationTarget = null;
+ }
+ }
+
@Override
public final void onRecentsAnimationCanceled() {
- mLastAnimationTarget = null;
+ clearAnimationTarget();
mLastAnimationCancelled = true;
mLastAnimationRunning = false;
@@ -64,7 +71,7 @@
mRecentsAnimationListener.removeListener(this);
}
mRecentsAnimationListener = null;
- mLastAnimationTarget = null;
+ clearAnimationTarget();
mLastAnimationCancelled = false;
mLastAnimationRunning = false;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 4526d67..f95f9c2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -19,13 +19,18 @@
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.graphics.RectF;
import android.view.View;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
@@ -109,8 +114,14 @@
*/
public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+ SyncRtSurfaceTransactionApplierCompat applier =
+ new SyncRtSurfaceTransactionApplierCompat(v);
ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
- .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
+ .setSyncTransactionApplier(applier);
+
+ final RemoteAnimationTargetSet targetSet =
+ new RemoteAnimationTargetSet(targets, MODE_OPENING);
+ targetSet.addDependentTransactionApplier(applier);
final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
@@ -120,17 +131,17 @@
final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
- final RemoteAnimationTargetSet mTargetSet;
final RectF mThumbnailRect;
{
- mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
- inOutHelper.prepareAnimation(true /* isOpening */);
+ inOutHelper.prepareAnimation(
+ BaseActivity.fromContext(v.getContext()).getDeviceProfile(),
+ true /* isOpening */);
inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
- mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
+ targetSet.apps.length == 0 ? null : targetSet.apps[0]);
mThumbnailRect = new RectF(inOutHelper.getTargetRect());
mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
@@ -140,7 +151,7 @@
@Override
public void onUpdate(float percent) {
params.setProgress(1 - percent);
- RectF taskBounds = inOutHelper.applyTransform(mTargetSet, params);
+ RectF taskBounds = inOutHelper.applyTransform(targetSet, params);
if (!skipViewChanges) {
float scale = taskBounds.width() / mThumbnailRect.width();
v.setScaleX(scale);
@@ -151,6 +162,12 @@
}
}
});
+ appAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ targetSet.release();
+ }
+ });
return appAnimator;
}
}
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 d7d0c36..dfcc2b7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -20,6 +20,8 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
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_SCREEN_PINNING;
@@ -67,9 +69,8 @@
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
-
-import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -456,25 +457,32 @@
final ActivityControlHelper activityControl =
mOverviewComponentObserver.getActivityControlHelper();
+ InputConsumer base;
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
- return InputConsumer.NO_OP;
- } else if (mAssistantAvailable
- && SysUINavigationMode.INSTANCE.get(this).getMode() == Mode.NO_BUTTON
- && AssistantTouchConsumer.withinTouchRegion(this, event)) {
-
- boolean addDelegate = !activityControl.isResumed();
- return new AssistantTouchConsumer(this, mISystemUiProxy, addDelegate ?
- createOtherActivityInputConsumer(event, runningTaskInfo) : null,
- mInputMonitorCompat, activityControl);
-
+ base = InputConsumer.NO_OP;
} else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
- return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
+ base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
activityControl.isInLiveTileMode()) {
- return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
+ base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else {
- return createOtherActivityInputConsumer(event, runningTaskInfo);
+ base = createOtherActivityInputConsumer(event, runningTaskInfo);
}
+
+ if (mMode == Mode.NO_BUTTON) {
+ if (mAssistantAvailable && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+ base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base,
+ mInputMonitorCompat);
+ }
+
+ if ((mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0) {
+ base = new AccessibilityInputConsumer(this, mISystemUiProxy,
+ (mSystemUiStateFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0, base,
+ mInputMonitorCompat);
+ }
+ }
+
+ return base;
}
private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index c94ad69..2471f64 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -250,7 +250,6 @@
private T mActivity;
private RecentsView mRecentsView;
- private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
private AnimationFactory mAnimationFactory = (t) -> { };
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
@@ -405,8 +404,11 @@
}
mRecentsView = activity.getOverviewPanel();
- SyncRtSurfaceTransactionApplierCompat.create(mRecentsView,
- applier -> mSyncTransactionApplier = applier );
+ SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, applier -> {
+ mTransformParams.setSyncTransactionApplier(applier);
+ mRecentsAnimationWrapper.runOnInit(() ->
+ mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier));
+ });
mRecentsView.setEnableFreeScroll(false);
mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
@@ -648,8 +650,7 @@
}
float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
mClipAnimationHelper.getTargetRect().width());
- mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
- .setSyncTransactionApplier(mSyncTransactionApplier);
+ mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale);
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
mTransformParams);
mRecentsAnimationWrapper.setWindowThresholdCrossed(
@@ -729,7 +730,7 @@
if (runningTaskTarget != null) {
mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
}
- mClipAnimationHelper.prepareAnimation(false /* isOpening */);
+ mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */);
initTransitionEndpoints(dp);
mRecentsAnimationWrapper.setController(targetSet);
@@ -769,6 +770,17 @@
}
/**
+ * Called as a result on ACTION_CANCEL to return the UI to the start state.
+ */
+ @UiThread
+ public void onGestureCancelled() {
+ updateDisplacement(0);
+ setStateOnUiThread(STATE_GESTURE_COMPLETED);
+ mLogAction = Touch.SWIPE_NOOP;
+ handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
+ }
+
+ /**
* @param endVelocity The velocity in the direction of the nav bar to the middle of the screen.
* @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.
@@ -788,7 +800,7 @@
mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT;
}
mDownPos = downPos;
- handleNormalGestureEnd(endVelocity, isFling, velocity);
+ handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
@UiThread
@@ -806,7 +818,8 @@
}
@UiThread
- private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity) {
+ private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
+ boolean isCancel) {
PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000);
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
@@ -824,7 +837,9 @@
}
final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
if (!isFling) {
- if (mMode == Mode.NO_BUTTON) {
+ if (isCancel) {
+ endTarget = LAST_TASK;
+ } else if (mMode == Mode.NO_BUTTON) {
if (mIsShelfPeeking) {
endTarget = RECENTS;
} else if (goingToNewTask) {
@@ -907,7 +922,7 @@
private void doLogGesture(GestureEndTarget endTarget) {
DeviceProfile dp = mDp;
- if (dp == null) {
+ if (dp == null || mDownPos == null) {
// We probably never received an animation controller, skip logging.
return;
}
@@ -1047,8 +1062,7 @@
float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0,
windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR);
- mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha)
- .setSyncTransactionApplier(mSyncTransactionApplier);
+ mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha);
mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
false /* launcherOnTop */);
@@ -1062,7 +1076,9 @@
@Override
public void onAnimationStart(Animator animation) {
homeAnim.dispatchOnStart();
- mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+ if (mActivity != null) {
+ mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
+ }
}
@Override
@@ -1106,7 +1122,11 @@
setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
- public void cancel() {
+ /**
+ * 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() {
mCurrentShift.cancelAnimation();
if (mLauncherTransitionController != null && mLauncherTransitionController
.getAnimationPlayer().isStarted()) {
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 1242d79..cb2bf1f 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
@@ -81,6 +81,7 @@
private final RectF mClipRectF = new RectF();
private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
private final Matrix mTmpMatrix = new Matrix();
+ private final Rect mTmpRect = new Rect();
private final RectF mTmpRectF = new RectF();
private final RectF mCurrentRectWithInsets = new RectF();
// Corner radius of windows, in pixels
@@ -89,6 +90,8 @@
private final float mTaskCornerRadius;
// If windows can have real time rounded corners.
private final boolean mSupportsRoundedCornersOnWindows;
+ // Whether or not to actually use the rounded cornders on windows
+ private boolean mUseRoundedCornersOnWindows;
// Corner radius currently applied to transformed window.
private float mCurrentCornerRadius;
@@ -103,6 +106,7 @@
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
mTaskCornerRadius = TaskCornerRadius.get(context);
+ mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
}
private void updateSourceStack(RemoteAnimationTargetCompat target) {
@@ -144,8 +148,9 @@
mSourceRect.set(scaledTargetRect);
}
- public void prepareAnimation(boolean isOpening) {
+ public void prepareAnimation(DeviceProfile dp, boolean isOpening) {
mBoostModeTargetLayers = isOpening ? MODE_OPENING : MODE_CLOSING;
+ mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows && !dp.isMultiWindowMode;
}
public RectF applyTransform(RemoteAnimationTargetSet targetSet, TransformParams params) {
@@ -177,7 +182,9 @@
for (int i = 0; i < targetSet.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = targetSet.unfilteredApps[i];
mTmpMatrix.setTranslate(app.position.x, app.position.y);
- Rect crop = app.sourceContainerBounds;
+ Rect crop = mTmpRect;
+ crop.set(app.sourceContainerBounds);
+ crop.offsetTo(0, 0);
float alpha = 1f;
int layer = RemoteAnimationProvider.getLayer(app, mBoostModeTargetLayers);
float cornerRadius = 0f;
@@ -188,7 +195,9 @@
mTmpMatrix.postTranslate(app.position.x, app.position.y);
mClipRectF.roundOut(crop);
if (mSupportsRoundedCornersOnWindows) {
- cornerRadius = Utilities.mapRange(params.progress, mWindowCornerRadius,
+ float windowCornerRadius = mUseRoundedCornersOnWindows
+ ? mWindowCornerRadius : 0;
+ cornerRadius = Utilities.mapRange(params.progress, windowCornerRadius,
mTaskCornerRadius);
mCurrentCornerRadius = cornerRadius;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
index 5a1a103..83973fa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
@@ -95,9 +95,4 @@
void onRecentsAnimationCanceled();
}
-
- public interface SwipeAnimationFinishListener {
-
- void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet);
- }
}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index cda9d4f..a8666f9 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -416,17 +416,15 @@
RemoteAnimationTargetSet openingTargets = new RemoteAnimationTargetSet(targets,
MODE_OPENING);
- RemoteAnimationTargetSet closingTargets = new RemoteAnimationTargetSet(targets,
- MODE_CLOSING);
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mFloatingView);
+ openingTargets.addDependentTransactionApplier(surfaceApplier);
// Scale the app icon to take up the entire screen. This simplifies the math when
// animating the app window position / scale.
- float maxScaleX = windowTargetBounds.width() / (float) bounds.width();
- // We use windowTargetBounds.width for scaleY too since we start off the animation where the
- // window is clipped to a square.
- float maxScaleY = windowTargetBounds.width() / (float) bounds.height();
+ float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
+ float maxScaleX = smallestSize / (float) bounds.width();
+ float maxScaleY = smallestSize / (float) bounds.height();
float scale = Math.max(maxScaleX, maxScaleY);
float startScale = 1f;
if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
@@ -470,6 +468,7 @@
if (v instanceof BubbleTextView) {
((BubbleTextView) v).setStayPressed(false);
}
+ openingTargets.release();
}
});
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
index c372485..0df4e94 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationTargetSet.java
@@ -16,14 +16,20 @@
package com.android.quickstep.util;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import java.util.ArrayDeque;
import java.util.ArrayList;
+import java.util.Queue;
/**
* Holds a collection of RemoteAnimationTargets, filtered by different properties.
*/
public class RemoteAnimationTargetSet {
+ private final Queue<SyncRtSurfaceTransactionApplierCompat> mDependentTransactionAppliers =
+ new ArrayDeque<>(1);
+
public final RemoteAnimationTargetCompat[] unfilteredApps;
public final RemoteAnimationTargetCompat[] apps;
public final int targetMode;
@@ -60,4 +66,19 @@
}
return false;
}
+
+ public void addDependentTransactionApplier(SyncRtSurfaceTransactionApplierCompat delay) {
+ mDependentTransactionAppliers.add(delay);
+ }
+
+ public void release() {
+ SyncRtSurfaceTransactionApplierCompat applier = mDependentTransactionAppliers.poll();
+ if (applier == null) {
+ for (RemoteAnimationTargetCompat target : unfilteredApps) {
+ target.release();
+ }
+ } else {
+ applier.addAfterApplyCallback(this::release);
+ }
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index 43f6039..72de80b 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -37,6 +37,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,7 +50,6 @@
@LargeTest
@RunWith(AndroidJUnit4.class)
public class AppPredictionsUITests extends AbstractQuickStepTest {
- private static final int DEFAULT_APP_LAUNCH_TIMES = 3;
private static final String TAG = "AppPredictionsUITests";
private LauncherActivityInfo mSampleApp1;
@@ -86,6 +86,7 @@
* Test that prediction UI is updated as soon as we get predictions from the system
*/
@Test
+ @Ignore // b/131188880
public void testPredictionExistsInAllApps() {
mActivityMonitor.startLauncher();
mLauncher.pressHome().switchToAllApps();
@@ -106,6 +107,7 @@
* Test tat prediction update is deferred if it is already visible
*/
@Test
+ @Ignore // b/131188880
public void testPredictionsDeferredUntilHome() {
mActivityMonitor.startLauncher();
sendPredictionUpdate(mSampleApp1, mSampleApp2);
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 20fdff2..960d907 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -44,6 +44,7 @@
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -97,6 +98,7 @@
@NavigationModeSwitch(mode = THREE_BUTTON)
@Test
+ @Ignore // b/131630813
public void goToOverviewFromHome() {
mDevice.pressHome();
assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
@@ -107,6 +109,7 @@
@NavigationModeSwitch(mode = THREE_BUTTON)
@Test
+ @Ignore // b/131630813
public void goToOverviewFromApp() {
startAppFast("com.android.settings");
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index f2fc718..77f278a 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -78,6 +78,7 @@
private final int mBlurSizeOutline;
+ private boolean mIsVerticalBarLayout = false;
private boolean mIsAdaptiveIcon = false;
private @Nullable Drawable mForeground;
@@ -273,7 +274,7 @@
}
float aspectRatio = launcher.getDeviceProfile().aspectRatio;
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ if (mIsVerticalBarLayout) {
lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
} else {
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
@@ -318,8 +319,13 @@
mBgDrawableBounds.set(mFinalDrawableBounds);
Utilities.scaleRectAboutCenter(mBgDrawableBounds, scale);
// Since the drawable is at the top of the view, we need to offset to keep it centered.
- mBgDrawableBounds.offsetTo(mBgDrawableBounds.left,
- (int) (mFinalDrawableBounds.top * scale));
+ if (mIsVerticalBarLayout) {
+ mBgDrawableBounds.offsetTo((int) (mFinalDrawableBounds.left * scale),
+ mBgDrawableBounds.top);
+ } else {
+ mBgDrawableBounds.offsetTo(mBgDrawableBounds.left,
+ (int) (mFinalDrawableBounds.top * scale));
+ }
mBackground.setBounds(mBgDrawableBounds);
}
@@ -410,6 +416,7 @@
recycle.recycle();
}
FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher);
+ view.mIsVerticalBarLayout = launcher.getDeviceProfile().isVerticalBarLayout();
// Match the position of the original view.
view.matchPositionOf(launcher, originalView, positionOut);
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 3e84440..dedc6b3 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -111,7 +111,7 @@
mLauncher = new LauncherInstrumentation(instrumentation);
// b/130558787; b/131419978
- if (TestHelpers.isInLauncherProcess()) {
+ if (TestHelpers.isInLauncherProcess() && !LauncherInstrumentation.needSlowGestures()) {
try {
Class systemProps = Class.forName("android.os.SystemProperties");
Method getInt = systemProps.getMethod("getInt", String.class, int.class);
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 74a17ce..70d8cf7 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -28,7 +28,7 @@
* Common overview pane for both Launcher and fallback recents
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
- private static final int FLING_SPEED = 1500;
+ private static final int FLING_SPEED = LauncherInstrumentation.needSlowGestures() ? 500 : 1500;
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
BaseOverview(LauncherInstrumentation launcher) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 27bc43e..f5c5a8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -191,7 +191,7 @@
return NavigationModel.THREE_BUTTON;
}
- static boolean needSlowGestures() {
+ public static boolean needSlowGestures() {
return Build.MODEL.contains("Cuttlefish");
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 0b3bbd2..85e112a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -36,7 +36,8 @@
* Operations on the workspace screen.
*/
public final class Workspace extends Home {
- private static final float FLING_SPEED = 3500.0F;
+ private static final float FLING_SPEED =
+ LauncherInstrumentation.needSlowGestures() ? 1500.0F : 3500.0F;
private static final int DRAG_DURACTION = 2000;
private final UiObject2 mHotseat;