Merging ub-launcher3-qt-dev, build 5528135

am: 4c8e5dd275

Change-Id: Ic9ece52afbf3b61453a17bc4f85f79093622852c
This commit is contained in:
Sunny Goyal
2019-05-03 17:27:08 -07:00
committed by android-build-merger
102 changed files with 1931 additions and 1314 deletions
+1 -1
View File
@@ -298,7 +298,7 @@ LOCAL_PROGUARD_ENABLED := full
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 := \
+5 -7
View File
@@ -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>
@@ -18,12 +18,16 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:orientation="vertical"
android:clipChildren="false">
<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"
android:clipChildren="false"/>
<TextView
android:id="@+id/recent_task_empty_view"
android:layout_width="match_parent"
+2 -4
View File
@@ -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"/>
@@ -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>
+11
View File
@@ -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>
@@ -52,11 +52,18 @@ public class OverviewState extends LauncherState {
public void onStateEnabled(Launcher launcher) {
IconRecentsView recentsView = launcher.getOverviewPanel();
recentsView.onBeginTransitionToOverview();
recentsView.setShowStatusBarForegroundScrim(true);
// Request orientation be set to unspecified, letting the system decide the best
// orientation.
launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
}
@Override
public void onStateDisabled(Launcher launcher) {
IconRecentsView recentsView = launcher.getOverviewPanel();
recentsView.setShowStatusBarForegroundScrim(false);
}
@Override
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
return new PageAlphaProvider(DEACCEL_2) {
@@ -37,6 +37,7 @@ public final class RecentsActivity extends BaseRecentsActivity {
mRecentsRootView = findViewById(R.id.drag_layer);
mIconRecentsView = findViewById(R.id.overview_panel);
mIconRecentsView.setRecentsToActivityHelper(new FallbackRecentsToActivityHelper(this));
mIconRecentsView.setShowStatusBarForegroundScrim(true);
}
@Override
@@ -80,7 +80,8 @@ public final class TaskListLoader {
return;
}
// TODO: Look into error checking / more robust handling for when things go wrong.
mTaskListChangeId = mRecentsModel.getTasks(tasks -> {
mTaskListChangeId = mRecentsModel.getTasks(loadedTasks -> {
ArrayList<Task> tasks = new ArrayList<>(loadedTasks);
// Reverse tasks to put most recent at the bottom of the view
Collections.reverse(tasks);
// Load task content
@@ -19,21 +19,25 @@ import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
import android.graphics.Canvas;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import java.util.function.Consumer;
/**
* Callback for swipe input on {@link TaskHolder} views in the recents view.
*/
public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
private final TaskActionController mTaskActionController;
private final Consumer<TaskHolder> mOnTaskSwipeCallback;
public TaskSwipeCallback(TaskActionController taskActionController) {
public TaskSwipeCallback(Consumer<TaskHolder> onTaskSwipeCallback) {
super(0 /* dragDirs */, RIGHT);
mTaskActionController = taskActionController;
mOnTaskSwipeCallback = onTaskSwipeCallback;
}
@Override
@@ -45,10 +49,22 @@ public final class TaskSwipeCallback extends ItemTouchHelper.SimpleCallback {
@Override
public void onSwiped(ViewHolder viewHolder, int direction) {
if (direction == RIGHT) {
mTaskActionController.removeTask((TaskHolder) viewHolder);
mOnTaskSwipeCallback.accept((TaskHolder) viewHolder);
}
}
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,
@NonNull ViewHolder viewHolder, float dX, float dY, int actionState,
boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
float alpha = 1.0f - dX / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY,
actionState, isCurrentlyActive);
}
@Override
public int getSwipeDirs(@NonNull RecyclerView recyclerView,
@NonNull ViewHolder viewHolder) {
@@ -57,6 +57,7 @@ public final class ThumbnailDrawable extends Drawable {
mCornerRadius = (int) res.getDimension(R.dimen.task_thumbnail_corner_radius);
mShader = new BitmapShader(mThumbnailData.thumbnail, CLAMP, CLAMP);
mPaint.setShader(mShader);
mPaint.setAntiAlias(true);
updateMatrix();
}
@@ -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);
}
}
@@ -20,6 +20,8 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
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,9 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -44,10 +49,13 @@ import androidx.recyclerview.widget.ItemTouchHelper;
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.launcher3.util.Themes;
import com.android.quickstep.ContentFillItemAnimator;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.RecentsToActivityHelper;
@@ -67,7 +75,7 @@ import java.util.Optional;
* 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") {
@@ -108,6 +116,8 @@ public final class IconRecentsView extends FrameLayout {
private final DefaultItemAnimator mDefaultItemAnimator = new DefaultItemAnimator();
private final ContentFillItemAnimator mLoadingContentItemAnimator =
new ContentFillItemAnimator();
private final BaseActivity mActivity;
private final Drawable mStatusBarForegroundScrim;
private RecentsToActivityHelper mActivityHelper;
private RecyclerView mTaskRecyclerView;
@@ -117,6 +127,7 @@ public final class IconRecentsView extends FrameLayout {
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++) {
@@ -136,8 +147,10 @@ public final class IconRecentsView extends FrameLayout {
public IconRecentsView(Context context, AttributeSet attrs) {
super(context, attrs);
BaseActivity activity = BaseActivity.fromContext(context);
mActivity = BaseActivity.fromContext(context);
mContext = context;
mStatusBarForegroundScrim =
Themes.getAttrDrawable(mContext, R.attr.workspaceStatusBarScrim);
mTaskLoader = new TaskListLoader(mContext);
mTaskAdapter = new TaskAdapter(mTaskLoader);
mTaskAdapter.setOnClearAllClickListener(view -> animateClearAllTasks());
@@ -155,7 +168,12 @@ public final class IconRecentsView extends FrameLayout {
mTaskRecyclerView.setAdapter(mTaskAdapter);
mTaskRecyclerView.setLayoutManager(mTaskLayoutManager);
ItemTouchHelper helper = new ItemTouchHelper(
new TaskSwipeCallback(mTaskActionController));
new TaskSwipeCallback(holder -> {
mTaskActionController.removeTask(holder);
if (mTaskLoader.getCurrentTaskList().isEmpty()) {
mActivityHelper.leaveRecents();
}
}));
helper.attachToRecyclerView(mTaskRecyclerView);
mTaskRecyclerView.addOnChildAttachStateChangeListener(
new OnChildAttachStateChangeListener() {
@@ -174,6 +192,43 @@ public final class IconRecentsView extends FrameLayout {
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 +243,6 @@ public final class IconRecentsView extends FrameLayout {
updateContentViewVisibility();
}
});
// TODO: Move layout param logic into onMeasure
}
}
@@ -295,6 +349,21 @@ public final class IconRecentsView extends FrameLayout {
mTaskActionController.launchTask(taskToLaunch);
}
/**
* Set whether or not to show the scrim in between the view and the top insets. This only works
* if the view is being insetted in the first place.
*
* The scrim is added to the activity's root view to prevent animations on this view
* affecting the scrim. As a result, it is the activity's responsibility to show/hide this
* scrim as appropriate.
*
* @param showStatusBarForegroundScrim true to show the scrim, false to hide
*/
public void setShowStatusBarForegroundScrim(boolean showStatusBarForegroundScrim) {
boolean shouldShow = mInsets.top != 0 && showStatusBarForegroundScrim;
mActivity.getDragLayer().setForeground(shouldShow ? mStatusBarForegroundScrim : null);
}
/**
* Get the bottom most thumbnail view to animate to.
*
@@ -395,7 +464,6 @@ public final class IconRecentsView extends FrameLayout {
if (mShowingContentView != mEmptyView && taskListSize == 0) {
mShowingContentView = mEmptyView;
crossfadeViews(mEmptyView, mContentView);
mActivityHelper.leaveRecents();
}
if (mShowingContentView != mContentView && taskListSize > 0) {
mShowingContentView = mContentView;
@@ -482,4 +550,11 @@ public final class IconRecentsView extends FrameLayout {
});
mLayoutAnimation.start();
}
@Override
public void setInsets(Rect insets) {
mInsets = insets;
mTaskRecyclerView.setPadding(insets.left, insets.top, insets.right, insets.bottom);
mTaskRecyclerView.invalidateItemDecorations();
}
}
@@ -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,10 +44,12 @@ public final class TaskItemView extends LinearLayout {
private final Drawable mDefaultThumbnail;
private final TaskLayerDrawable mIconDrawable;
private final TaskLayerDrawable mThumbnailDrawable;
private View mTaskIconThumbnailView;
private TextView mLabelView;
private ImageView mIconView;
private ImageView mThumbnailView;
private float mContentTransitionProgress;
private int mDisplayedOrientation;
/**
* Property representing the content transition progress of the view. 1.0f represents that the
@@ -81,6 +81,7 @@ public final class TaskItemView extends LinearLayout {
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 +92,6 @@ public final class TaskItemView extends LinearLayout {
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.
*/
@@ -185,15 +179,30 @@ public final class TaskItemView extends LinearLayout {
return (label != null) ? label : DEFAULT_LABEL;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
onOrientationChanged(getResources().getConfiguration().orientation);
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
onOrientationChanged(newConfig.orientation);
}
private void onOrientationChanged(int newOrientation) {
if (mDisplayedOrientation == newOrientation) {
return;
}
mDisplayedOrientation = newOrientation;
int layerCount = mThumbnailDrawable.getNumberOfLayers();
for (int i = 0; i < layerCount; i++) {
Drawable drawable = mThumbnailDrawable.getDrawable(i);
if (drawable instanceof ThumbnailDrawable) {
((ThumbnailDrawable) drawable).setRequestedOrientation(newConfig.orientation);
((ThumbnailDrawable) drawable).setRequestedOrientation(newOrientation);
}
}
mTaskIconThumbnailView.forceLayout();
}
}
@@ -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);
}
}
@@ -56,7 +56,6 @@ final class TaskThumbnailIconView extends ViewGroup {
int width = height;
setMeasuredDimension(width, height);
int subItemSize = (int) (SUBITEM_FRAME_RATIO * height);
if (mThumbnailView.getVisibility() != GONE) {
boolean isPortrait =
@@ -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>
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.appprediction;
import static com.android.launcher3.appprediction.PredictionUiStateManager.KEY_APP_SUGGESTION;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import android.annotation.TargetApi;
import android.app.prediction.AppPredictionContext;
@@ -26,8 +26,6 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
@@ -35,12 +33,10 @@ import android.os.UserHandle;
import android.util.Log;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -48,8 +44,7 @@ import androidx.annotation.WorkerThread;
* Subclass of app tracker which publishes the data to the prediction engine and gets back results.
*/
@TargetApi(Build.VERSION_CODES.Q)
public class PredictionAppTracker extends AppLaunchTracker
implements OnSharedPreferenceChangeListener {
public class PredictionAppTracker extends AppLaunchTracker {
private static final String TAG = "PredictionAppTracker";
private static final boolean DBG = false;
@@ -62,8 +57,6 @@ public class PredictionAppTracker extends AppLaunchTracker
private final Context mContext;
private final Handler mMessageHandler;
private boolean mEnabled;
// Accessed only on worker thread
private AppPredictor mHomeAppPredictor;
private AppPredictor mRecentsOverviewPredictor;
@@ -71,24 +64,16 @@ public class PredictionAppTracker extends AppLaunchTracker
public PredictionAppTracker(Context context) {
mContext = context;
mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage);
SharedPreferences prefs = Utilities.getPrefs(context);
setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
prefs.registerOnSharedPreferenceChangeListener(this);
InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
mMessageHandler.sendEmptyMessage(MSG_INIT);
}
@UiThread
private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
// Reinitialize everything
setEnabled(mEnabled);
}
@Override
@UiThread
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (KEY_APP_SUGGESTION.equals(key)) {
setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
// Reinitialize everything
mMessageHandler.sendEmptyMessage(MSG_INIT);
}
}
@@ -137,13 +122,13 @@ public class PredictionAppTracker extends AppLaunchTracker
return true;
}
case MSG_LAUNCH: {
if (mEnabled && mHomeAppPredictor != null) {
if (mHomeAppPredictor != null) {
mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj);
}
return true;
}
case MSG_PREDICT: {
if (mEnabled && mHomeAppPredictor != null) {
if (mHomeAppPredictor != null) {
String client = (String) msg.obj;
if (Client.HOME.id.equals(client)) {
mHomeAppPredictor.requestPredictionUpdate();
@@ -168,18 +153,6 @@ public class PredictionAppTracker extends AppLaunchTracker
}
}
@UiThread
public void setEnabled(boolean isEnabled) {
mEnabled = isEnabled;
if (isEnabled) {
mMessageHandler.removeMessages(MSG_DESTROY);
mMessageHandler.sendEmptyMessage(MSG_INIT);
} else {
mMessageHandler.removeMessages(MSG_INIT);
mMessageHandler.sendEmptyMessage(MSG_DESTROY);
}
}
@Override
@UiThread
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
@@ -24,8 +24,7 @@ import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Handler;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import com.android.launcher3.AppInfo;
@@ -61,9 +60,10 @@ import java.util.List;
* that client id.
*/
public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
OnSharedPreferenceChangeListener, OnIDPChangeListener, OnUpdateListener {
OnIDPChangeListener, OnUpdateListener {
public static final String KEY_APP_SUGGESTION = "pref_show_predictions";
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
private static final long INITIAL_CALLBACK_WAIT_TIMEOUT_MS = 5000;
// TODO (b/129421797): Update the client constants
public enum Client {
@@ -81,7 +81,6 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
new MainThreadInitializedObject<>(PredictionUiStateManager::new);
private final Context mContext;
private final SharedPreferences mMainPrefs;
private final DynamicItemCache mDynamicItemCache;
private final List[] mPredictionServicePredictions;
@@ -94,9 +93,10 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
private PredictionState mPendingState;
private PredictionState mCurrentState;
private boolean mGettingValidPredictionResults;
private PredictionUiStateManager(Context context) {
mContext = context;
mMainPrefs = Utilities.getPrefs(context);
mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
@@ -110,8 +110,14 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
for (int i = 0; i < mPredictionServicePredictions.length; i++) {
mPredictionServicePredictions[i] = Collections.emptyList();
}
// Listens for enable/disable signal, and predictions if using AiAi is disabled.
mMainPrefs.registerOnSharedPreferenceChangeListener(this);
mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
.getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
if (mGettingValidPredictionResults) {
new Handler().postDelayed(
this::updatePredictionStateAfterCallback, INITIAL_CALLBACK_WAIT_TIMEOUT_MS);
}
// Call this last
mCurrentState = parseLastState();
}
@@ -177,13 +183,6 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
}
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (KEY_APP_SUGGESTION.equals(key)) {
dispatchOnChange(true);
}
}
private void applyState(PredictionState state) {
boolean wasEnabled = mCurrentState.isEnabled;
mCurrentState = state;
@@ -198,10 +197,24 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
}
}
private void updatePredictionStateAfterCallback() {
boolean validResults = false;
for (List l : mPredictionServicePredictions) {
validResults |= l != null && !l.isEmpty();
}
if (validResults != mGettingValidPredictionResults) {
mGettingValidPredictionResults = validResults;
Utilities.getDevicePrefs(mContext).edit()
.putBoolean(LAST_PREDICTION_ENABLED_STATE, true)
.apply();
}
dispatchOnChange(true);
}
public AppPredictor.Callback appPredictorCallback(Client client) {
return targets -> {
mPredictionServicePredictions[client.ordinal()] = targets;
dispatchOnChange(true);
updatePredictionStateAfterCallback();
};
}
@@ -217,7 +230,7 @@ public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInf
private PredictionState parseLastState() {
PredictionState state = new PredictionState();
state.isEnabled = mMainPrefs.getBoolean(KEY_APP_SUGGESTION, true);
state.isEnabled = mGettingValidPredictionResults;
if (!state.isEnabled) {
state.apps = Collections.EMPTY_LIST;
return state;
@@ -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);
}
}
}
@@ -137,7 +137,7 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
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));
@@ -50,20 +50,10 @@ import com.android.systemui.shared.system.QuickStepContract;
/**
* 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 @@ public class AssistantTouchConsumer implements InputConsumer {
private float mTimeFraction;
private long mDragTime;
private float mLastProgress;
private int mState;
private int mDirection;
private ActivityControlHelper mActivityControlHelper;
@@ -87,46 +76,25 @@ public class AssistantTouchConsumer implements InputConsumer {
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 @@ public class AssistantTouchConsumer implements InputConsumer {
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 @@ public class AssistantTouchConsumer implements InputConsumer {
// 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 @@ public class AssistantTouchConsumer implements InputConsumer {
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 @@ public class AssistantTouchConsumer implements InputConsumer {
break;
}
if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
mConsumerDelegate.onMotionEvent(ev);
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
}
}
@@ -249,7 +210,8 @@ public class AssistantTouchConsumer implements InputConsumer {
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,
@@ -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();
}
}
@@ -24,11 +24,12 @@ import android.view.MotionEvent;
@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;
@@ -38,6 +39,13 @@ public interface InputConsumer {
return false;
}
/**
* 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.
*/
@@ -116,10 +116,12 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
} else {
workspaceView = null;
}
final Rect iconLocation = new Rect();
final FloatingIconView floatingView = workspaceView == null ? null
: FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */, null /* recycle */);
final RectF iconLocation = new RectF();
boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
final FloatingIconView floatingView = canUseWorkspaceView
? FloatingIconView.getFloatingIconView(activity, workspaceView,
true /* hideOriginal */, iconLocation, false /* isOpening */, null /* recycle */)
: null;
return new HomeAnimationFactory() {
@Nullable
@@ -135,8 +137,8 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
final float targetCenterX = dp.availableWidthPx / 2f;
final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
if (workspaceView != null) {
return new RectF(iconLocation);
if (canUseWorkspaceView) {
return iconLocation;
} else {
// Fallback to animate to center of screen.
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
@@ -308,7 +310,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
SCALE_PROPERTY.set(recentsView, targetRvScale);
recentsView.setTranslationY(0);
ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher);
float tmpCurveScale = v.getCurveScale();
v.setCurveScale(1f);
clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
v.setCurveScale(tmpCurveScale);
SCALE_PROPERTY.set(recentsView, prevRvScale);
recentsView.setTranslationY(prevRvTransY);
@@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
@@ -34,20 +35,17 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import androidx.annotation.UiThread;
import com.android.launcher3.R;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
@@ -61,10 +59,11 @@ import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.function.Consumer;
import androidx.annotation.UiThread;
/**
* Input consumer for handling events originating from an activity other than Launcher
*/
@@ -80,14 +79,12 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
private final Intent mHomeIntent;
private final ActivityControlHelper mActivityControlHelper;
private final OverviewCallbacks mOverviewCallbacks;
private final TaskOverlayFactory mTaskOverlayFactory;
private final InputConsumerController mInputConsumer;
private final SwipeSharedState mSwipeSharedState;
private final InputMonitorCompat mInputMonitorCompat;
private final SysUINavigationMode.Mode mMode;
private final int mDisplayRotation;
private final Rect mStableInsets = new Rect();
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
@@ -122,7 +119,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
InputConsumerController inputConsumer,
Consumer<OtherActivityInputConsumer> onCompleteCallback,
SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat) {
super(base);
@@ -144,14 +141,10 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
boolean continuingPreviousGesture = swipeSharedState.getActiveListener() != null;
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
mOverviewCallbacks = overviewCallbacks;
mTaskOverlayFactory = taskOverlayFactory;
mInputConsumer = inputConsumer;
mSwipeSharedState = swipeSharedState;
Display display = getSystemService(WindowManager.class).getDefaultDisplay();
mDisplayRotation = display.getRotation();
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
mDisplayRotation = getSystemService(WindowManager.class).getDefaultDisplay().getRotation();
mDragSlop = QuickStepContract.getQuickStepDragSlopPx();
mTouchSlop = QuickStepContract.getQuickStepTouchSlopPx();
@@ -170,16 +163,17 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
// Proxy events to recents view
if (!isNavBarOnLeft() && !isNavBarOnRight()) {
if (mPassedDragSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher());
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
mRecentsViewDispatcher.dispatchEvent(ev);
ev.setEdgeFlags(edgeFlags);
if (mPassedDragSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
isNavBarOnLeft()
? RotationMode.SEASCAPE
: (isNavBarOnRight() ? RotationMode.LANDSCAPE : RotationMode.NORMAL)));
}
int edgeFlags = ev.getEdgeFlags();
ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
mRecentsViewDispatcher.dispatchEvent(ev);
ev.setEdgeFlags(edgeFlags);
mVelocityTracker.addMovement(ev);
if (ev.getActionMasked() == ACTION_POINTER_UP) {
@@ -301,13 +295,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private boolean isNavBarOnRight() {
return SysUINavigationMode.INSTANCE.get(getBaseContext()).getMode() != NO_BUTTON
&& mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90;
}
private boolean isNavBarOnLeft() {
return SysUINavigationMode.INSTANCE.get(getBaseContext()).getMode() != NO_BUTTON
&& mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270;
}
private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
@@ -345,18 +337,21 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
*/
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.
@@ -387,7 +382,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued;
mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher;
if (mSwipeSharedState.canGestureBeContinued) {
mInteractionHandler.cancel();
mInteractionHandler.cancelCurrentAnimation();
} else {
mInteractionHandler.reset();
}
@@ -410,19 +405,22 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
private float getDisplacement(MotionEvent ev) {
float eventX = ev.getX();
float eventY = ev.getY();
float displacement = eventY - mDownPos.y;
if (isNavBarOnRight()) {
displacement = eventX - mDownPos.x;
return ev.getX() - mDownPos.x;
} else if (isNavBarOnLeft()) {
displacement = mDownPos.x - eventX;
return mDownPos.x - ev.getX();
} else {
return ev.getY() - mDownPos.y;
}
return displacement;
}
@Override
public boolean useSharedSwipeState() {
return mInteractionHandler != null;
}
@Override
public boolean allowInterceptByParent() {
return !mPassedTouchSlop;
}
}
@@ -15,24 +15,22 @@
*/
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_UP;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.graphics.PointF;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import java.util.function.Predicate;
/**
* Input consumer for handling touch on the recents/Launcher activity.
@@ -40,24 +38,33 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
public class OverviewInputConsumer<T extends BaseDraggingActivity>
implements InputConsumer {
private final CachedEventDispatcher mCachedEventDispatcher = new CachedEventDispatcher();
private final T mActivity;
private final BaseDragLayer mTarget;
private final InputMonitorCompat mInputMonitor;
private final int[] mLocationOnScreen = new int[2];
private final PointF mDownPos = new PointF();
private final int mTouchSlopSquared;
private final boolean mProxyTouch;
private final Predicate<MotionEvent> mEventReceiver;
private final boolean mStartingInActivityBounds;
private boolean mTargetHandledTouch;
private boolean mTrackingStarted = false;
private boolean mInvalidated = false;
OverviewInputConsumer(T activity, boolean startingInActivityBounds) {
OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor,
boolean startingInActivityBounds) {
mActivity = activity;
mTarget = activity.getDragLayer();
int touchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
mTouchSlopSquared = touchSlop * touchSlop;
mInputMonitor = inputMonitor;
mStartingInActivityBounds = startingInActivityBounds;
mTarget = activity.getDragLayer();
if (startingInActivityBounds) {
mEventReceiver = mTarget::dispatchTouchEvent;
mProxyTouch = true;
} else {
// Only proxy touches to controllers if we are starting touch from nav bar.
mEventReceiver = mTarget::proxyTouchEvent;
mTarget.getLocationOnScreen(mLocationOnScreen);
mProxyTouch = mTarget.prepareProxyEventStarting();
}
}
@Override
@@ -65,47 +72,36 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
return TYPE_OVERVIEW;
}
@Override
public boolean allowInterceptByParent() {
return !mTargetHandledTouch;
}
@Override
public void onMotionEvent(MotionEvent ev) {
if (mInvalidated) {
if (!mProxyTouch) {
return;
}
mCachedEventDispatcher.dispatchEvent(ev);
int action = ev.getActionMasked();
if (action == ACTION_DOWN) {
if (mStartingInActivityBounds) {
startTouchTracking(ev, false /* updateLocationOffset */,
false /* closeActiveWindows */);
return;
}
mTrackingStarted = false;
mDownPos.set(ev.getX(), ev.getY());
} else if (!mTrackingStarted) {
switch (action) {
case ACTION_CANCEL:
case ACTION_UP:
startTouchTracking(ev, true /* updateLocationOffset */,
false /* closeActiveWindows */);
break;
case ACTION_MOVE: {
float x = ev.getX() - mDownPos.x;
float y = ev.getY() - mDownPos.y;
double hypotSquared = x * x + y * y;
if (hypotSquared >= mTouchSlopSquared) {
// Start tracking only when touch slop is crossed.
startTouchTracking(ev, true /* updateLocationOffset */,
true /* closeActiveWindows */);
}
}
}
int flags = ev.getEdgeFlags();
if (!mStartingInActivityBounds) {
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
}
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
boolean handled = mEventReceiver.test(ev);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
if (action == ACTION_UP || action == ACTION_CANCEL) {
mInvalidated = true;
// Set an empty consumer to that all the cached events are cleared
if (!mCachedEventDispatcher.hasConsumer()) {
mCachedEventDispatcher.setConsumer(motionEvent -> { });
if (!mTargetHandledTouch && handled) {
mTargetHandledTouch = true;
if (!mStartingInActivityBounds) {
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
}
if (mInputMonitor != null) {
mInputMonitor.pilferPointers();
}
}
}
@@ -117,42 +113,12 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
}
}
private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset,
boolean closeActiveWindows) {
if (updateLocationOffset) {
mTarget.getLocationOnScreen(mLocationOnScreen);
}
if (closeActiveWindows) {
OverviewCallbacks.get(mActivity).closeAllWindows();
ActivityManagerWrapper.getInstance()
.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
TOUCH_INTERACTION_LOG.addLog("startQuickstep");
}
mTrackingStarted = true;
mCachedEventDispatcher.setConsumer(this::sendEvent);
}
private void sendEvent(MotionEvent ev) {
if (mInvalidated) {
return;
}
int flags = ev.getEdgeFlags();
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
mInvalidated = !mTarget.dispatchTouchEvent(this, ev);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
}
public static InputConsumer newInstance(ActivityControlHelper activityHelper,
boolean startingInActivityBounds) {
@Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) {
BaseDraggingActivity activity = activityHelper.getCreatedActivity();
if (activity == null) {
return InputConsumer.NO_OP;
}
return new OverviewInputConsumer(activity, startingInActivityBounds);
return new OverviewInputConsumer(activity, inputMonitor, startingInActivityBounds);
}
}
@@ -51,9 +51,16 @@ public class SwipeSharedState implements SwipeAnimationListener {
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 @@ public class SwipeSharedState implements SwipeAnimationListener {
mRecentsAnimationListener.removeListener(this);
}
mRecentsAnimationListener = null;
mLastAnimationTarget = null;
clearAnimationTarget();
mLastAnimationCancelled = false;
mLastAnimationRunning = false;
}
@@ -19,13 +19,18 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
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 final class TaskViewUtils {
*/
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 @@ public final class TaskViewUtils {
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 @@ public final class TaskViewUtils {
@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 @@ public final class TaskViewUtils {
}
}
});
appAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
targetSet.release();
}
});
return appAnimator;
}
}
@@ -20,15 +20,17 @@ import static android.view.MotionEvent.ACTION_DOWN;
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;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.KeyguardManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -44,6 +46,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.view.Choreographer;
import android.view.Display;
@@ -53,6 +56,7 @@ import android.view.Surface;
import android.view.WindowManager;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.logging.EventLogArray;
@@ -67,9 +71,8 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
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;
@@ -78,7 +81,7 @@ import java.util.List;
/**
* Service connected by system-UI for handling touch interaction.
*/
@TargetApi(Build.VERSION_CODES.O)
@TargetApi(Build.VERSION_CODES.Q)
public class TouchInteractionService extends Service implements
NavigationModeChangeListener, DisplayListener {
@@ -229,6 +232,7 @@ public class TouchInteractionService extends Service implements
private Mode mMode = Mode.THREE_BUTTONS;
private int mDefaultDisplayId;
private final RectF mSwipeTouchRegion = new RectF();
private ComponentName mGestureBlockingActivity;
@Override
public void onCreate() {
@@ -250,6 +254,10 @@ public class TouchInteractionService extends Service implements
mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay()
.getDisplayId();
String blockingActivity = getString(R.string.gesture_blocking_activity);
mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null :
ComponentName.unflattenFromString(blockingActivity);
sConnected = true;
}
@@ -435,9 +443,9 @@ public class TouchInteractionService extends Service implements
}
private boolean isInValidSystemUiState() {
return (mSystemUiStateFlags & SYSUI_STATE_SCREEN_PINNING) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0;
return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
&& !ActivityManagerWrapper.getInstance().isLockToAppActive();
}
private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
@@ -456,25 +464,35 @@ public class TouchInteractionService extends Service implements
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, false);
base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
activityControl.isInLiveTileMode()) {
return OverviewInputConsumer.newInstance(activityControl, false);
base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false);
} else if (mGestureBlockingActivity != null && runningTaskInfo != null
&& mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) {
base = InputConsumer.NO_OP;
} 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,
@@ -484,8 +502,8 @@ public class TouchInteractionService extends Service implements
boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
mOverviewComponentObserver.getOverviewIntent(), activityControl,
shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
this::onConsumerInactive, mSwipeSharedState, mInputMonitorCompat);
shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive,
mSwipeSharedState, mInputMonitorCompat);
}
/**
@@ -77,6 +77,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -220,6 +221,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
protected Runnable mGestureEndCallback;
protected GestureEndTarget mGestureEndTarget;
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
private boolean mIsShelfPeeking;
private DeviceProfile mDp;
private int mTransitionDragLength;
@@ -250,7 +253,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
private T mActivity;
private RecentsView mRecentsView;
private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
private AnimationFactory mAnimationFactory = (t) -> { };
private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
@@ -405,8 +407,11 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
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) -> {
@@ -529,8 +534,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
return TaskView.getCurveScaleForInterpolation(interpolation);
}
public Consumer<MotionEvent> getRecentsViewDispatcher() {
return mRecentsView != null ? mRecentsView::dispatchTouchEvent : null;
public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) {
return mRecentsView != null ? mRecentsView.getEventDispatcher(rotationMode) : null;
}
@UiThread
@@ -648,8 +653,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
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 +733,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
if (runningTaskTarget != null) {
mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget);
}
mClipAnimationHelper.prepareAnimation(false /* isOpening */);
mClipAnimationHelper.prepareAnimation(dp, false /* isOpening */);
initTransitionEndpoints(dp);
mRecentsAnimationWrapper.setController(targetSet);
@@ -768,6 +772,17 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
}
/**
* 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.
@@ -788,12 +803,12 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT;
}
mDownPos = downPos;
handleNormalGestureEnd(endVelocity, isFling, velocity);
handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
@UiThread
private InputConsumer createNewInputProxyHandler() {
mCurrentShift.finishAnimation();
endRunningWindowAnim();
if (mLauncherTransitionController != null) {
mLauncherTransitionController.getAnimationPlayer().end();
}
@@ -802,11 +817,18 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
}
return OverviewInputConsumer.newInstance(mActivityControlHelper, true);
return OverviewInputConsumer.newInstance(mActivityControlHelper, null, true);
}
private void endRunningWindowAnim() {
if (mRunningWindowAnim != null) {
mRunningWindowAnim.end();
}
}
@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 +846,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
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) {
@@ -883,12 +907,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
}
if (endTarget.isLauncher) {
mRecentsAnimationWrapper.enableInputProxy();
}
if (endTarget == HOME) {
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
mLiveTileOverlay.startIconAnimation();
mRecentsAnimationWrapper.enableInputProxy();
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
@@ -907,7 +934,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
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;
}
@@ -969,6 +996,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
});
windowAnim.start(velocityPxPerMs);
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
mLauncherTransitionController = null;
} else {
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
@@ -987,6 +1015,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
});
windowAnim.start();
mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
}
// Always play the entire launcher animation when going home, since it is separate from
// the animation that has been controlled thus far.
@@ -1047,8 +1076,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
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 +1090,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
@Override
public void onAnimationStart(Animator animation) {
homeAnim.dispatchOnStart();
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
if (mActivity != null) {
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
}
}
@Override
@@ -1097,7 +1127,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
false /* animate */, true /* freezeTaskList */);
});
}
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true);
doLogGesture(NEW_TASK);
reset();
}
@@ -1106,7 +1136,11 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
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()) {
@@ -1115,7 +1149,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
}
private void invalidateHandler() {
mCurrentShift.finishAnimation();
endRunningWindowAnim();
if (mGestureEndCallback != null) {
mGestureEndCallback.run();
@@ -1263,4 +1297,16 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
return app.isNotInRecents
|| app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
}
private interface RunningWindowAnim {
void end();
static RunningWindowAnim wrap(Animator animator) {
return animator::end;
}
static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
return rectFSpringAnim::end;
}
}
}
@@ -17,18 +17,13 @@ package com.android.quickstep.fallback;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewDebug;
import android.view.WindowInsets;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -39,9 +34,6 @@ public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
private static final int MIN_SIZE = 10;
private final RecentsActivity mActivity;
@ViewDebug.ExportedProperty(category = "launcher")
private final RectF mTouchExcludeRegion = new RectF();
private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
public RecentsRootView(Context context, AttributeSet attrs) {
@@ -100,26 +92,7 @@ public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
if (Utilities.ATLEAST_Q) {
Insets gestureInsets = insets.getMandatorySystemGestureInsets();
mTouchExcludeRegion.set(gestureInsets.left, gestureInsets.top,
gestureInsets.right, gestureInsets.bottom);
}
updateTouchExcludeRegion(insets);
return super.dispatchApplyWindowInsets(insets);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
float x = ev.getX();
float y = ev.getY();
if (y < mTouchExcludeRegion.top
|| x < mTouchExcludeRegion.left
|| x > (getWidth() - mTouchExcludeRegion.right)
|| y > (getHeight() - mTouchExcludeRegion.bottom)) {
return false;
}
}
return super.dispatchTouchEvent(ev);
}
}
@@ -31,11 +31,12 @@ import android.graphics.RectF;
import android.os.Build;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
@@ -50,8 +51,6 @@ import com.android.systemui.shared.system.WindowManagerWrapper;
import java.util.function.BiFunction;
import androidx.annotation.Nullable;
/**
* Utility class to handle window clip animation
*/
@@ -81,6 +80,7 @@ public class ClipAnimationHelper {
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 +89,8 @@ public class ClipAnimationHelper {
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 +105,7 @@ public class ClipAnimationHelper {
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
mTaskCornerRadius = TaskCornerRadius.get(context);
mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
}
private void updateSourceStack(RemoteAnimationTargetCompat target) {
@@ -144,8 +147,9 @@ public class ClipAnimationHelper {
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 +181,9 @@ public class ClipAnimationHelper {
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 +194,9 @@ public class ClipAnimationHelper {
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;
}
@@ -318,12 +326,14 @@ public class ClipAnimationHelper {
float scale = mTargetRect.width() / mSourceRect.width();
float insetProgress = (1 - progress);
float windowCornerRadius = mUseRoundedCornersOnWindows
? mWindowCornerRadius : 0;
ttv.drawOnCanvas(canvas,
-mSourceWindowClipInsets.left * insetProgress,
-mSourceWindowClipInsets.top * insetProgress,
ttv.getMeasuredWidth() + mSourceWindowClipInsets.right * insetProgress,
ttv.getMeasuredHeight() + mSourceWindowClipInsets.bottom * insetProgress,
Utilities.mapRange(progress, mWindowCornerRadius * scale, ttv.getCornerRadius()));
Utilities.mapRange(progress, windowCornerRadius * scale, ttv.getCornerRadius()));
}
public RectF getTargetRect() {
@@ -23,6 +23,9 @@ import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatProperty;
import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.FlingSpringAnim;
@@ -30,9 +33,6 @@ import com.android.launcher3.anim.FlingSpringAnim;
import java.util.ArrayList;
import java.util.List;
import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
import androidx.dynamicanimation.animation.FloatPropertyCompat;
/**
* Applies spring forces to animate from a starting rect to a target rect,
* while providing update callbacks to the caller.
@@ -98,6 +98,10 @@ public class RectFSpringAnim {
private float mCurrentCenterX;
private float mCurrentCenterY;
private float mCurrentScaleProgress;
private FlingSpringAnim mRectXAnim;
private FlingSpringAnim mRectYAnim;
private ValueAnimator mRectScaleAnim;
private boolean mAnimsStarted;
private boolean mRectXAnimEnded;
private boolean mRectYAnimEnded;
private boolean mRectScaleAnimEnded;
@@ -127,15 +131,15 @@ public class RectFSpringAnim {
mRectYAnimEnded = true;
maybeOnEnd();
});
FlingSpringAnim rectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, mCurrentCenterX,
mRectXAnim = new FlingSpringAnim(this, RECT_CENTER_X, mCurrentCenterX,
mTargetRect.centerX(), velocityPxPerMs.x * 1000, onXEndListener);
FlingSpringAnim rectYAnim = new FlingSpringAnim(this, RECT_CENTER_Y, mCurrentCenterY,
mRectYAnim = new FlingSpringAnim(this, RECT_CENTER_Y, mCurrentCenterY,
mTargetRect.centerY(), velocityPxPerMs.y * 1000, onYEndListener);
ValueAnimator rectScaleAnim = ObjectAnimator.ofPropertyValuesHolder(this,
mRectScaleAnim = ObjectAnimator.ofPropertyValuesHolder(this,
PropertyValuesHolder.ofFloat(RECT_SCALE_PROGRESS, 1))
.setDuration(RECT_SCALE_DURATION);
rectScaleAnim.addListener(new AnimationSuccessListener() {
mRectScaleAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mRectScaleAnimEnded = true;
@@ -143,14 +147,23 @@ public class RectFSpringAnim {
}
});
rectXAnim.start();
rectYAnim.start();
rectScaleAnim.start();
mRectXAnim.start();
mRectYAnim.start();
mRectScaleAnim.start();
mAnimsStarted = true;
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
animatorListener.onAnimationStart(null);
}
}
public void end() {
if (mAnimsStarted) {
mRectXAnim.end();
mRectYAnim.end();
mRectScaleAnim.end();
}
}
private void onUpdate() {
if (!mOnUpdateListeners.isEmpty()) {
float currentWidth = Utilities.mapRange(mCurrentScaleProgress, mStartRect.width(),
@@ -166,7 +179,8 @@ public class RectFSpringAnim {
}
private void maybeOnEnd() {
if (mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded) {
if (mAnimsStarted && mRectXAnimEnded && mRectYAnimEnded && mRectScaleAnimEnded) {
mAnimsStarted = false;
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
animatorListener.onAnimationEnd(null);
}
@@ -95,9 +95,4 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
void onRecentsAnimationCanceled();
}
public interface SwipeAnimationFinishListener {
void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet);
}
}
@@ -25,6 +25,7 @@ import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
import android.view.View;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.Utilities;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
@@ -52,7 +53,7 @@ public class TaskViewDrawable extends Drawable {
private final RecentsView mParent;
private final View mIconView;
private final int[] mIconPos;
private final float[] mIconPos;
private final TaskView mTaskView;
private final TaskThumbnailView mThumbnailView;
@@ -68,13 +69,15 @@ public class TaskViewDrawable extends Drawable {
mParent = parent;
mTaskView = tv;
mIconView = tv.getIconView();
mIconPos = new int[2];
mIconPos = new float[2];
mIconScale = mIconView.getScaleX();
Utilities.getDescendantCoordRelativeToAncestor(mIconView, parent, mIconPos, true);
mThumbnailView = tv.getThumbnail();
mClipAnimationHelper = new ClipAnimationHelper(parent.getContext());
mClipAnimationHelper.fromTaskThumbnailView(mThumbnailView, parent);
mClipAnimationHelper.prepareAnimation(
BaseActivity.fromContext(tv.getContext()).getDeviceProfile(), true /* isOpening */);
}
public void setProgress(float progress) {
@@ -45,6 +45,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -70,9 +71,6 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ListView;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
@@ -85,6 +83,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -111,6 +110,9 @@ import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
import java.util.function.Consumer;
import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.SpringForce;
/**
* A list of recent tasks.
*/
@@ -1639,4 +1641,26 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
public ClearAllButton getClearAllButton() {
return mClearAllButton;
}
public Consumer<MotionEvent> getEventDispatcher(RotationMode rotationMode) {
if (rotationMode.isTransposed) {
Matrix transform = new Matrix();
transform.setRotate(-rotationMode.surfaceRotation);
if (getWidth() > 0 && getHeight() > 0) {
float scale = ((float) getWidth()) / getHeight();
transform.postScale(scale, 1 / scale);
}
Matrix inverse = new Matrix();
transform.invert(inverse);
return e -> {
e.transform(transform);
super.onTouchEvent(e);
e.transform(inverse);
};
} else {
return super::onTouchEvent;
}
}
}
@@ -51,7 +51,6 @@ import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.QuickStepContract;
/**
* A task in the Recents view.
@@ -60,6 +59,7 @@ public class TaskThumbnailView extends View {
private final static ColorMatrix COLOR_MATRIX = new ColorMatrix();
private final static ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix();
private final static Rect EMPTY_RECT = new Rect();
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailView>("dimAlpha") {
@@ -79,16 +79,17 @@ public class TaskThumbnailView extends View {
private final BaseActivity mActivity;
private final TaskOverlay mOverlay;
private final boolean mIsDarkTextTheme;
private final Paint mPaint = new Paint();
private final Paint mBackgroundPaint = new Paint();
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint mClearPaint = new Paint();
private final Paint mDimmingPaintAfterClearing = new Paint();
private final float mWindowCornerRadius;
private final Matrix mMatrix = new Matrix();
private float mClipBottom = -1;
private Rect mScaledInsets = new Rect();
private Rect mCurrentDrawnInsets = new Rect();
private float mCurrentDrawnCornerRadius;
private boolean mIsRotated;
private Task mTask;
@@ -117,7 +118,7 @@ public class TaskThumbnailView extends View {
mDimmingPaintAfterClearing.setColor(Color.BLACK);
mActivity = BaseActivity.fromContext(context);
mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
setCurrentDrawnInsetsAndRadius(EMPTY_RECT, mCornerRadius);
}
public void bind(Task task) {
@@ -200,25 +201,24 @@ public class TaskThumbnailView extends View {
@Override
protected void onDraw(Canvas canvas) {
TaskView taskView = (TaskView) getParent();
float fullscreenProgress = taskView.getFullscreenProgress();
if (mIsRotated) {
// Don't show insets in the wrong orientation.
fullscreenProgress = 0;
}
if (fullscreenProgress > 0) {
// Draw the insets if we're being drawn fullscreen (we do this for quick switch).
float cornerRadius = Utilities.mapRange(fullscreenProgress, mCornerRadius,
mWindowCornerRadius);
drawOnCanvas(canvas,
-mScaledInsets.left * fullscreenProgress,
-mScaledInsets.top * fullscreenProgress,
getMeasuredWidth() + mScaledInsets.right * fullscreenProgress,
getMeasuredHeight() + mScaledInsets.bottom * fullscreenProgress,
cornerRadius / taskView.getRecentsView().getScaleX());
} else {
drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
}
// Draw the insets if we're being drawn fullscreen (we do this for quick switch).
drawOnCanvas(canvas,
-mCurrentDrawnInsets.left,
-mCurrentDrawnInsets.top,
getMeasuredWidth() + mCurrentDrawnInsets.right,
getMeasuredHeight() + mCurrentDrawnInsets.bottom,
mCurrentDrawnCornerRadius);
}
public Rect getInsetsToDrawInFullscreen(boolean isMultiWindowMode) {
// Don't show insets in the wrong orientation or in multi window mode.
return mIsRotated || isMultiWindowMode ? EMPTY_RECT : mScaledInsets;
}
public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) {
mCurrentDrawnInsets.set(insets);
mCurrentDrawnCornerRadius = radius;
invalidate();
}
public float getCornerRadius() {
@@ -17,7 +17,7 @@
package com.android.quickstep.views;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.BaseActivity.fromContext;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -31,6 +31,7 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
@@ -46,6 +47,7 @@ import android.widget.Toast;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
@@ -53,7 +55,6 @@ import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
@@ -61,12 +62,15 @@ import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.views.RecentsView.PageCallbacks;
import com.android.quickstep.views.RecentsView.ScrollState;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
@@ -95,6 +99,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
public static final Property<TaskView, Float> ZOOM_SCALE =
new FloatProperty<TaskView>("zoomScale") {
@Override
@@ -149,6 +156,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
};
private final TaskOutlineProvider mOutlineProvider;
private Task mTask;
private TaskThumbnailView mSnapshotView;
private TaskMenuView mMenuView;
@@ -157,6 +166,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private float mCurveScale;
private float mZoomScale;
private float mFullscreenProgress;
private final Rect mCurrentDrawnInsets = new Rect();
private final float mCornerRadius;
private final float mWindowCornerRadius;
private final BaseDraggingActivity mActivity;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
@@ -178,6 +191,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mActivity = BaseDraggingActivity.fromContext(context);
setOnClickListener((view) -> {
if (getTask() == null) {
return;
@@ -192,13 +206,16 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
launchTask(true /* animate */);
}
fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
fromContext(context).getStatsLogManager().logTaskLaunch(getRecentsView(),
mActivity.getStatsLogManager().logTaskLaunch(getRecentsView(),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
});
setOutlineProvider(new TaskOutlineProvider(context, getResources()));
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
mOutlineProvider = new TaskOutlineProvider(getResources(), mCornerRadius);
setOutlineProvider(mOutlineProvider);
}
@Override
@@ -291,8 +308,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
if (mTask != null) {
final ActivityOptions opts;
if (animate) {
opts = ((BaseDraggingActivity) fromContext(getContext()))
.getActivityLaunchOptions(this);
opts = mActivity.getActivityLaunchOptions(this);
if (freezeTaskList) {
ActivityOptionsCompat.setFreezeRecentTasksList(opts);
}
@@ -480,6 +496,10 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
super.onLayout(changed, left, top, right, bottom);
setPivotX((right - left) * 0.5f);
setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
if (Utilities.ATLEAST_Q) {
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
}
}
public static float getCurveScaleForInterpolation(float linearInterpolation) {
@@ -491,7 +511,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
}
private void setCurveScale(float curveScale) {
public void setCurveScale(float curveScale) {
mCurveScale = curveScale;
onScaleChanged();
}
@@ -520,17 +540,26 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private static final class TaskOutlineProvider extends ViewOutlineProvider {
private final int mMarginTop;
private final float mRadius;
private final Rect mInsets = new Rect();
private float mRadius;
TaskOutlineProvider(Context context, Resources res) {
TaskOutlineProvider(Resources res, float radius) {
mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
mRadius = Themes.getDialogCornerRadius(context);
mRadius = radius;
}
public void setCurrentDrawnInsetsAndRadius(Rect insets, float radius) {
mInsets.set(insets);
mRadius = radius;
}
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, mMarginTop, view.getWidth(),
view.getHeight(), mRadius);
outline.setRoundRect(-mInsets.left,
mMarginTop - mInsets.top,
view.getWidth() + mInsets.right,
view.getHeight() + mInsets.bottom,
mRadius);
}
}
@@ -543,13 +572,12 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
getContext().getText(R.string.accessibility_close_task)));
final Context context = getContext();
final BaseDraggingActivity activity = fromContext(context);
final List<TaskSystemShortcut> shortcuts =
mSnapshotView.getTaskOverlay().getEnabledShortcuts(this);
final int count = shortcuts.size();
for (int i = 0; i < count; ++i) {
final TaskSystemShortcut menuOption = shortcuts.get(i);
OnClickListener onClickListener = menuOption.getOnClickListener(activity, this);
OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
if (onClickListener != null) {
info.addAction(menuOption.createAccessibilityAction(context));
}
@@ -589,8 +617,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
for (int i = 0; i < count; ++i) {
final TaskSystemShortcut menuOption = shortcuts.get(i);
if (menuOption.hasHandlerForAction(action)) {
OnClickListener onClickListener = menuOption.getOnClickListener(
fromContext(getContext()), this);
OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, this);
if (onClickListener != null) {
onClickListener.onClick(this);
}
@@ -628,11 +655,21 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
setClipChildren(!isFullscreen);
setClipToPadding(!isFullscreen);
getThumbnail().invalidate();
}
public float getFullscreenProgress() {
return mFullscreenProgress;
TaskThumbnailView thumbnail = getThumbnail();
boolean isMultiWindowMode = mActivity.getDeviceProfile().isMultiWindowMode;
Rect insets = thumbnail.getInsetsToDrawInFullscreen(isMultiWindowMode);
mCurrentDrawnInsets.set((int) (insets.left * mFullscreenProgress),
(int) (insets.top * mFullscreenProgress),
(int) (insets.right * mFullscreenProgress),
(int) (insets.bottom * mFullscreenProgress));
float fullscreenCornerRadius = isMultiWindowMode ? 0 : mWindowCornerRadius;
float cornerRadius = Utilities.mapRange(mFullscreenProgress, mCornerRadius,
fullscreenCornerRadius) / getRecentsView().getScaleX();
thumbnail.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius);
mOutlineProvider.setCurrentDrawnInsetsAndRadius(mCurrentDrawnInsets, cornerRadius);
invalidateOutline();
}
public boolean isRunningTask() {
+3
View File
@@ -18,6 +18,9 @@
<string name="overview_callbacks_class" translatable="false"></string>
<!-- Activity which blocks home gesture -->
<string name="gesture_blocking_activity" translatable="false"></string>
<string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
<string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
@@ -408,7 +408,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
*/
private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
Rect windowTargetBounds, boolean toggleVisibility) {
Rect bounds = new Rect();
RectF bounds = new RectF();
mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
bounds, true /* isOpening */, mFloatingView);
Rect crop = new Rect();
@@ -416,17 +416,15 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
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 / bounds.width();
float maxScaleY = smallestSize / bounds.height();
float scale = Math.max(maxScaleX, maxScaleY);
float startScale = 1f;
if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
@@ -470,6 +468,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
if (v instanceof BubbleTextView) {
((BubbleTextView) v).setStayPressed(false);
}
openingTargets.release();
}
});
@@ -111,14 +111,6 @@ public class TestInformationProvider extends ContentProvider {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
break;
}
case TestProtocol.REQUEST_ENABLE_DRAG_LOGGING:
TestProtocol.sDebugTracing = true;
break;
case TestProtocol.REQUEST_DISABLE_DRAG_LOGGING:
TestProtocol.sDebugTracing = false;
break;
}
return response;
}
@@ -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 @@ public class RemoteAnimationTargetSet {
}
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);
}
}
}
@@ -17,6 +17,7 @@
package com.android.quickstep;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
@@ -25,7 +26,9 @@ import android.content.ComponentName;
import android.content.pm.LauncherActivityInfo;
import android.os.Process;
import android.view.View;
import android.widget.ProgressBar;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
@@ -37,20 +40,16 @@ import com.android.launcher3.model.AppLaunchTracker;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class AppPredictionsUITests extends AbstractQuickStepTest {
private static final int DEFAULT_APP_LAUNCH_TIMES = 3;
private static final String TAG = "AppPredictionsUITests";
public class AppPredictionsUITests extends AbstractQuickStepTest {
private LauncherActivityInfo mSampleApp1;
private LauncherActivityInfo mSampleApp2;
@@ -86,24 +85,20 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
* Test that prediction UI is updated as soon as we get predictions from the system
*/
@Test
@Ignore // b/131772711: this really fails (when being run as a part of the whole test suite)!
public void testPredictionExistsInAllApps() {
mActivityMonitor.startLauncher();
mLauncher.pressHome().switchToAllApps();
// There has not been any update, verify that progress bar is showing
waitForLauncherCondition("Prediction is not in loading state", launcher -> {
ProgressBar p = findLoadingBar(launcher);
return p != null && p.isShown();
});
// Dispatch an update
sendPredictionUpdate(mSampleApp1, mSampleApp2);
// The first update should apply immediately.
waitForLauncherCondition("Predictions were not updated in loading state",
launcher -> getPredictedApp(launcher).size() == 2);
}
/**
* Test tat prediction update is deferred if it is already visible
* Test that prediction update is deferred if it is already visible
*/
@Test
public void testPredictionsDeferredUntilHome() {
@@ -122,6 +117,20 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
assertEquals(3, getFromLauncher(this::getPredictedApp).size());
}
@Test
@Ignore // b/131772711 - this was failing in the lab
public void testPredictionsDisabled() {
mActivityMonitor.startLauncher();
sendPredictionUpdate();
mLauncher.pressHome().switchToAllApps();
waitForLauncherCondition("Predictions were not updated in loading state",
launcher -> launcher.getAppsView().getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class).getVisibility() == View.GONE);
assertFalse(PredictionUiStateManager.INSTANCE.get(mTargetContext)
.getCurrentState().isEnabled);
}
public ArrayList<BubbleTextView> getPredictedApp(Launcher launcher) {
PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class);
@@ -136,20 +145,6 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
return predictedAppViews;
}
private ProgressBar findLoadingBar(Launcher launcher) {
PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
.findFixedRowByType(PredictionRowView.class);
for (int i = 0; i < container.getChildCount(); i++) {
View view = container.getChildAt(i);
if (view instanceof ProgressBar) {
return (ProgressBar) view;
}
}
return null;
}
private void sendPredictionUpdate(LauncherActivityInfo... activities) {
getOnUiThread(() -> {
List<AppTarget> targets = new ArrayList<>(activities.length);
@@ -44,6 +44,7 @@ import com.android.launcher3.tapl.LauncherInstrumentation;
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 @@ public class FallbackRecentsTest {
@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 @@ public class FallbackRecentsTest {
@NavigationModeSwitch(mode = THREE_BUTTON)
@Test
@Ignore // b/131630813
public void goToOverviewFromApp() {
startAppFast("com.android.settings");
@@ -71,7 +71,8 @@ public class NavigationModeSwitchRule implements TestRule {
@Override
public Statement apply(Statement base, Description description) {
if (TestHelpers.isInLauncherProcess() &&
// b/130558787; b/131419978
if (false && TestHelpers.isInLauncherProcess() &&
description.getAnnotation(NavigationModeSwitch.class) != null) {
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
@@ -64,7 +64,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
private void startTestApps() throws Exception {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
startTestActivity(2);
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
@@ -130,8 +130,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
assertNotNull("overview.getCurrentTask() returned null (1)", task);
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Contacts app didn't open from Overview", mDevice.wait(Until.hasObject(
By.pkg(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS)).depth(0)),
assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
By.pkg(getAppPackageName()).text("TestActivity2")),
LONG_WAIT_TIME_MS));
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
@@ -36,6 +36,7 @@ import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.Themes;
import androidx.annotation.Nullable;
@@ -69,7 +70,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
// Update theme
WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
wallpaperColorInfo.addOnChangeListener(this);
int themeRes = getThemeRes(wallpaperColorInfo);
int themeRes = Themes.getActivityThemeRes(this);
if (themeRes != mThemeRes) {
mThemeRes = themeRes;
setTheme(themeRes);
@@ -88,31 +89,11 @@ public abstract class BaseDraggingActivity extends BaseActivity
}
private void updateTheme() {
WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
if (mThemeRes != getThemeRes(wallpaperColorInfo)) {
if (mThemeRes != Themes.getActivityThemeRes(this)) {
recreate();
}
}
protected int getThemeRes(WallpaperColorInfo wallpaperColorInfo) {
boolean darkTheme;
if (Utilities.ATLEAST_Q) {
Configuration configuration = getResources().getConfiguration();
int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
darkTheme = nightMode == Configuration.UI_MODE_NIGHT_YES;
} else {
darkTheme = wallpaperColorInfo.isDark();
}
if (darkTheme) {
return wallpaperColorInfo.supportsDarkText() ?
R.style.AppTheme_Dark_DarkText : R.style.AppTheme_Dark;
} else {
return wallpaperColorInfo.supportsDarkText() ?
R.style.AppTheme_DarkText : R.style.AppTheme;
}
}
@Override
public void onActionModeStarted(ActionMode mode) {
super.onActionModeStarted(mode);
@@ -123,12 +123,12 @@ public abstract class BaseRecyclerView extends RecyclerView {
* @param ev MotionEvent in {@param eventSource}
*/
public boolean shouldContainerScroll(MotionEvent ev, View eventSource) {
int[] point = new int[2];
point[0] = (int) ev.getX();
point[1] = (int) ev.getY();
float[] point = new float[2];
point[0] = ev.getX();
point[1] = ev.getY();
Utilities.mapCoordInSelfToDescendant(mScrollbar, eventSource, point);
// IF the MotionEvent is inside the thumb, container should not be pulled down.
if (mScrollbar.shouldBlockIntercept(point[0], point[1])) {
if (mScrollbar.shouldBlockIntercept((int) point[0], (int) point[1])) {
return false;
}
@@ -142,7 +142,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mActivity = ActivityContext.lookupContext(context);
DeviceProfile grid = mActivity.getDeviceProfile();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
@@ -150,18 +149,24 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
int defaultIconSize = grid.iconSizePx;
final int defaultIconSize;
if (display == DISPLAY_WORKSPACE) {
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
} else if (display == DISPLAY_ALL_APPS) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
} else if (display == DISPLAY_FOLDER) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
} else {
defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
}
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
+49 -7
View File
@@ -48,6 +48,9 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.IntDef;
import androidx.core.view.ViewCompat;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -57,12 +60,14 @@ import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.lang.annotation.Retention;
@@ -73,10 +78,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Stack;
import androidx.annotation.IntDef;
import androidx.core.view.ViewCompat;
public class CellLayout extends ViewGroup {
public class CellLayout extends ViewGroup implements Transposable {
public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
@@ -182,6 +184,7 @@ public class CellLayout extends ViewGroup {
// Related to accessible drag and drop
private DragAndDropAccessibilityDelegate mTouchHelper;
private boolean mUseTouchHelper = false;
private RotationMode mRotationMode = RotationMode.NORMAL;
public CellLayout(Context context) {
this(context, null);
@@ -203,7 +206,7 @@ public class CellLayout extends ViewGroup {
setClipToPadding(false);
mActivity = ActivityContext.lookupContext(context);
DeviceProfile grid = mActivity.getDeviceProfile();
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
@@ -317,6 +320,24 @@ public class CellLayout extends ViewGroup {
}
}
public void setRotationMode(RotationMode mode) {
if (mRotationMode != mode) {
mRotationMode = mode;
requestLayout();
}
}
@Override
public RotationMode getRotationMode() {
return mRotationMode;
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
mRotationMode.mapRect(left, top, right, bottom, mTempRect);
super.setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
}
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// Always attempt to dispatch hover events to accessibility first.
@@ -339,6 +360,10 @@ public class CellLayout extends ViewGroup {
mShortcutsAndWidgets.setLayerType(hasLayer ? LAYER_TYPE_HARDWARE : LAYER_TYPE_NONE, sPaint);
}
public boolean isHardwareLayerEnabled() {
return mShortcutsAndWidgets.getLayerType() == LAYER_TYPE_HARDWARE;
}
public void setCellDimensions(int width, int height) {
mFixedCellWidth = mCellWidth = width;
mFixedCellHeight = mCellHeight = height;
@@ -745,6 +770,14 @@ public class CellLayout extends ViewGroup {
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
mShortcutsAndWidgets.setRotation(mRotationMode.surfaceRotation);
if (mRotationMode.isTransposed) {
int tmp = childWidthSize;
childWidthSize = childHeightSize;
childHeightSize = tmp;
}
if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
@@ -787,7 +820,6 @@ public class CellLayout extends ViewGroup {
int top = getPaddingTop();
int bottom = b - t - getPaddingBottom();
mShortcutsAndWidgets.layout(left, top, right, bottom);
// Expand the background drawing bounds by the padding baked into the background drawable
mBackground.getPadding(mTempRect);
mBackground.setBounds(
@@ -795,6 +827,16 @@ public class CellLayout extends ViewGroup {
top - mTempRect.top - getPaddingTop(),
right + mTempRect.right + getPaddingRight(),
bottom + mTempRect.bottom + getPaddingBottom());
if (mRotationMode.isTransposed) {
int halfW = mShortcutsAndWidgets.getMeasuredWidth() / 2;
int halfH = mShortcutsAndWidgets.getMeasuredHeight() / 2;
int cX = (left + right) / 2;
int cY = (top + bottom) / 2;
mShortcutsAndWidgets.layout(cX - halfW, cY - halfH, cX + halfW, cY + halfH);
} else {
mShortcutsAndWidgets.layout(left, top, right, bottom);
}
}
/**
@@ -929,7 +971,7 @@ public class CellLayout extends ViewGroup {
if (resize) {
cellToRect(cellX, cellY, spanX, spanY, r);
if (v instanceof LauncherAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
} else {
@@ -33,20 +33,12 @@ public class CheckLongPressHelper {
class CheckForLongPress implements Runnable {
public void run() {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"CheckForLongPress1");
}
if ((mView.getParent() != null) && mView.hasWindowFocus()
&& !mHasPerformedLongPress) {
boolean handled;
if (mListener != null) {
handled = mListener.onLongClick(mView);
} else {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"CheckForLongPress2");
}
handled = mView.performLongClick();
}
if (handled) {
@@ -81,21 +73,11 @@ public class CheckLongPressHelper {
}
mView.postDelayed(mPendingCheckForLongPress,
(long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"postCheckForLongPress: " + ViewConfiguration.getLongPressTimeout() + " "
+ mLongPressTimeoutFactor);
}
}
public void cancelLongPress() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress != null) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"cancelLongPress @ " + android.util.Log.getStackTraceString(
new Throwable()));
}
mView.removeCallbacks(mPendingCheckForLongPress);
mPendingCheckForLongPress = null;
}
+4 -39
View File
@@ -419,6 +419,10 @@ public class DeviceProfile {
updateWorkspacePadding();
}
/**
* The current device insets. This is generally same as the insets being dispatched to
* {@link Insettable} elements, but can differ if the element is using a different profile.
*/
public Rect getInsets() {
return mInsets;
}
@@ -582,45 +586,6 @@ public class DeviceProfile {
}
}
/**
* Gets an item's location on the home screen. This is useful if the home screen
* is animating, otherwise use {@link View#getLocationOnScreen(int[])}.
* @param pageDiff The page difference relative to the current page.
*/
public void getItemLocation(int cellX, int cellY, int spanX, int spanY, int container,
int pageDiff, Rect outBounds) {
outBounds.setEmpty();
if (container == CONTAINER_HOTSEAT) {
final int actualHotseatCellHeight;
if (isVerticalBarLayout()) {
actualHotseatCellHeight = availableHeightPx / inv.numRows;
if (mIsSeascape) {
outBounds.left = mHotseatPadding.left;
} else {
outBounds.left = availableWidthPx - hotseatBarSizePx + mHotseatPadding.left;
}
outBounds.right = outBounds.left + iconSizePx;
outBounds.top = mHotseatPadding.top
+ actualHotseatCellHeight * (inv.numRows - cellX - 1);
outBounds.bottom = outBounds.top + actualHotseatCellHeight;
} else {
actualHotseatCellHeight = hotseatBarSizePx - hotseatBarBottomPaddingPx
- hotseatBarTopPaddingPx;
outBounds.left = mInsets.left + workspacePadding.left + cellLayoutPaddingLeftRightPx
+ (cellX * getCellSize().x);
outBounds.right = outBounds.left + getCellSize().x;
outBounds.top = mInsets.top + availableHeightPx - hotseatBarSizePx;
outBounds.bottom = outBounds.top + actualHotseatCellHeight;
}
} else {
outBounds.left = mInsets.left + workspacePadding.left + cellLayoutPaddingLeftRightPx
+ (cellX * getCellSize().x) + (pageDiff * availableWidthPx);
outBounds.right = outBounds.left + (getCellSize().x * spanX);
outBounds.top = mInsets.top + workspacePadding.top + (cellY * getCellSize().y);
outBounds.bottom = outBounds.top + (getCellSize().y * spanY);
}
}
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
+1 -1
View File
@@ -204,7 +204,7 @@ public class DropTargetBar extends FrameLayout
return visibleCount;
}
private void animateToVisibility(boolean isVisible) {
public void animateToVisibility(boolean isVisible) {
if (mVisible != isVisible) {
mVisible = isVisible;
+10 -2
View File
@@ -26,11 +26,13 @@ import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.Transposable;
public class Hotseat extends CellLayout implements LogContainerProvider, Insettable {
public class Hotseat extends CellLayout implements LogContainerProvider, Insettable, Transposable {
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mHasVerticalHotseat;
@@ -77,7 +79,8 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta
@Override
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = mActivity.getDeviceProfile();
DeviceProfile grid = mActivity.getWallpaperDeviceProfile();
insets = grid.getInsets();
if (grid.isVerticalBarLayout()) {
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
@@ -105,4 +108,9 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta
// Don't let if follow through to workspace
return true;
}
@Override
public RotationMode getRotationMode() {
return Launcher.getLauncher(getContext()).getRotationMode();
}
}
+52 -3
View File
@@ -53,6 +53,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -71,6 +72,7 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
@@ -93,6 +95,7 @@ import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -272,6 +275,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private float mCurrentAssistantVisibility = 0f;
private DeviceProfile mStableDeviceProfile;
private RotationMode mRotationMode = RotationMode.NORMAL;
@Override
protected void onCreate(Bundle savedInstanceState) {
RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
@@ -390,6 +396,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
}
});
if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
getWindow().setAttributes(lp);
}
}
@Override
@@ -418,6 +430,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
@Override
protected void reapplyUi() {
if (FeatureFlags.FAKE_LANDSCAPE_UI.get()) {
mRotationMode = mStableDeviceProfile == null ? RotationMode.NORMAL :
(mDeviceProfile.isSeascape() ? RotationMode.SEASCAPE : RotationMode.LANDSCAPE);
}
getRootView().dispatchInsets();
getStateManager().reapplyState(true /* cancelCurrentAnimation */);
}
@@ -469,8 +485,41 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
if (FeatureFlags.FAKE_LANDSCAPE_UI.get() && mDeviceProfile.isVerticalBarLayout()
&& !mDeviceProfile.isMultiWindowMode) {
mStableDeviceProfile = mDeviceProfile.inv.portraitProfile;
mRotationMode = mDeviceProfile.isSeascape()
? RotationMode.SEASCAPE : RotationMode.LANDSCAPE;
} else {
mStableDeviceProfile = null;
mRotationMode = RotationMode.NORMAL;
}
onDeviceProfileInitiated();
mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout(), true);
mModelWriter = mModel.getWriter(getWallpaperDeviceProfile().isVerticalBarLayout(), true);
}
public void updateInsets(Rect insets) {
mDeviceProfile.updateInsets(insets);
if (mStableDeviceProfile != null) {
mStableDeviceProfile.updateInsets(insets);
}
}
@Override
public RotationMode getRotationMode() {
return mRotationMode;
}
/**
* Device profile to be used by UI elements which are shown directly on top of the wallpaper
* and whose presentation is tied to the wallpaper (and physical device) and not the activity
* configuration.
*/
@Override
public DeviceProfile getWallpaperDeviceProfile() {
return mStableDeviceProfile == null ? mDeviceProfile : mStableDeviceProfile;
}
public RotationHelper getRotationHelper() {
@@ -879,7 +928,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
super.onPause();
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
mDropTargetBar.animateToVisibility(false);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onPause();
}
@@ -1812,7 +1861,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mAppWidgetHost.clearViews();
if (mHotseat != null) {
mHotseat.resetLayout(mDeviceProfile.isVerticalBarLayout());
mHotseat.resetLayout(getWallpaperDeviceProfile().isVerticalBarLayout());
}
TraceHelper.endSection("startBinding");
}
@@ -213,7 +213,6 @@ public class LauncherModel extends BroadcastReceiver
synchronized (mLock) {
Preconditions.assertUIThread();
mCallbacks = new WeakReference<>(callbacks);
android.util.Log.d("b/131170582", "mCallbacks = " + mCallbacks);
}
}
@@ -331,7 +330,6 @@ public class LauncherModel extends BroadcastReceiver
// Stop any existing loaders first, so they don't set mModelLoaded to true later
stopLoader();
mModelLoaded = false;
android.util.Log.d("b/131170582", "1 mModelLoaded = " + mModelLoaded);
}
// Start the loader if launcher is already running, otherwise the loader will run,
@@ -392,7 +390,6 @@ public class LauncherModel extends BroadcastReceiver
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
mLoaderTask = null;
android.util.Log.d("b/131170582", "1 mLoaderTask = " + mLoaderTask);
if (oldTask != null) {
oldTask.stopLocked();
}
@@ -403,7 +400,6 @@ public class LauncherModel extends BroadcastReceiver
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
android.util.Log.d("b/131170582", "2 mLoaderTask = " + mLoaderTask);
runOnWorkerThread(mLoaderTask);
}
}
@@ -448,7 +444,6 @@ public class LauncherModel extends BroadcastReceiver
mTask = task;
mIsLoaderTaskRunning = true;
mModelLoaded = false;
android.util.Log.d("b/131170582", "2 mModelLoaded = " + mModelLoaded);
}
}
@@ -456,7 +451,6 @@ public class LauncherModel extends BroadcastReceiver
synchronized (mLock) {
// Everything loaded bind the data.
mModelLoaded = true;
android.util.Log.d("b/131170582", "3 mModelLoaded = " + mModelLoaded);
}
}
@@ -466,7 +460,6 @@ public class LauncherModel extends BroadcastReceiver
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == mTask) {
mLoaderTask = null;
android.util.Log.d("b/131170582", "3 mLoaderTask = " + mLoaderTask);
}
mIsLoaderTaskRunning = false;
}
@@ -8,13 +8,10 @@ import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.WindowInsets;
@@ -31,9 +28,6 @@ public class LauncherRootView extends InsettableFrameLayout {
@ViewDebug.ExportedProperty(category = "launcher")
private final Rect mConsumedInsets = new Rect();
@ViewDebug.ExportedProperty(category = "launcher")
private final RectF mTouchExcludeRegion = new RectF();
@ViewDebug.ExportedProperty(category = "launcher")
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
@@ -87,7 +81,7 @@ public class LauncherRootView extends InsettableFrameLayout {
UI_STATE_ROOT_VIEW, drawInsetBar ? FLAG_DARK_NAV : 0);
// Update device profile before notifying th children.
mLauncher.getDeviceProfile().updateInsets(insets);
mLauncher.updateInsets(insets);
boolean resetState = !insets.equals(mInsets);
setInsets(insets);
@@ -120,7 +114,7 @@ public class LauncherRootView extends InsettableFrameLayout {
}
public void dispatchInsets() {
mLauncher.getDeviceProfile().updateInsets(mInsets);
mLauncher.updateInsets(mInsets);
super.setInsets(mInsets);
}
@@ -164,29 +158,10 @@ public class LauncherRootView extends InsettableFrameLayout {
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
if (Utilities.ATLEAST_Q) {
Insets gestureInsets = insets.getMandatorySystemGestureInsets();
mTouchExcludeRegion.set(gestureInsets.left, gestureInsets.top,
gestureInsets.right, gestureInsets.bottom);
}
mLauncher.getDragLayer().updateTouchExcludeRegion(insets);
return super.dispatchApplyWindowInsets(insets);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
float x = ev.getX();
float y = ev.getY();
if (y < mTouchExcludeRegion.top
|| x < mTouchExcludeRegion.left
|| x > (getWidth() - mTouchExcludeRegion.right)
|| y > (getHeight() - mTouchExcludeRegion.bottom)) {
return false;
}
}
return super.dispatchTouchEvent(ev);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
@@ -211,14 +186,4 @@ public class LauncherRootView extends InsettableFrameLayout {
void onWindowVisibilityChanged(int visibility);
}
@Override
public void requestLayout() {
super.requestLayout();
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"requestLayout @ " + android.util.Log.getStackTraceString(
new Throwable()));
}
}
}
+1 -3
View File
@@ -1088,10 +1088,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
// Skip touch handling if there are no pages to swipe
if (getChildCount() <= 0) return super.onTouchEvent(ev);
if (getChildCount() <= 0) return false;
acquireVelocityTrackerAndAddMovement(ev);
@@ -93,7 +93,7 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
public void setupLp(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (child instanceof LauncherAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
profile.appWidgetScale.x, profile.appWidgetScale.y);
} else {
@@ -108,12 +108,12 @@ public class ShortcutAndWidgetContainer extends ViewGroup {
public int getCellContentHeight() {
return Math.min(getMeasuredHeight(),
mActivity.getDeviceProfile().getCellHeight(mContainerType));
mActivity.getWallpaperDeviceProfile().getCellHeight(mContainerType));
}
public void measureChild(View child) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
final DeviceProfile profile = mActivity.getDeviceProfile();
final DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
if (child instanceof LauncherAppWidgetHostView) {
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
@@ -63,9 +63,4 @@ public final class TestProtocol {
"all-apps-to-overview-swipe-height";
public static final String REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT =
"home-to-all-apps-swipe-height";
public static boolean sDebugTracing = false;
public static final String NO_DRAG_TAG = "b/129434166";
public static final String REQUEST_ENABLE_DRAG_LOGGING = "enable-drag-logging";
public static final String REQUEST_DISABLE_DRAG_LOGGING = "disable-drag-logging";
}
+55 -73
View File
@@ -16,9 +16,6 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WallpaperManager;
@@ -63,13 +60,12 @@ import android.view.animation.Interpolator;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.io.Closeable;
@@ -97,7 +93,6 @@ public final class Utilities {
private static final int[] sLoc0 = new int[2];
private static final int[] sLoc1 = new int[2];
private static final float[] sPoint = new float[2];
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
@@ -143,7 +138,7 @@ public final class Utilities {
*/
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
@@ -170,37 +165,60 @@ public final class Utilities {
* assumption fails, we will need to return a pair of scale factors.
*/
public static float getDescendantCoordRelativeToAncestor(
View descendant, View ancestor, int[] coord, boolean includeRootScroll) {
sPoint[0] = coord[0];
sPoint[1] = coord[1];
View descendant, View ancestor, float[] coord, boolean includeRootScroll) {
return getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, includeRootScroll,
false);
}
/**
* Given a coordinate relative to the descendant, find the coordinate in a parent view's
* coordinates.
*
* @param descendant The descendant to which the passed coordinate is relative.
* @param ancestor The root view to make the coordinates relative to.
* @param coord The coordinate that we want mapped.
* @param includeRootScroll Whether or not to account for the scroll of the descendant:
* sometimes this is relevant as in a child's coordinates within the descendant.
* @param ignoreTransform If true, view transform is ignored
* @return The factor by which this descendant is scaled relative to this DragLayer. Caution
* this scale factor is assumed to be equal in X and Y, and so if at any point this
* assumption fails, we will need to return a pair of scale factors.
*/
public static float getDescendantCoordRelativeToAncestor(View descendant, View ancestor,
float[] coord, boolean includeRootScroll, boolean ignoreTransform) {
float scale = 1.0f;
View v = descendant;
while(v != ancestor && v != null) {
// For TextViews, scroll has a meaning which relates to the text position
// which is very strange... ignore the scroll.
if (v != descendant || includeRootScroll) {
sPoint[0] -= v.getScrollX();
sPoint[1] -= v.getScrollY();
offsetPoints(coord, -v.getScrollX(), -v.getScrollY());
}
v.getMatrix().mapPoints(sPoint);
sPoint[0] += v.getLeft();
sPoint[1] += v.getTop();
if (ignoreTransform) {
if (v instanceof Transposable) {
RotationMode m = ((Transposable) v).getRotationMode();
if (m.isTransposed) {
sMatrix.setRotate(m.surfaceRotation, v.getPivotX(), v.getPivotY());
sMatrix.mapPoints(coord);
}
}
} else {
v.getMatrix().mapPoints(coord);
}
offsetPoints(coord, v.getLeft(), v.getTop());
scale *= v.getScaleX();
v = (View) v.getParent();
}
coord[0] = Math.round(sPoint[0]);
coord[1] = Math.round(sPoint[1]);
return scale;
}
/**
* Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, int[], boolean)}.
* Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
*/
public static void mapCoordInSelfToDescendant(View descendant, View root, int[] coord) {
public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
sMatrix.reset();
View v = descendant;
while(v != root) {
@@ -211,12 +229,23 @@ public final class Utilities {
}
sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
sMatrix.invert(sInverseMatrix);
sInverseMatrix.mapPoints(coord);
}
sPoint[0] = coord[0];
sPoint[1] = coord[1];
sInverseMatrix.mapPoints(sPoint);
coord[0] = Math.round(sPoint[0]);
coord[1] = Math.round(sPoint[1]);
/**
* Sets {@param out} to be same as {@param in} by rounding individual values
*/
public static void roundArray(float[] in, int[] out) {
for (int i = 0; i < in.length; i++) {
out[i] = Math.round(in[i]);
}
}
public static void offsetPoints(float[] points, float offsetX, float offsetY) {
for (int i = 0; i < points.length; i += 2) {
points[i] += offsetX;
points[i + 1] += offsetY;
}
}
/**
@@ -569,53 +598,6 @@ public final class Utilities {
return String.format(Locale.ENGLISH, "%d,%d", x, y);
}
/**
* Returns the location bounds of a view.
* - For DeepShortcutView, we return the bounds of the icon view.
* - For BubbleTextView, we return the icon bounds.
*/
public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
final DragLayer dragLayer = launcher.getDragLayer();
final boolean isBubbleTextView = v instanceof BubbleTextView;
final boolean isFolderIcon = v instanceof FolderIcon;
final Rect rect = new Rect();
// Deep shortcut views have their icon drawn in a separate view.
final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
if (v instanceof DeepShortcutView) {
dragLayer.getDescendantRectRelativeToSelf(((DeepShortcutView) v).getIconView(), rect);
} else if (fromDeepShortcutView) {
DeepShortcutView view = (DeepShortcutView) v.getParent();
dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
} else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
&& (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
|| ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
CellLayout pageViewIsOn = ((CellLayout) v.getParent().getParent());
int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);
DeviceProfile dp = launcher.getDeviceProfile();
ItemInfo info = ((ItemInfo) v.getTag());
dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
} else {
dragLayer.getDescendantRectRelativeToSelf(v, rect);
}
int viewLocationLeft = rect.left;
int viewLocationTop = rect.top;
if (isBubbleTextView && !fromDeepShortcutView) {
((BubbleTextView) v).getIconBounds(rect);
} else if (isFolderIcon) {
((FolderIcon) v).getPreviewBounds(rect);
} else {
rect.set(0, 0, rect.width(), rect.height());
}
viewLocationLeft += rect.left;
viewLocationTop += rect.top;
outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + rect.width(),
viewLocationTop + rect.height());
}
public static void unregisterReceiverSafely(Context context, BroadcastReceiver receiver) {
try {
context.unregisterReceiver(receiver);
+47 -55
View File
@@ -82,6 +82,7 @@ import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
@@ -175,7 +176,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
@Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
private final Rect mTempRect = new Rect();
private final int[] mTempXY = new int[2];
private final float[] mTempFXY = new float[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
private final float[] mTempTouchCoordinates = new float[2];
@@ -285,18 +288,23 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
DeviceProfile grid = mLauncher.getDeviceProfile();
mMaxDistanceForFolderCreation = grid.isTablet
? 0.75f * grid.iconSizePx
: 0.55f * grid.iconSizePx;
DeviceProfile stableGrid = mLauncher.getWallpaperDeviceProfile();
mMaxDistanceForFolderCreation = stableGrid.isTablet
? 0.75f * stableGrid.iconSizePx
: 0.55f * stableGrid.iconSizePx;
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
Rect padding = grid.workspacePadding;
setPadding(padding.left, padding.top, padding.right, padding.bottom);
Rect padding = stableGrid.workspacePadding;
if (grid.shouldFadeAdjacentWorkspaceScreens()) {
RotationMode rotationMode = mLauncher.getRotationMode();
rotationMode.mapRect(padding, mTempRect);
setPadding(mTempRect.left, mTempRect.top, mTempRect.right, mTempRect.bottom);
rotationMode.mapRect(insets, mInsets);
if (mWorkspaceFadeInAdjacentScreens) {
// In landscape mode the page spacing is set to the default.
setPageSpacing(grid.edgeMarginPx);
} else {
@@ -306,11 +314,13 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1));
}
int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
int paddingBottom = grid.cellLayoutBottomPaddingPx;
int paddingLeftRight = stableGrid.cellLayoutPaddingLeftRightPx;
int paddingBottom = stableGrid.cellLayoutBottomPaddingPx;
for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
mWorkspaceScreens.valueAt(i)
.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
CellLayout page = mWorkspaceScreens.valueAt(i);
page.setRotationMode(rotationMode);
page.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
}
}
@@ -330,7 +340,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
float scale = 1;
if (isWidget) {
DeviceProfile profile = mLauncher.getDeviceProfile();
DeviceProfile profile = mLauncher.getWallpaperDeviceProfile();
scale = Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
size[0] = r.width();
@@ -550,8 +560,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
// created CellLayout.
CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
DeviceProfile grid = mLauncher.getWallpaperDeviceProfile();
int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
int paddingBottom = grid.cellLayoutBottomPaddingPx;
newScreen.setRotationMode(mLauncher.getRotationMode());
newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
mWorkspaceScreens.put(screenId, newScreen);
@@ -1440,10 +1452,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
DragPreviewProvider previewProvider, DragOptions dragOptions) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"beginDragShared");
}
float iconScale = 1f;
if (child instanceof BubbleTextView) {
Drawable icon = ((BubbleTextView) child).getIcon();
@@ -1934,18 +1942,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
if (child == null) {
return;
}
ShortcutAndWidgetContainer boundingLayout = child.getShortcutsAndWidgets();
// Use the absolute left instead of the child left, as we want the visible area
// irrespective of the visible child. Since the view can only scroll horizontally, the
// top position is not affected.
mTempXY[0] = getPaddingLeft() + boundingLayout.getLeft();
mTempXY[1] = child.getTop() + boundingLayout.getTop();
float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
outArea.set(mTempXY[0], mTempXY[1],
(int) (mTempXY[0] + scale * boundingLayout.getMeasuredWidth()),
(int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(boundingLayout, outArea);
}
@Override
@@ -2098,14 +2097,14 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
boolean isPointInSelfOverHotseat(int x, int y) {
mTempXY[0] = x;
mTempXY[1] = y;
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
mTempFXY[0] = x;
mTempFXY[1] = y;
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
View hotseat = mLauncher.getHotseat();
return mTempXY[0] >= hotseat.getLeft() &&
mTempXY[0] <= hotseat.getRight() &&
mTempXY[1] >= hotseat.getTop() &&
mTempXY[1] <= hotseat.getBottom();
return mTempFXY[0] >= hotseat.getLeft() &&
mTempFXY[0] <= hotseat.getRight() &&
mTempFXY[1] >= hotseat.getTop() &&
mTempFXY[1] <= hotseat.getBottom();
}
/**
@@ -2115,13 +2114,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
*/
private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
if (mLauncher.isHotseatLayout(layout)) {
mTempXY[0] = (int) xy[0];
mTempXY[1] = (int) xy[1];
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, mTempXY);
xy[0] = mTempXY[0];
xy[1] = mTempXY[1];
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
} else {
mapPointFromSelfToChild(layout, xy);
}
@@ -2612,13 +2606,14 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
DeviceProfile profile = mLauncher.getDeviceProfile();
Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
}
loc[0] = r.left;
loc[1] = r.top;
mTempFXY[0] = r.left;
mTempFXY[1] = r.top;
setFinalTransitionTransform();
float cellLayoutScale =
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, loc, true);
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(layout, mTempFXY, true);
resetTransitionTransform();
Utilities.roundArray(mTempFXY, loc);
if (scale) {
float dragViewScaleX = (1.0f * r.width()) / dragView.getMeasuredWidth();
@@ -2911,14 +2906,11 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
&& info.user.equals(user);
final Workspace.ItemOperator packageAndUserAndApp = (ItemInfo info, View view) ->
packageAndUser.evaluate(info, view) && info.itemType == ITEM_TYPE_APPLICATION;
final Workspace.ItemOperator packageAndUserAndShortcut = (ItemInfo info, View view) ->
packageAndUser.evaluate(info, view) && (info.itemType == ITEM_TYPE_SHORTCUT
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT);
final Workspace.ItemOperator packageAndUserInFolder = (info, view) -> {
final Workspace.ItemOperator packageAndUserAndAppInFolder = (info, view) -> {
if (info instanceof FolderInfo) {
FolderInfo folderInfo = (FolderInfo) info;
for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) {
if (packageAndUser.evaluate(shortcutInfo, view)) {
if (packageAndUserAndApp.evaluate(shortcutInfo, view)) {
return true;
}
}
@@ -2926,15 +2918,15 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
return false;
};
// Order: App icons, shortcuts, app/shortcut in folder. Items in hotseat get returned first.
// Order: App icons, app in folder. Items in hotseat get returned first.
if (ADAPTIVE_ICON_WINDOW_ANIM.get()) {
return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
packageAndUserAndApp, packageAndUserAndShortcut, packageAndUserInFolder);
packageAndUserAndApp, packageAndUserAndAppInFolder);
} else {
// Do not use Folder as a criteria, since it'll cause a crash when trying to draw
// FolderAdaptiveIcon as the background.
return getFirstMatch(new CellLayout[] { getHotseat(), currentPage },
packageAndUserAndApp, packageAndUserAndShortcut);
packageAndUserAndApp);
}
}
@@ -93,14 +93,16 @@ public class WorkspaceStateTransitionAnimation {
Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
// Set the hotseat's pivot point to match the workspace's, so that it scales together.
DragLayer dragLayer = mLauncher.getDragLayer();
int[] workspacePivot = new int[]{(int) mWorkspace.getPivotX(),
(int) mWorkspace.getPivotY()};
dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
hotseat.setPivotX(workspacePivot[0]);
hotseat.setPivotY(workspacePivot[1]);
if (!hotseat.getRotationMode().isTransposed) {
// Set the hotseat's pivot point to match the workspace's, so that it scales together.
DragLayer dragLayer = mLauncher.getDragLayer();
float[] workspacePivot =
new float[]{ mWorkspace.getPivotX(), mWorkspace.getPivotY() };
dragLayer.getDescendantCoordRelativeToSelf(mWorkspace, workspacePivot);
dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
hotseat.setPivotX(workspacePivot[0]);
hotseat.setPivotY(workspacePivot[1]);
}
float hotseatScale = hotseatScaleAndTranslation.scale;
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator);
@@ -34,6 +34,7 @@ public class FlingSpringAnim {
private static final float SPRING_DAMPING = SpringForce.DAMPING_RATIO_LOW_BOUNCY;
private final FlingAnimation mFlingAnim;
private SpringAnimation mSpringAnim;
public <K> FlingSpringAnim(K object, FloatPropertyCompat<K> property, float startPosition,
float targetPosition, float startVelocity, OnAnimationEndListener onEndListener) {
@@ -44,17 +45,24 @@ public class FlingSpringAnim {
.setMinValue(Math.min(startPosition, targetPosition))
.setMaxValue(Math.max(startPosition, targetPosition));
mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
SpringAnimation springAnim = new SpringAnimation(object, property)
mSpringAnim = new SpringAnimation(object, property)
.setStartVelocity(velocity)
.setSpring(new SpringForce(targetPosition)
.setStiffness(SPRING_STIFFNESS)
.setDampingRatio(SPRING_DAMPING));
springAnim.addEndListener(onEndListener);
springAnim.start();
mSpringAnim.addEndListener(onEndListener);
mSpringAnim.start();
}));
}
public void start() {
mFlingAnim.start();
}
public void end() {
mFlingAnim.cancel();
if (mSpringAnim.canSkipToEnd()) {
mSpringAnim.skipToEnd();
}
}
}
@@ -110,6 +110,10 @@ abstract class BaseFlags {
"ENABLE_HINTS_IN_OVERVIEW", false,
"Show chip hints and gleams on the overview screen");
public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag(
"FAKE_LANDSCAPE_UI", false,
"Rotate launcher UI instead of using transposed layout");
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
@@ -219,9 +219,6 @@ public class DragController implements DragDriver.EventListener, TouchController
}
private void callOnDragStart() {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, "callOnDragStart");
}
if (mOptions.preDragCondition != null) {
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
}
@@ -475,9 +472,6 @@ public class DragController implements DragDriver.EventListener, TouchController
}
private void handleMoveEvent(int x, int y) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, "handleMoveEvent1");
}
mDragObject.dragView.move(x, y);
// Drop on someone?
@@ -494,10 +488,6 @@ public class DragController implements DragDriver.EventListener, TouchController
if (mIsInPreDrag && mOptions.preDragCondition != null
&& mOptions.preDragCondition.shouldStartDrag(mDistanceSinceScroll)) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"handleMoveEvent2");
}
callOnDragStart();
}
}
@@ -535,10 +525,6 @@ public class DragController implements DragDriver.EventListener, TouchController
* Call this from a drag source view.
*/
public boolean onControllerTouchEvent(MotionEvent ev) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"onControllerTouchEvent1");
}
if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
return false;
}
@@ -559,10 +545,6 @@ public class DragController implements DragDriver.EventListener, TouchController
break;
}
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"onControllerTouchEvent2");
}
return mDragDriver.onTouchEvent(ev);
}
@@ -45,18 +45,10 @@ public abstract class DragDriver {
public void onDragViewAnimationEnd() { }
public boolean onTouchEvent(MotionEvent ev) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"onTouchEvent " + ev);
}
final int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_MOVE:
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"onTouchEvent MOVE");
}
mEventListener.onDriverDragMove(ev.getX(), ev.getY());
break;
case MotionEvent.ACTION_UP:
@@ -17,6 +17,10 @@
package com.android.launcher3.dragndrop;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
import static android.view.View.MeasureSpec.getSize;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
@@ -29,12 +33,14 @@ import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
@@ -42,6 +48,7 @@ import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
@@ -52,6 +59,7 @@ import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.Transposable;
import java.util.ArrayList;
@@ -126,10 +134,6 @@ public class DragLayer extends BaseDragLayer<Launcher> {
protected boolean findActiveController(MotionEvent ev) {
if (mActivity.getStateManager().getState().disableInteraction) {
// You Shall Not Pass!!!
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"mActiveController = null");
}
mActiveController = null;
return true;
}
@@ -264,10 +268,10 @@ public class DragLayer extends BaseDragLayer<Launcher> {
Rect r = new Rect();
getViewRectRelativeToSelf(dragView, r);
int coord[] = new int[2];
float coord[] = new float[2];
float childScale = child.getScaleX();
coord[0] = lp.x + (int) (child.getMeasuredWidth() * (1 - childScale) / 2);
coord[1] = lp.y + (int) (child.getMeasuredHeight() * (1 - childScale) / 2);
coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2);
coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2);
// Since the child hasn't necessarily been laid out, we force the lp to be updated with
// the correct coordinates (above) and use these to determine the final location
@@ -275,8 +279,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
// We need to account for the scale of the child itself, as the above only accounts for
// for the scale in parents.
scale *= childScale;
int toX = coord[0];
int toY = coord[1];
int toX = Math.round(coord[0]);
int toY = Math.round(coord[1]);
float toScale = scale;
if (child instanceof TextView) {
TextView tv = (TextView) child;
@@ -555,4 +559,159 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public WorkspaceAndHotseatScrim getScrim() {
return mScrim;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
RotationMode rotation = mActivity.getRotationMode();
int count = getChildCount();
if (!rotation.isTransposed
|| getMode(widthMeasureSpec) != EXACTLY
|| getMode(heightMeasureSpec) != EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
child.setRotation(rotation.surfaceRotation);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (!(child instanceof Transposable)) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
} else {
measureChildWithMargins(child, heightMeasureSpec, 0, widthMeasureSpec, 0);
child.setPivotX(child.getMeasuredWidth() / 2);
child.setPivotY(child.getMeasuredHeight() / 2);
child.setRotation(rotation.surfaceRotation);
}
}
setMeasuredDimension(getSize(widthMeasureSpec), getSize(heightMeasureSpec));
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
RotationMode rotation = mActivity.getRotationMode();
if (!rotation.isTransposed) {
super.onLayout(changed, left, top, right, bottom);
return;
}
final int count = getChildCount();
final int parentWidth = right - left;
final int parentHeight = bottom - top;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
if (lp instanceof LayoutParams) {
final LayoutParams dlp = (LayoutParams) lp;
if (dlp.customPosition) {
child.layout(dlp.x, dlp.y, dlp.x + dlp.width, dlp.y + dlp.height);
continue;
}
}
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = Gravity.TOP | Gravity.START;
}
final int layoutDirection = getLayoutDirection();
int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
int horizontalGravity = absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int verticalGravity = absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (child instanceof Transposable) {
if (rotation == RotationMode.SEASCAPE) {
if (horizontalGravity == Gravity.RIGHT) {
horizontalGravity = Gravity.LEFT;
} else if (horizontalGravity == Gravity.LEFT) {
horizontalGravity = Gravity.RIGHT;
}
if (verticalGravity == Gravity.TOP) {
verticalGravity = Gravity.BOTTOM;
} else if (verticalGravity == Gravity.BOTTOM) {
verticalGravity = Gravity.TOP;
}
}
switch (horizontalGravity) {
case Gravity.CENTER_HORIZONTAL:
childTop = (parentHeight - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.RIGHT:
childTop = width / 2 + lp.rightMargin - height / 2;
break;
case Gravity.LEFT:
default:
childTop = parentHeight - lp.leftMargin - width / 2 - height / 2;
}
switch (verticalGravity) {
case Gravity.CENTER_VERTICAL:
childLeft = (parentWidth - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.BOTTOM:
childLeft = parentWidth - width / 2 - height / 2 - lp.bottomMargin;
break;
case Gravity.TOP:
default:
childLeft = height / 2 - width / 2 + lp.topMargin;
}
} else {
switch (horizontalGravity) {
case Gravity.CENTER_HORIZONTAL:
childLeft = (parentWidth - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
childLeft = parentWidth - width - lp.rightMargin;
break;
case Gravity.LEFT:
default:
childLeft = lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = (parentHeight - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentHeight - height - lp.bottomMargin;
break;
default:
childTop = lp.topMargin;
}
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
+30 -4
View File
@@ -24,6 +24,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.SuppressLint;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -61,8 +62,10 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
@@ -121,6 +124,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
private static final int REORDER_DELAY = 250;
private static final int ON_EXIT_CLOSE_DELAY = 400;
private static final Rect sTempRect = new Rect();
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
private static String sDefaultFolderName;
private static String sHintText;
@@ -430,21 +434,44 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
mCurrentAnimator.cancel();
}
final Workspace workspace = mLauncher.getWorkspace();
final CellLayout currentCellLayout =
(CellLayout) workspace.getChildAt(workspace.getCurrentPage());
final boolean useHardware = shouldUseHardwareLayerForAnimation(currentCellLayout);
final boolean wasHardwareAccelerated = currentCellLayout.isHardwareLayerEnabled();
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
if (useHardware) {
currentCellLayout.enableHardwareLayer(true);
}
mState = STATE_ANIMATING;
mCurrentAnimator = a;
}
@Override
public void onAnimationEnd(Animator animation) {
if (useHardware) {
currentCellLayout.enableHardwareLayer(wasHardwareAccelerated);
}
mCurrentAnimator = null;
}
});
a.start();
}
private boolean shouldUseHardwareLayerForAnimation(CellLayout currentCellLayout) {
int folderCount = 0;
final ShortcutAndWidgetContainer container = currentCellLayout.getShortcutsAndWidgets();
for (int i = container.getChildCount() - 1; i >= 0; --i) {
final View child = container.getChildAt(i);
if (child instanceof AppWidgetHostView) return false;
if (child instanceof FolderIcon) ++folderCount;
}
return folderCount >= MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION;
}
/**
* Opens the user folder described by the specified tag. The opening of the folder
* is animated relative to the specified View. If the View is null, no animation
@@ -869,7 +896,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
DeviceProfile grid = mLauncher.getDeviceProfile();
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
DragLayer parent = mLauncher.getDragLayer();
int width = getFolderWidth();
int height = getFolderHeight();
@@ -881,8 +908,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
// We need to bound the folder to the currently visible workspace area
if (mLauncher.getStateManager().getState().overviewUi) {
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
sTempRect);
parent.getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(), sTempRect);
} else {
mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
}
@@ -159,7 +159,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
"is dependent on this");
}
DeviceProfile grid = launcher.getDeviceProfile();
DeviceProfile grid = launcher.getWallpaperDeviceProfile();
FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
.inflate(resId, group, false);
@@ -174,7 +174,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
icon.mDotRenderer = launcher.getDeviceProfile().mDotRenderer;
icon.mDotRenderer = grid.mDotRenderer;
icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
Folder folder = Folder.fromXml(launcher);
folder.setDragController(launcher.getDragController());
@@ -508,7 +508,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
public void drawDot(Canvas canvas) {
if ((mDotInfo != null && mDotInfo.hasDot()) || mDotScale > 0) {
Rect iconBounds = mDotParams.iconBounds;
BubbleTextView.getIconBounds(this, iconBounds, mLauncher.getDeviceProfile().iconSizePx);
BubbleTextView.getIconBounds(this, iconBounds,
mLauncher.getWallpaperDeviceProfile().iconSizePx);
// If we are animating to the accepting state, animate the dot out.
mDotParams.scale = Math.max(0, mDotScale - mBackground.getScaleProgress());
@@ -42,7 +42,6 @@ import android.view.View;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
/**
@@ -135,7 +134,7 @@ public class PreviewBackground {
mBgColor = ta.getColor(R.styleable.FolderIconPreview_android_colorPrimary, 0);
ta.recycle();
DeviceProfile grid = activity.getDeviceProfile();
DeviceProfile grid = activity.getWallpaperDeviceProfile();
previewSize = grid.folderIconSizePx;
basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.graphics;
import android.graphics.Rect;
public abstract class RotationMode {
public final float surfaceRotation;
public final boolean isTransposed;
private RotationMode(float surfaceRotation) {
this.surfaceRotation = surfaceRotation;
isTransposed = surfaceRotation != 0;
}
public final void mapRect(Rect rect, Rect out) {
mapRect(rect.left, rect.top, rect.right, rect.bottom, out);
}
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.set(left, top, right, bottom);
}
public static RotationMode NORMAL = new RotationMode(0) { };
public static RotationMode LANDSCAPE = new RotationMode(-90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.left = top;
out.top = right;
out.right = bottom;
out.bottom = left;
}
};
public static RotationMode SEASCAPE = new RotationMode(90) {
@Override
public void mapRect(int left, int top, int right, int bottom, Rect out) {
out.left = bottom;
out.top = left;
out.right = top;
out.bottom = right;
}
};
}
@@ -22,7 +22,6 @@ import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFI
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
@@ -173,8 +172,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
BaseDragLayer dl = getPopupContainer();
final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
if (!cameFromNavBar && !dl.isEventOverView(this, ev)) {
if (!dl.isEventOverView(this, ev)) {
mLauncher.getUserEventDispatcher().logActionTapOutside(
LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
close(true);
@@ -192,10 +190,6 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
* @return the container if shown or null.
*/
public static PopupContainerWithArrow showForIcon(BubbleTextView icon) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"PopupContainerWithArrow.showForIcon");
}
Launcher launcher = Launcher.getLauncher(icon.getContext());
if (getOpen(launcher) != null) {
// There is already an items container open, so don't open this one.
@@ -233,11 +233,6 @@ public abstract class AbstractStateChangeTouchController
@Override
public void onDragStart(boolean start) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"AbstractStateChangeTouchController.onDragStart() called with: start = [" +
start + "]");
}
mStartState = mLauncher.getStateManager().getState();
if (mStartState == ALL_APPS) {
mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
@@ -269,11 +264,6 @@ public abstract class AbstractStateChangeTouchController
public boolean onDrag(float displacement) {
float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
float progress = deltaProgress + mStartProgress;
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"AbstractStateChangeTouchController.onDrag() called with: displacement = [" +
displacement + "], progress = [" + progress + "]");
}
updateProgress(progress);
boolean isDragTowardPositive = mSwipeDirection.isPositive(
displacement - mDisplacementShift);
@@ -393,12 +383,6 @@ public abstract class AbstractStateChangeTouchController
? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS;
targetState = (interpolatedProgress > successProgress) ? mToState : mFromState;
}
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"AbstractStateChangeTouchController.onDragEnd() called with: velocity = [" +
velocity + "], fling = [" + fling + "], target state: " +
targetState.getClass().getSimpleName());
}
final float endProgress;
final float startProgress;
@@ -66,14 +66,6 @@ public class ItemClickHandler {
}
private static void onClick(View v, String sourceContainer) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"onClick() called with: v = [" + v.getClass().getSimpleName() +
"], sourceContainer = [" +
(sourceContainer != null ?
sourceContainer.getClass().getSimpleName() : "null")
+ "]");
}
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
@@ -74,10 +74,6 @@ public class ItemLongClickListener {
}
private static boolean onAllAppsItemLongClick(View v) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"onAllAppsItemLongClick");
}
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!canStartDrag(launcher)) return false;
// When we have exited all apps or are in transition, disregard long clicks
@@ -17,6 +17,7 @@
package com.android.launcher3.util;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.ColorMatrix;
@@ -26,12 +27,34 @@ import android.util.SparseArray;
import android.util.TypedValue;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
/**
* Various utility methods associated with theming.
*/
public class Themes {
public static int getActivityThemeRes(Context context) {
WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(context);
boolean darkTheme;
if (Utilities.ATLEAST_Q) {
Configuration configuration = context.getResources().getConfiguration();
int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
darkTheme = nightMode == Configuration.UI_MODE_NIGHT_YES;
} else {
darkTheme = wallpaperColorInfo.isDark();
}
if (darkTheme) {
return wallpaperColorInfo.supportsDarkText() ?
R.style.AppTheme_Dark_DarkText : R.style.AppTheme_Dark;
} else {
return wallpaperColorInfo.supportsDarkText() ?
R.style.AppTheme_DarkText : R.style.AppTheme;
}
}
public static String getDefaultBodyFont(Context context) {
TypedArray ta = context.obtainStyledAttributes(android.R.style.TextAppearance_DeviceDefault,
new int[]{android.R.attr.fontFamily});
@@ -22,6 +22,7 @@ import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.dot.DotInfo;
/**
@@ -56,6 +57,19 @@ public interface ActivityContext {
DeviceProfile getDeviceProfile();
/**
* Device profile to be used by UI elements which are shown directly on top of the wallpaper
* and whose presentation is tied to the wallpaper (and physical device) and not the activity
* configuration.
*/
default DeviceProfile getWallpaperDeviceProfile() {
return getDeviceProfile();
}
default RotationMode getRotationMode() {
return RotationMode.NORMAL;
}
static ActivityContext lookupContext(Context context) {
if (context instanceof ActivityContext) {
return (ActivityContext) context;
@@ -22,13 +22,19 @@ import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
@@ -74,18 +80,33 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
}
};
protected final int[] mTmpXY = new int[2];
// Touch is being dispatched through the normal view dispatch system
private static final int TOUCH_DISPATCHING_VIEW = 1 << 0;
// Touch is being dispatched through the normal view dispatch system, and started at the
// system gesture region
private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1;
// Touch is being dispatched through a proxy from InputMonitor
private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
protected final float[] mTmpXY = new float[2];
protected final float[] mTmpRectPoints = new float[4];
protected final Rect mHitRect = new Rect();
@ViewDebug.ExportedProperty(category = "launcher")
private final RectF mSystemGestureRegion = new RectF();
private int mTouchDispatchState = 0;
protected final T mActivity;
private final MultiValueAlpha mMultiValueAlpha;
// All the touch controllers for the view
protected TouchController[] mControllers;
// Touch controller which is currently active for the normal view dispatch
protected TouchController mActiveController;
private TouchCompleteListener mTouchCompleteListener;
// Touch controller which is being used for the proxy events
protected TouchController mProxyTouchController;
// Object controlling the current touch interaction
private Object mCurrentTouchOwner;
private TouchCompleteListener mTouchCompleteListener;
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
super(context, attrs);
@@ -113,33 +134,29 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
return findActiveController(ev);
}
protected boolean findActiveController(MotionEvent ev) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"mActiveController = null");
}
mActiveController = null;
private TouchController findControllerToHandleTouch(MotionEvent ev) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"setting controller1: " + topView.getClass().getSimpleName());
}
mActiveController = topView;
return true;
return topView;
}
for (TouchController controller : mControllers) {
if (controller.onControllerInterceptTouchEvent(ev)) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"setting controller1: " + controller.getClass().getSimpleName());
}
mActiveController = controller;
return true;
return controller;
}
}
return null;
}
protected boolean findActiveController(MotionEvent ev) {
mActiveController = null;
if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) {
// Only look for controllers if we are not dispatching from gesture area and proxy is
// not active
mActiveController = findControllerToHandleTouch(ev);
if (mActiveController != null) return true;
}
return false;
}
@@ -205,17 +222,8 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
}
if (mActiveController != null) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"BaseDragLayer before onControllerTouchEvent " +
mActiveController.getClass().getSimpleName());
}
return mActiveController.onControllerTouchEvent(ev);
} else {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"BaseDragLayer no controller");
}
// In case no child view handled the touch event, we may not get onIntercept anymore
return findActiveController(ev);
}
@@ -223,39 +231,74 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return dispatchTouchEvent(this, ev);
}
switch (ev.getAction()) {
case ACTION_DOWN: {
float x = ev.getX();
float y = ev.getY();
mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
public boolean dispatchTouchEvent(Object caller, MotionEvent ev) {
return verifyTouchDispatch(caller, ev) && super.dispatchTouchEvent(ev);
if ((y < mSystemGestureRegion.top
|| x < mSystemGestureRegion.left
|| x > (getWidth() - mSystemGestureRegion.right)
|| y > (getHeight() - mSystemGestureRegion.bottom))) {
mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
} else {
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
}
break;
}
case ACTION_CANCEL:
case ACTION_UP:
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
break;
}
super.dispatchTouchEvent(ev);
// We want to get all events so that mTouchDispatchSource is maintained properly
return true;
}
/**
* Returns true if the {@param caller} is allowed to dispatch {@param ev} on this view,
* false otherwise.
* Called before we are about to receive proxy events.
*
* @return false if we can't handle proxy at this time
*/
private boolean verifyTouchDispatch(Object caller, MotionEvent ev) {
int action = ev.getAction();
if (action == ACTION_DOWN) {
if (mCurrentTouchOwner != null) {
// Another touch in progress.
ev.setAction(ACTION_CANCEL);
super.dispatchTouchEvent(ev);
ev.setAction(action);
}
mCurrentTouchOwner = caller;
return true;
}
if (mCurrentTouchOwner != caller) {
// Someone else is controlling the touch
public boolean prepareProxyEventStarting() {
mProxyTouchController = null;
if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) {
// We are already dispatching using view system and have an active controller, we can't
// handle another controller.
// This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just
// to be safe
mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
return false;
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mCurrentTouchOwner = null;
}
mTouchDispatchState |= TOUCH_DISPATCHING_PROXY;
return true;
}
/**
* Proxies the touch events to the gesture handlers
*/
public boolean proxyTouchEvent(MotionEvent ev) {
boolean handled;
if (mProxyTouchController != null) {
handled = mProxyTouchController.onControllerTouchEvent(ev);
} else {
mProxyTouchController = findControllerToHandleTouch(ev);
handled = mProxyTouchController != null;
}
int action = ev.getAction();
if (action == ACTION_UP || action == ACTION_CANCEL) {
mProxyTouchController = null;
mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
}
return handled;
}
/**
* Determine the rect of the descendant in this DragLayer's coordinates
*
@@ -264,14 +307,16 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
* @return The factor by which this descendant is scaled relative to this DragLayer.
*/
public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
mTmpXY[0] = 0;
mTmpXY[1] = 0;
float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
r.set(mTmpXY[0], mTmpXY[1],
(int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
(int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
return scale;
mTmpRectPoints[0] = 0;
mTmpRectPoints[1] = 0;
mTmpRectPoints[2] = descendant.getWidth();
mTmpRectPoints[3] = descendant.getHeight();
float s = getDescendantCoordRelativeToSelf(descendant, mTmpRectPoints);
r.left = Math.round(Math.min(mTmpRectPoints[0], mTmpRectPoints[2]));
r.top = Math.round(Math.min(mTmpRectPoints[1], mTmpRectPoints[3]));
r.right = Math.round(Math.max(mTmpRectPoints[0], mTmpRectPoints[2]));
r.bottom = Math.round(Math.max(mTmpRectPoints[1], mTmpRectPoints[3]));
return s;
}
public float getLocationInDragLayer(View child, int[] loc) {
@@ -281,6 +326,14 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
}
public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
mTmpXY[0] = coord[0];
mTmpXY[1] = coord[1];
float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
Utilities.roundArray(mTmpXY, coord);
return scale;
}
public float getDescendantCoordRelativeToSelf(View descendant, float[] coord) {
return getDescendantCoordRelativeToSelf(descendant, coord, false);
}
@@ -296,17 +349,27 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
* this scale factor is assumed to be equal in X and Y, and so if at any point this
* assumption fails, we will need to return a pair of scale factors.
*/
public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
public float getDescendantCoordRelativeToSelf(View descendant, float[] coord,
boolean includeRootScroll) {
return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
coord, includeRootScroll);
}
/**
* Inverse of {@link #getDescendantCoordRelativeToSelf(View, float[])}.
*/
public void mapCoordInSelfToDescendant(View descendant, float[] coord) {
Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
}
/**
* Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
*/
public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
mTmpXY[0] = coord[0];
mTmpXY[1] = coord[1];
Utilities.mapCoordInSelfToDescendant(descendant, this, mTmpXY);
Utilities.roundArray(mTmpXY, coord);
}
public void getViewRectRelativeToSelf(View v, Rect r) {
@@ -423,4 +486,13 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
}
}
}
@TargetApi(Build.VERSION_CODES.Q)
public void updateTouchExcludeRegion(WindowInsets insets) {
if (Utilities.ATLEAST_Q) {
Insets gestureInsets = insets.getMandatorySystemGestureInsets();
mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top,
gestureInsets.right, gestureInsets.bottom);
}
}
}
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.views;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -23,6 +26,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -61,23 +65,23 @@ import com.android.launcher3.shortcuts.DeepShortcutView;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import static com.android.launcher3.Utilities.mapToRange;
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
@TargetApi(Build.VERSION_CODES.Q)
public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
public static final float SHAPE_PROGRESS_DURATION = 0.15f;
private static final int FADE_DURATION_MS = 200;
private static final Rect sTmpRect = new Rect();
private static final Object[] sTmpObjArray = new Object[1];
private Runnable mEndRunnable;
private CancellationSignal mLoadIconSignal;
private final int mBlurSizeOutline;
private boolean mIsVerticalBarLayout = false;
private boolean mIsAdaptiveIcon = false;
private @Nullable Drawable mForeground;
@@ -185,14 +189,16 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
* @param v The view to copy
* @param positionOut Rect that will hold the size and position of v.
*/
private void matchPositionOf(Launcher launcher, View v, Rect positionOut) {
Utilities.getLocationBoundsForView(launcher, v, positionOut);
final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
private void matchPositionOf(Launcher launcher, View v, RectF positionOut) {
getLocationBoundsForView(launcher, v, positionOut);
final LayoutParams lp = new LayoutParams(
Math.round(positionOut.width()),
Math.round(positionOut.height()));
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
lp.leftMargin = positionOut.left;
lp.topMargin = positionOut.top;
lp.leftMargin = Math.round(positionOut.left);
lp.topMargin = Math.round(positionOut.top);
setLayoutParams(lp);
// Set the properties here already to make sure they are available when running the first
// animation frame.
@@ -200,6 +206,57 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
+ lp.height);
}
/**
* Returns the location bounds of a view.
* - For DeepShortcutView, we return the bounds of the icon view.
* - For BubbleTextView, we return the icon bounds.
*/
private void getLocationBoundsForView(Launcher launcher, View v, RectF outRect) {
final boolean isBubbleTextView = v instanceof BubbleTextView;
final boolean isFolderIcon = v instanceof FolderIcon;
// Deep shortcut views have their icon drawn in a separate view.
final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
final View targetView;
boolean ignoreTransform = false;
if (v instanceof DeepShortcutView) {
targetView = ((DeepShortcutView) v).getIconView();
} else if (fromDeepShortcutView) {
DeepShortcutView view = (DeepShortcutView) v.getParent();
targetView = view.getIconView();
} else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
&& (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
|| ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
targetView = v;
ignoreTransform = true;
} else {
targetView = v;
}
float[] points = new float[] {0, 0, targetView.getWidth(), targetView.getHeight()};
Utilities.getDescendantCoordRelativeToAncestor(targetView, launcher.getDragLayer(), points,
false, ignoreTransform);
float viewLocationLeft = Math.min(points[0], points[2]);
float viewLocationTop = Math.min(points[1], points[3]);
final Rect iconRect = new Rect();
if (isBubbleTextView && !fromDeepShortcutView) {
((BubbleTextView) v).getIconBounds(iconRect);
} else if (isFolderIcon) {
((FolderIcon) v).getPreviewBounds(iconRect);
} else {
iconRect.set(0, 0, Math.abs(Math.round(points[2] - points[0])),
Math.abs(Math.round(points[3] - points[1])));
}
viewLocationLeft += iconRect.left;
viewLocationTop += iconRect.top;
outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + iconRect.width(),
viewLocationTop + iconRect.height());
}
@WorkerThread
private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening,
Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) {
@@ -207,10 +264,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
Drawable drawable = null;
boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
// Similar to DragView, we simply use the BubbleTextView icon here.
drawable = ((BubbleTextView) v).getIcon();
}
Drawable btvIcon = v instanceof BubbleTextView ? ((BubbleTextView) v).getIcon() : null;
if (info instanceof SystemShortcut) {
if (v instanceof ImageView) {
drawable = ((ImageView) v).getDrawable();
@@ -219,10 +273,24 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
} else {
drawable = v.getBackground();
}
}
if (drawable == null) {
drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
false, new Object[1]);
} else {
if (supportsAdaptiveIcons) {
drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
false, sTmpObjArray);
if (!(drawable instanceof AdaptiveIconDrawable)) {
// The drawable we get back is not an adaptive icon, so we need to use the
// BubbleTextView icon that is already legacy treated.
drawable = btvIcon;
}
} else {
if (v instanceof BubbleTextView) {
// Similar to DragView, we simply use the BubbleTextView icon here.
drawable = btvIcon;
} else {
drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
false, sTmpObjArray);
}
}
}
Drawable finalDrawable = drawable == null ? null
@@ -273,7 +341,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
}
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 +386,13 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
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);
}
@@ -405,11 +478,12 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
* @param isOpening True if this view replaces the icon for app open animation.
*/
public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
boolean hideOriginal, Rect positionOut, boolean isOpening, FloatingIconView recycle) {
boolean hideOriginal, RectF positionOut, boolean isOpening, FloatingIconView recycle) {
if (recycle != null) {
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);
@@ -0,0 +1,26 @@
/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.views;
import com.android.launcher3.graphics.RotationMode;
/**
* Indicates that a view can be transposed.
*/
public interface Transposable {
RotationMode getRotationMode();
}
+74 -1
View File
@@ -99,7 +99,8 @@
</activity>
<activity
android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
android:label="LauncherTestApp">
android:label="LauncherTestApp"
android:taskAffinity="com.android.launcher3.testcomponent.Affinity1">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -111,5 +112,77 @@
<meta-data android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"/>
</activity>
<activity-alias android:name="Activity2"
android:label="TestActivity2"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity3"
android:label="TestActivity3"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity4"
android:label="TestActivity4"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity5"
android:label="TestActivity5"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity6"
android:label="TestActivity6"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity7"
android:label="TestActivity7"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity8"
android:label="TestActivity8"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity9"
android:label="TestActivity9"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias android:name="Activity10"
android:label="TestActivity10"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
</application>
</manifest>
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity1 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity10 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity2 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity3 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity4 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity5 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity6 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity7 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity8 extends Activity {
}
@@ -1,24 +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.launcher3.testcomponent;
import android.app.Activity;
/**
* Base activity with utility methods to help automate testing.
*/
public class MainActivity9 extends Activity {
}
@@ -17,6 +17,11 @@ package com.android.launcher3.ui;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -24,11 +29,13 @@ import static java.lang.System.exit;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -57,6 +64,7 @@ import com.android.launcher3.util.rule.LauncherActivityRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TestRule;
@@ -67,6 +75,7 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -102,6 +111,68 @@ public abstract class AbstractLauncherUiTest {
}
if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
mLauncher = new LauncherInstrumentation(instrumentation);
// b/130558787; b/131419978
if (TestHelpers.isInLauncherProcess() && !LauncherInstrumentation.needSlowGestures()) {
try {
Class systemProps = Class.forName("android.os.SystemProperties");
Method getInt = systemProps.getMethod("getInt", String.class, int.class);
int apiLevel = (int) getInt.invoke(null, "ro.product.first_api_level", 0);
if (apiLevel >= Build.VERSION_CODES.P) {
setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
LauncherInstrumentation.NavigationModel.TWO_BUTTON);
}
if (apiLevel >= Build.VERSION_CODES.O && apiLevel < Build.VERSION_CODES.P) {
setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
}
if (apiLevel < Build.VERSION_CODES.O) {
setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
LauncherInstrumentation.NavigationModel.THREE_BUTTON);
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
public void setActiveOverlay(String overlayPackage,
LauncherInstrumentation.NavigationModel expectedMode) {
setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
for (int i = 0; i != 100; ++i) {
if (mLauncher.getNavigationModel() == expectedMode) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Assert.fail("Couldn't switch to " + overlayPackage);
}
private void setOverlayPackageEnabled(String overlayPackage, boolean enable) {
Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
final String action = enable ? "enable" : "disable";
try {
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"cmd overlay " + action + " " + overlayPackage);
} catch (IOException e) {
e.printStackTrace();
}
}
@Rule
@@ -357,6 +428,22 @@ public abstract class AbstractLauncherUiTest {
mDevice.wait(Until.hasObject(By.pkg(packageName).depth(0)), LONG_WAIT_TIME_MS));
}
protected void startTestActivity(int activityNumber) {
final String packageName = getAppPackageName();
final Instrumentation instrumentation = getInstrumentation();
final Intent intent = instrumentation.getContext().getPackageManager().
getLaunchIntentForPackage(packageName);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setComponent(new ComponentName(packageName,
"com.android.launcher3.tests.Activity" + activityNumber));
instrumentation.getTargetContext().startActivity(intent);
assertTrue(packageName + " didn't start",
mDevice.wait(
Until.hasObject(By.pkg(packageName).text("TestActivity" + activityNumber)),
LONG_WAIT_TIME_MS));
}
protected static String resolveSystemApp(String category) {
return getInstrumentation().getContext().getPackageManager().resolveActivity(
new Intent(Intent.ACTION_MAIN).addCategory(category),
@@ -71,7 +71,7 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
// Convert test to TAPL; b/131116002
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
@@ -86,7 +86,7 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
// Convert test to TAPL; b/131116002
public void testCustomProfileLoaded_with_widget() throws Exception {
// A non-restored widget with no config screen gets restored automatically.
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -106,7 +106,7 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
// Convert test to TAPL; b/131116002
public void testCustomProfileLoaded_with_folder() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
.addApp(SETTINGS_APP, SETTINGS_APP)
@@ -33,7 +33,6 @@ import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.TestProtocol;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.tapl.AppIcon;
@@ -239,12 +238,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
// Test starting a workspace app.
final AppIcon app = workspace.tryGetWorkspaceAppIcon("Chrome");
assertNotNull("No Chrome app in workspace", app);
assertNotNull("AppIcon.launch returned null",
app.launch(resolveSystemApp(Intent.CATEGORY_APP_BROWSER)));
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
isInBackground(launcher)));
}
public static void runIconLaunchFromAllAppsTest(AbstractLauncherUiTest test, AllApps allApps) {
@@ -329,20 +322,19 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
@Test
@PortraitLandscape
public void testDragAppIcon() throws Throwable {
try {
TestProtocol.sDebugTracing = true;
// 1. Open all apps and wait for load complete.
// 2. Drag icon to homescreen.
// 3. Verify that the icon works on homescreen.
mLauncher.getWorkspace().
switchToAllApps().
getAppIcon(APP_NAME).
dragToWorkspace().
getWorkspaceAppIcon(APP_NAME).
launch(getAppPackageName());
} finally {
TestProtocol.sDebugTracing = false;
}
// 1. Open all apps and wait for load complete.
// 2. Drag icon to homescreen.
// 3. Verify that the icon works on homescreen.
mLauncher.getWorkspace().
switchToAllApps().
getAppIcon(APP_NAME).
dragToWorkspace().
getWorkspaceAppIcon(APP_NAME).
launch(getAppPackageName());
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
isInBackground(launcher)));
}
@Test
@@ -71,25 +71,25 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest {
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
// Convert test to TAPL b/131116002
public void testWidgetConfig() throws Throwable {
runTest(false, true);
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
@Ignore // b/121280703
public void testWidgetConfig_rotate() throws Throwable {
runTest(true, true);
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
// Convert test to TAPL b/131116002
public void testConfigCancelled() throws Throwable {
runTest(false, false);
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
@Ignore // b/121280703
public void testConfigCancelled_rotate() throws Throwable {
runTest(true, false);
}
@@ -49,19 +49,19 @@ public class AddWidgetTest extends AbstractLauncherUiTest {
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
public void testDragIcon_portrait() throws Throwable {
lockRotation(true);
performTest();
}
@Test
@Ignore // Convert test to TAPL and enable them; b/131116002
@Ignore // b/121280703
public void testDragIcon_landscape() throws Throwable {
lockRotation(false);
performTest();
}
// Convert to TAPL b/131116002
private void performTest() throws Throwable {
clearHomescreen();
mActivityMonitor.startLauncher();
@@ -69,6 +69,8 @@ public class AllApps extends LauncherInstrumentation.VisibleContainer {
if (mLauncher.getNavigationModel() != ZERO_BUTTON) {
final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1);
} else {
allAppsContainer.setGestureMargins(0, 0, 0, 100);
}
final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
@@ -28,7 +28,7 @@ import java.util.List;
* 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) {

Some files were not shown because too many files have changed in this diff Show More