Merge "Remove accessibility long click option if no long click action occurs." into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index c7a0253..d7191b4 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -138,14 +138,6 @@
</activity>
<!--
- Should point to the content provider which can be used to dump Launcher3 compatible
- worspace configuration to the dump's file descriptor by using launcher_dump.proto
- -->
- <meta-data
- android:name="com.android.launcher3.launcher_dump_provider"
- android:value="com.android.launcher3.LauncherProvider" />
-
- <!--
The settings provider contains Home's data, like the workspace favorites. The permissions
should be changed to what is defined above. The authorities should also be changed to
represent the package name.
diff --git a/protos/launcher_dump.proto b/protos/launcher_dump.proto
deleted file mode 100644
index dc8fbda..0000000
--- a/protos/launcher_dump.proto
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-syntax = "proto2";
-
-option java_package = "com.android.launcher3.model";
-option java_outer_classname = "LauncherDumpProto";
-
-package model;
-
-message DumpTarget {
- enum Type {
- NONE = 0;
- ITEM = 1;
- CONTAINER = 2;
- }
-
- optional Type type = 1;
- optional int32 page_id = 2;
- optional int32 grid_x = 3;
- optional int32 grid_y = 4;
-
- // For container types only
- optional ContainerType container_type = 5;
-
- // For item types only
- optional ItemType item_type = 6;
-
- optional string package_name = 7; // All ItemTypes except UNKNOWN type
- optional string component = 8; // All ItemTypes except UNKNOWN type
- optional string item_id = 9; // For Pinned Shortcuts and appWidgetId
-
- optional int32 span_x = 10 [default = 1];// Used for ItemType.WIDGET
- optional int32 span_y = 11 [default = 1];// Used for ItemType.WIDGET
- optional UserType user_type = 12;
-}
-
-// Used to define what type of item a Target would represent.
-enum ItemType {
- UNKNOWN_ITEMTYPE = 0; // Launcher specific items
- APP_ICON = 1; // Regular app icons
- WIDGET = 2; // Elements from AppWidgetManager
- SHORTCUT = 3; // ShortcutManager
-}
-
-// Used to define what type of container a Target would represent.
-enum ContainerType {
- UNKNOWN_CONTAINERTYPE = 0;
- WORKSPACE = 1;
- HOTSEAT = 2;
- FOLDER = 3;
-}
-
-// Used to define what type of control a Target would represent.
-enum UserType {
- DEFAULT = 0;
- WORK = 1;
-}
-
-// Main message;
-message LauncherImpression {
- repeated DumpTarget targets = 1;
-}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 1d0b045..04506b5 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -73,6 +73,17 @@
</intent-filter>
</provider>
+ <!-- FileProvider used for sharing images. -->
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="${packageName}.overview.fileprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/overview_file_provider_paths" />
+ </provider>
+
<service
android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
tools:node="remove" />
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 549187f..131b71f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,18 +15,22 @@
*/
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
+import android.view.View;
+import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.PendingAnimation;
@@ -44,7 +48,7 @@
public final class RecentsViewStateController extends
BaseRecentsViewStateController<LauncherRecentsView> {
- public RecentsViewStateController(Launcher launcher) {
+ public RecentsViewStateController(BaseQuickstepLauncher launcher) {
super(launcher);
}
@@ -55,7 +59,7 @@
mRecentsView.updateEmptyMessage();
mRecentsView.resetTaskVisuals();
}
- setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state.getVisibleElements(mLauncher));
+ setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state, LINEAR);
mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
}
@@ -73,15 +77,22 @@
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
}
- setAlphas(builder, toState.getVisibleElements(mLauncher));
+ setAlphas(builder, toState,
+ config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
toState.getOverviewFullscreenProgress(), LINEAR);
}
- private void setAlphas(PropertySetter propertySetter, int visibleElements) {
- boolean hasClearAllButton = (visibleElements & RECENTS_CLEAR_ALL_BUTTON) != 0;
+ private void setAlphas(PropertySetter propertySetter, LauncherState state,
+ Interpolator actionInterpolator) {
+ float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0;
propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
- hasClearAllButton ? 1f : 0f, LINEAR);
+ buttonAlpha, LINEAR);
+
+ View actionsView = mLauncher.getActionsView();
+ if (actionsView != null) {
+ propertySetter.setViewAlpha(actionsView, buttonAlpha, actionInterpolator);
+ }
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index de3fce1..a87d6d1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -90,7 +90,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
return super.getVisibleElements(launcher)
- & ~RECENTS_CLEAR_ALL_BUTTON & ~VERTICAL_SWIPE_INDICATOR;
+ & ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
index 8087611..1288e7b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -25,6 +25,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.states.StateAnimationConfig;
public class OverviewPeekState extends OverviewState {
@@ -37,6 +38,9 @@
ScaleAndTranslation result = super.getOverviewScaleAndTranslation(launcher);
result.translationX = NORMAL.getOverviewScaleAndTranslation(launcher).translationX
- launcher.getResources().getDimension(R.dimen.overview_peek_distance);
+ if (Utilities.isRtl(launcher.getResources())) {
+ result.translationX = -result.translationX;
+ }
return result;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 024872f..bcfb11c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -164,17 +164,15 @@
@Override
public int getVisibleElements(Launcher launcher) {
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
+ if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
+ return OVERVIEW_BUTTONS;
+ } else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
} else {
- if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
- return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
- }
-
boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
&& launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
- return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
- (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
+ return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS
+ | (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index 6fc03b1..8af2747 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -73,7 +73,11 @@
super(l, false /* allowDragToOverview */);
mMotionPauseDetector = new MotionPauseDetector(l);
mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
- mMotionPauseMaxDisplacement = getShiftRange() * MAX_DISPLACEMENT_PERCENT;
+ mMotionPauseMaxDisplacement = getMotionPauseMaxDisplacement();
+ }
+
+ protected float getMotionPauseMaxDisplacement() {
+ return getShiftRange() * MAX_DISPLACEMENT_PERCENT;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 064133c..71aa2e8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -66,6 +66,13 @@
}
@Override
+ protected float getMotionPauseMaxDisplacement() {
+ // No need to disallow pause when swiping up all the way up the screen (unlike
+ // FlingAndHoldTouchController where user is probably intending to go to all apps).
+ return Float.MAX_VALUE;
+ }
+
+ @Override
protected boolean canInterceptTouch(MotionEvent ev) {
mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
return super.canInterceptTouch(ev);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 7786a8f..113cdec 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -31,6 +31,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
@@ -48,6 +49,7 @@
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.VibratorWrapper;
@@ -198,18 +200,33 @@
}
protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask1");
+ }
// Launch the task user scrolled to (mRecentsView.getNextPage()).
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// We finish recents animation inside launchTask() when live tile is enabled.
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask2");
+ }
int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
mFinishingRecentsAnimationForNewTaskId = taskId;
mRecentsAnimationController.finish(true /* toRecents */, () -> {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "onFinishComplete1");
+ }
if (!mCanceled) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "onFinishComplete2");
+ }
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "onFinishComplete3");
+ }
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
success -> {
resultCallback.accept(success);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index ea5561b..da73bc0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -166,6 +166,7 @@
super.onActivityInit(alreadyOnHome);
mActivity = mActivityInterface.getCreatedActivity();
mRecentsView = mActivity.getOverviewPanel();
+ mRecentsView.setOnPageTransitionEndCallback(null);
linkRecentsViewScroll();
mRecentsView.setDisallowScrollToClearAll(true);
mRecentsView.getClearAllButton().setVisibilityAlpha(0);
@@ -434,7 +435,12 @@
@Override
public void onAnimationSuccess(Animator animator) {
- finishAnimationTargetSetAnimationComplete();
+ if (mRecentsView != null) {
+ mRecentsView.setOnPageTransitionEndCallback(FallbackSwipeHandler.this
+ ::finishAnimationTargetSetAnimationComplete);
+ } else {
+ finishAnimationTargetSetAnimationComplete();
+ }
mFinishAnimation = null;
}
};
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java
new file mode 100644
index 0000000..33fe5a9
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 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.content.Intent.EXTRA_STREAM;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.util.ImageActionUtils.persistBitmapAndStartActivity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.BuildConfig;
+import com.android.quickstep.util.ImageActionUtils;
+
+import java.util.function.Supplier;
+
+/**
+ * Contains image selection functions necessary to complete overview action button functions.
+ */
+public class ImageActionsApi {
+
+ private static final String TAG = BuildConfig.APPLICATION_ID + "ImageActionsApi";
+ private final Context mContext;
+ private final Supplier<Bitmap> mBitmapSupplier;
+ private final SystemUiProxy mSystemUiProxy;
+
+ public ImageActionsApi(Context context, Supplier<Bitmap> bitmapSupplier) {
+ mContext = context;
+ mBitmapSupplier = bitmapSupplier;
+ mSystemUiProxy = SystemUiProxy.INSTANCE.get(context);
+ }
+
+ /**
+ * Share the image this api was constructed with using the provided intent. The implementation
+ * should add an {@link Intent#EXTRA_STREAM} with the URI pointing to the image to the intent.
+ */
+ @UiThread
+ public void shareWithExplicitIntent(@Nullable Rect crop, Intent intent) {
+ if (mBitmapSupplier.get() == null) {
+ Log.e(TAG, "No snapshot available, not starting share.");
+ return;
+ }
+
+ UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
+ mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
+ intentForUri.putExtra(EXTRA_STREAM, uri);
+ return new Intent[]{intentForUri};
+ }, TAG));
+
+ }
+
+ /**
+ * Share the image this api was constructed with.
+ */
+ @UiThread
+ public void startShareActivity() {
+ ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, null, null, TAG);
+ }
+
+ /**
+ * @param screenshot to be saved to the media store.
+ * @param screenshotBounds the location of where the bitmap was laid out on the screen in
+ * screen coordinates.
+ * @param visibleInsets that are used to draw the screenshot within the bounds.
+ * @param taskId of the task that the screenshot was taken of.
+ */
+ public void saveScreenshot(Bitmap screenshot, Rect screenshotBounds,
+ Insets visibleInsets, int taskId) {
+ ImageActionUtils.saveScreenshot(mSystemUiProxy, screenshot, screenshotBounds, visibleInsets,
+ taskId);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 5bac844..b3b0b02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -29,6 +29,7 @@
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
+import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
@@ -46,6 +47,7 @@
import android.graphics.RectF;
import android.os.Build;
import android.os.SystemClock;
+import android.util.Log;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
@@ -64,6 +66,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -244,7 +247,11 @@
| STATE_GESTURE_STARTED,
this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
- mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, this::onEndTargetSet);
+ mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED,
+ this::continueComputingRecentsScrollIfNecessary);
+ mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED
+ | STATE_RECENTS_SCROLLING_FINISHED,
+ this::onSettledOnEndTarget);
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
@@ -283,6 +290,7 @@
}
mRecentsView = activity.getOverviewPanel();
+ mRecentsView.setOnPageTransitionEndCallback(null);
linkRecentsViewScroll();
addLiveTileOverlay();
@@ -505,16 +513,22 @@
}
private void buildAnimationController() {
- if (mGestureState.getEndTarget() == HOME || mHasLauncherTransitionControllerStarted) {
- // We don't want a new mLauncherTransitionController if
- // mGestureState.getEndTarget() == HOME (it has its own animation) or if we're already
- // animating the current controller.
+ if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
mAnimationFactory.createActivityInterface(mTransitionDragLength);
}
+ /**
+ * We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
+ * (it has its own animation) or if we're already animating the current controller.
+ * @return Whether we can create the launcher controller or update its progress.
+ */
+ private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
+ return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
+ }
+
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
WindowInsets result = view.onApplyWindowInsets(windowInsets);
@@ -558,15 +572,12 @@
}
}
- if (mLauncherTransitionController == null || mLauncherTransitionController
- .getAnimationPlayer().isStarted()) {
- return;
- }
updateLauncherTransitionProgress();
}
private void updateLauncherTransitionProgress() {
- if (mGestureState.getEndTarget() == HOME) {
+ if (mLauncherTransitionController == null
+ || !canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
// Normalize the progress to 0 to 1, as the animation controller will clamp it to that
@@ -696,7 +707,7 @@
}
}
- private void onEndTargetSet() {
+ private void onSettledOnEndTarget() {
switch (mGestureState.getEndTarget()) {
case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
@@ -859,13 +870,17 @@
if (mDeviceState.isFullyGesturalNavMode()) {
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
}
- } else if (endTarget == NEW_TASK || endTarget == LAST_TASK) {
- // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
- // or resumeLastTask().
- if (mRecentsView != null) {
- duration = Math.max(duration, mRecentsView.getScroller().getDuration());
- }
}
+
+ // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
+ // or resumeLastTask().
+ if (mRecentsView != null) {
+ mRecentsView.setOnPageTransitionEndCallback(
+ () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
+ } else {
+ mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+ }
+
animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
@@ -949,15 +964,14 @@
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
windowAnim.setDuration(duration).setInterpolator(interpolator);
windowAnim.addUpdateListener(valueAnimator -> {
- if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
- // Views typically don't compute scroll when invisible as an optimization,
- // but in our case we need to since the window offset depends on the scroll.
- mRecentsView.computeScroll();
- }
+ computeRecentsScrollIfInvisible();
});
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationSuccess");
+ }
if (mRecentsAnimationController == null) {
// If the recents animation is interrupted, we still end the running
// animation (not canceled) so this is still called. In that case, we can
@@ -1005,6 +1019,21 @@
mHasLauncherTransitionControllerStarted = true;
}
+ private void computeRecentsScrollIfInvisible() {
+ if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
+ // Views typically don't compute scroll when invisible as an optimization,
+ // but in our case we need to since the window offset depends on the scroll.
+ mRecentsView.computeScroll();
+ }
+ }
+
+ private void continueComputingRecentsScrollIfNecessary() {
+ if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED)) {
+ computeRecentsScrollIfInvisible();
+ mRecentsView.post(this::continueComputingRecentsScrollIfNecessary);
+ }
+ }
+
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
@@ -1166,6 +1195,9 @@
}
private void switchToScreenshot() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "switchToScreenshot");
+ }
final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 33d9d9a..fbf29af 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -16,12 +16,13 @@
package com.android.quickstep;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
+import android.content.Context;
+import android.graphics.Insets;
import android.graphics.Matrix;
-import android.view.View;
-
-import androidx.annotation.Nullable;
+import android.graphics.Rect;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
@@ -29,6 +30,7 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.OverscrollPlugin;
@@ -43,16 +45,6 @@
*/
public class TaskOverlayFactory implements ResourceBasedOverride {
- /** Note that these will be shown in order from top to bottom, if available for the task. */
- private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
- TaskShortcutFactory.APP_INFO,
- TaskShortcutFactory.SPLIT_SCREEN,
- TaskShortcutFactory.PIN,
- TaskShortcutFactory.INSTALL,
- TaskShortcutFactory.FREE_FORM,
- TaskShortcutFactory.WELLBEING
- };
-
public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
@@ -76,25 +68,68 @@
}
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
- return new TaskOverlay();
+ return new TaskOverlay(thumbnailView);
}
+ /** Note that these will be shown in order from top to bottom, if available for the task. */
+ private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
+ TaskShortcutFactory.APP_INFO,
+ TaskShortcutFactory.SPLIT_SCREEN,
+ TaskShortcutFactory.PIN,
+ TaskShortcutFactory.INSTALL,
+ TaskShortcutFactory.FREE_FORM,
+ TaskShortcutFactory.WELLBEING
+ };
+
+ /**
+ * Overlay on each task handling Overview Action Buttons.
+ */
public static class TaskOverlay {
+ private final Context mApplicationContext;
+ private OverviewActionsView mActionsView;
+ private final TaskThumbnailView mThumbnailView;
+
+
+ protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
+ mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
+ mThumbnailView = taskThumbnailView;
+ }
+
/**
* Called when the current task is interactive for the user
*/
- public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) { }
+ public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
+ ImageActionsApi imageApi = new ImageActionsApi(
+ mApplicationContext, mThumbnailView::getThumbnail);
- @Nullable
- public View getActionsView() {
- return null;
+ if (mActionsView == null && ENABLE_OVERVIEW_ACTIONS.get()
+ && SysUINavigationMode.removeShelfFromOverview(mApplicationContext)) {
+ mActionsView = BaseActivity.fromContext(mThumbnailView.getContext()).findViewById(
+ R.id.overview_actions_view);
+ }
+ if (mActionsView != null) {
+ mActionsView.setListener(new OverviewActionsView.Listener() {
+ @Override
+ public void onShare() {
+ imageApi.startShareActivity();
+ }
+
+ @Override
+ public void onScreenshot() {
+ imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
+ getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key.id);
+ }
+ });
+ }
+
}
/**
* Called when the overlay is no longer used.
*/
- public void reset() { }
+ public void reset() {
+ }
/**
* Whether the overlay is modal, which means only tapping is enabled, but no swiping.
@@ -102,5 +137,28 @@
public boolean isOverlayModal() {
return false;
}
+
+ /**
+ * Gets the task snapshot as it is displayed on the screen.
+ *
+ * @return the bounds of the snapshot in screen coordinates.
+ */
+ public Rect getTaskSnapshotBounds() {
+ int[] location = new int[2];
+ mThumbnailView.getLocationOnScreen(location);
+
+ return new Rect(location[0], location[1], mThumbnailView.getWidth() + location[0],
+ mThumbnailView.getHeight() + location[1]);
+ }
+
+ /**
+ * Gets the insets that the snapshot is drawn with.
+ *
+ * @return the insets in screen coordinates.
+ */
+ public Insets getTaskSnapshotInsets() {
+ // TODO: return the real insets
+ return Insets.of(0, 0, 0, 0);
+ }
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index a87e7eb..71465eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -41,7 +41,7 @@
protected void setActive(MotionEvent ev) {
mState = STATE_ACTIVE;
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
// Send cancel event
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index ba1d38c..7b8d40c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -204,7 +204,7 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
Intent intent = new Intent(Intent.ACTION_MAIN)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 416d7a1..a462949 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -314,7 +314,7 @@
if (mInteractionHandler == null) {
return;
}
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
mActivityInterface.closeOverlay();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index f161cc0..6bfabcd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -108,7 +108,7 @@
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 823b254..ac1c3a8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -65,7 +65,7 @@
private void onInterceptTouch() {
if (mInputMonitor != null) {
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 24703bd..e62de18 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -323,7 +323,7 @@
if (enabled) {
LauncherState state = mActivity.getStateManager().getState();
boolean hasClearAllButton = (state.getVisibleElements(mActivity)
- & RECENTS_CLEAR_ALL_BUTTON) != 0;
+ & OVERVIEW_BUTTONS) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
new file mode 100644
index 0000000..6a37e2b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 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 android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+/**
+ * View for showing action buttons in Overview
+ */
+public class OverviewActionsView extends FrameLayout {
+
+ private final View mScreenshotButton;
+ private final View mShareButton;
+
+ /**
+ * Listener for taps on the various actions.
+ */
+ public interface Listener {
+ /** User has initiated the share actions. */
+ void onShare();
+
+ /** User has initiated the screenshot action. */
+ void onScreenshot();
+ }
+
+ public OverviewActionsView(Context context) {
+ this(context, null);
+ }
+
+ public OverviewActionsView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public OverviewActionsView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ LayoutInflater.from(context).inflate(R.layout.overview_actions, this, true);
+ mShareButton = findViewById(R.id.action_share);
+ mScreenshotButton = findViewById(R.id.action_screenshot);
+ }
+
+ /**
+ * Set listener for callbacks on action button taps.
+ *
+ * @param listener for callbacks, or {@code null} to clear the listener.
+ */
+ public void setListener(@Nullable OverviewActionsView.Listener listener) {
+ mShareButton.setOnClickListener(
+ listener == null ? null : view -> listener.onShare());
+ mScreenshotButton.setOnClickListener(
+ listener == null ? null : view -> listener.onScreenshot());
+ }
+}
diff --git a/quickstep/res/drawable/ic_screenshot.xml b/quickstep/res/drawable/ic_screenshot.xml
new file mode 100644
index 0000000..d97eae1
--- /dev/null
+++ b/quickstep/res/drawable/ic_screenshot.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2020 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_share.xml b/quickstep/res/drawable/ic_share.xml
new file mode 100644
index 0000000..ff4baec
--- /dev/null
+++ b/quickstep/res/drawable/ic_share.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2020 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,16c-0.79,0 -1.5,0.31 -2.03,0.81L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.53,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.48 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.05,4.12c-0.05,0.22 -0.09,0.45 -0.09,0.69 0,1.66 1.34,3 3,3s3,-1.34 3,-3 -1.34,-3 -3,-3zM18,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM6,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,20c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
+</vector>
diff --git a/quickstep/res/layout/overview_actions.xml b/quickstep/res/layout/overview_actions.xml
new file mode 100644
index 0000000..ad5efb6
--- /dev/null
+++ b/quickstep/res/layout/overview_actions.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:id="@+id/action_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" >
+ </Space>
+ <Button
+ android:id="@+id/action_screenshot"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/ic_screenshot"
+ android:text="@string/action_screenshot" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" >
+ </Space>
+
+ <Button
+ android:id="@+id/action_share"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/ic_share"
+ android:text="@string/action_share" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" >
+ </Space>
+ </LinearLayout>
+
+</merge>
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
new file mode 100644
index 0000000..328c20b
--- /dev/null
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2020 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.
+-->
+<com.android.quickstep.views.OverviewActionsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+
+</com.android.quickstep.views.OverviewActionsView>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 988c78d..8d42c4a 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -23,7 +23,7 @@
<dimen name="task_corner_radius_small">2dp</dimen>
<!-- Overrideable in overlay that provides the Overview Actions. -->
- <dimen name="overview_actions_height">0dp</dimen>
+ <dimen name="overview_actions_height">110dp</dimen>
<dimen name="recents_page_spacing">10dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index ab34f47..f0f8933 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -113,4 +113,10 @@
<string name="back_gesture_tutorial_action_button_label" translatable="false">Done</string>
<!-- Button text shown on a text button on the confirm screen. [CHAR LIMIT=14] -->
<string name="back_gesture_tutorial_action_text_button_label" translatable="false">Settings</string>
+
+ <!-- ******* Overview ******* -->
+ <!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
+ <string name="action_share">Share</string>
+ <!-- Label for a button that causes a screen shot of the current app to be taken. [CHAR_LIMIT=40] -->
+ <string name="action_screenshot">Screenshot</string>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index c8d7777..bf107fb 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -60,4 +60,13 @@
parent="TextAppearance.BackGestureTutorial.ButtonLabel">
<item name="android:textColor">@color/back_gesture_tutorial_primary_color</item>
</style>
+
+ <style name="OverviewActionButton"
+ parent="@android:style/Widget.DeviceDefault.Button.Borderless">
+ <item name="android:textColor">?attr/workspaceTextColor</item>
+ <item name="android:drawableTint">?attr/workspaceTextColor</item>
+ <item name="android:tint">?attr/workspaceTextColor</item>
+ <item name="android:drawablePadding">4dp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/xml/overview_file_provider_paths.xml b/quickstep/res/xml/overview_file_provider_paths.xml
new file mode 100644
index 0000000..14d7459
--- /dev/null
+++ b/quickstep/res/xml/overview_file_provider_paths.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <cache-path name="shared_images" path="/" />
+ <files-path name="log_files" path="/" />
+</paths>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 83c67bb..135daef 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -34,6 +34,7 @@
import android.content.IntentSender;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.view.View;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
@@ -80,6 +81,8 @@
private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
+ private View mActionsView;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -224,16 +227,22 @@
@Override
protected void setupViews() {
super.setupViews();
+ mActionsView = findViewById(R.id.overview_actions_view);
+
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
getOverviewPanel().bringToFront();
- if (getActionsView() != null) {
- getActionsView().bringToFront();
+ if (mActionsView != null) {
+ mActionsView.bringToFront();
}
}
}
+ public View getActionsView() {
+ return mActionsView;
+ }
+
@Override
protected void closeOpenViews(boolean animate) {
super.closeOpenViews(animate);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 03454f7..123c988 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -17,7 +17,6 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
@@ -34,11 +33,10 @@
import android.util.FloatProperty;
import android.view.View;
-import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
@@ -55,13 +53,11 @@
public abstract class BaseRecentsViewStateController<T extends View>
implements StateHandler {
protected final T mRecentsView;
- protected final Launcher mLauncher;
- protected final View mActionsView;
+ protected final BaseQuickstepLauncher mLauncher;
- public BaseRecentsViewStateController(@NonNull Launcher launcher) {
+ public BaseRecentsViewStateController(@NonNull BaseQuickstepLauncher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
- mActionsView = launcher.getActionsView();
}
@Override
@@ -69,19 +65,12 @@
ScaleAndTranslation scaleAndTranslation = state
.getOverviewScaleAndTranslation(mLauncher);
SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
- float translationX = scaleAndTranslation.translationX;
- if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- translationX = -translationX;
- }
- mRecentsView.setTranslationX(translationX);
+ mRecentsView.setTranslationX(scaleAndTranslation.translationX);
mRecentsView.setTranslationY(scaleAndTranslation.translationY);
+
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
- if (mActionsView != null) {
- mActionsView.setTranslationX(translationX);
- mActionsView.setAlpha(state.overviewUi ? 1f : 0);
- }
}
@Override
@@ -107,29 +96,18 @@
void setStateWithAnimationInternal(@NonNull final LauncherState toState,
@NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
- Interpolator scaleInterpolator = config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
- setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
- Interpolator translateXInterpolator = config.getInterpolator(
- ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
- Interpolator translateYInterpolator = config.getInterpolator(
- ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
- float translationX = scaleAndTranslation.translationX;
- if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- translationX = -translationX;
- }
- setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator);
+ setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale,
+ config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
+ setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, scaleAndTranslation.translationX,
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY,
- translateYInterpolator);
+ config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
+
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
- if (mActionsView != null) {
- setter.setFloat(mActionsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator);
- setter.setFloat(mActionsView, VIEW_ALPHA, toState.overviewUi ? 1 : 0,
- config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
- }
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
index 5836ebd..010694b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
@@ -54,6 +54,9 @@
@Override
public void addChangeListener(Context context, Runnable r) {
+ if (mListeners == null) {
+ initialize(context);
+ }
mListeners.add(r);
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index f7e40ca..631df4c 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -105,6 +105,10 @@
public static final int STATE_RECENTS_ANIMATION_ENDED =
getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
+ // Called when RecentsView stops scrolling and settles on a TaskView.
+ public static final int STATE_RECENTS_SCROLLING_FINISHED =
+ getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
+
// Needed to interact with the current activity
private final Intent mHomeIntent;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 0f98b32..783978d 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -19,11 +19,13 @@
import android.graphics.Rect;
import android.util.ArraySet;
+import android.util.Log;
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -89,6 +91,9 @@
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationStart");
+ }
RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
wallpaperTargets, homeContentInsets, minimizedHomeBounds);
mController = new RecentsAnimationController(animationController,
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 6902e37..b04a1ae 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -26,6 +26,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.testing.TestProtocol;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -52,6 +53,9 @@
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.NO_START_FROM_RECENTS, "startRecentsAnimation");
+ }
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
new file mode 100644
index 0000000..f5fbf28
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Insets;
+import android.graphics.Picture;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.util.Log;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.core.content.FileProvider;
+
+import com.android.launcher3.BuildConfig;
+import com.android.quickstep.SystemUiProxy;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.function.BiFunction;
+import java.util.function.Supplier;
+
+/**
+ * Utility class containing methods to help manage image actions such as sharing, cropping, and
+ * saving image.
+ */
+public class ImageActionUtils {
+
+ private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".overview.fileprovider";
+
+ /**
+ * Saves screenshot to location determine by SystemUiProxy
+ */
+ public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
+ Rect screenshotBounds,
+ Insets visibleInsets, int taskId) {
+ systemUiProxy.handleImageAsScreenshot(screenshot, screenshotBounds, visibleInsets, taskId);
+ }
+
+ /**
+ * Launch the activity to share image.
+ */
+ @UiThread
+ public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
+ Rect crop, Intent intent, String tag) {
+ if (bitmapSupplier.get() == null) {
+ Log.e(tag, "No snapshot available, not starting share.");
+ return;
+ }
+
+ UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
+ bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
+ tag));
+ }
+
+ /**
+ * Starts activity based on given intent created from image uri.
+ */
+ @WorkerThread
+ public static void persistBitmapAndStartActivity(Context context, Bitmap bitmap, Rect crop,
+ Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag) {
+ context.startActivities(
+ uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent));
+ }
+
+ /**
+ * Converts image bitmap to Uri by temporarily saving bitmap to cache, and creating Uri pointing
+ * to that location. Used to be able to share an image with another app.
+ *
+ * @param bitmap The whole bitmap to be shared.
+ * @param crop The section of the bitmap to be shared.
+ * @param context The application context, used to interact with file system.
+ * @param tag Tag used to log errors.
+ * @return Uri that points to the cropped version of desired bitmap to share.
+ */
+ @WorkerThread
+ public static Uri getImageUri(Bitmap bitmap, Rect crop, Context context, String tag) {
+ Bitmap croppedBitmap = cropBitmap(bitmap, crop);
+ int cropHash = crop == null ? 0 : crop.hashCode();
+ String baseName = "image_" + bitmap.hashCode() + "_" + cropHash + ".png";
+ File file = new File(context.getCacheDir(), baseName);
+
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
+ } catch (IOException e) {
+ Log.e(tag, "Error saving image", e);
+ }
+
+ return FileProvider.getUriForFile(context, AUTHORITY, file);
+ }
+
+ /**
+ * Crops the bitmap to the provided size and returns a software backed bitmap whenever possible.
+ *
+ * @param bitmap The bitmap to be cropped.
+ * @param crop The section of the bitmap in the crop.
+ * @return The cropped bitmap.
+ */
+ @WorkerThread
+ public static Bitmap cropBitmap(Bitmap bitmap, Rect crop) {
+ Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ if (crop == null) {
+ crop = new Rect(src);
+ }
+ if (crop.equals(src)) {
+ return bitmap;
+ } else {
+ if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
+ return Bitmap.createBitmap(bitmap, crop.left, crop.top, crop.width(),
+ crop.height());
+ }
+
+ // For hardware bitmaps, use the Picture API to directly create a software bitmap
+ Picture picture = new Picture();
+ Canvas canvas = picture.beginRecording(crop.width(), crop.height());
+ canvas.drawBitmap(bitmap, -crop.left, -crop.top, null);
+ picture.endRecording();
+ return Bitmap.createBitmap(picture, crop.width(), crop.height(),
+ Bitmap.Config.ARGB_8888);
+ }
+ }
+
+ /**
+ * Gets the intent used to share image.
+ */
+ @WorkerThread
+ private static Intent[] getShareIntentForImageUri(Uri uri, Intent intent) {
+ if (intent == null) {
+ intent = new Intent();
+ }
+ ClipData clipdata = new ClipData(new ClipDescription("content",
+ new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
+ new ClipData.Item(uri));
+ intent.setAction(Intent.ACTION_SEND)
+ .setComponent(null)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .setType("image/png")
+ .setFlags(FLAG_GRANT_READ_URI_PERMISSION)
+ .putExtra(Intent.EXTRA_STREAM, uri)
+ .setClipData(clipdata);
+ return new Intent[]{Intent.createChooser(intent, null).addFlags(FLAG_ACTIVITY_NEW_TASK)};
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index 8e4762d..5904fcd 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -88,7 +88,6 @@
*/
@Test
public void testPredictionExistsInAllApps() {
- mDevice.pressHome();
mLauncher.pressHome().switchToAllApps();
// Dispatch an update
diff --git a/res/drawable-v24/ic_block_shadow.xml b/res/drawable-v24/ic_block_shadow.xml
new file mode 100644
index 0000000..045fe8d
--- /dev/null
+++ b/res/drawable-v24/ic_block_shadow.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<com.android.launcher3.graphics.ShadowDrawable
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_block_no_shadow"
+ android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/drawable/ic_block.xml b/res/drawable/ic_block_no_shadow.xml
similarity index 100%
rename from res/drawable/ic_block.xml
rename to res/drawable/ic_block_no_shadow.xml
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
index 1367174..9c57ec1 100644
--- a/res/values/drawables.xml
+++ b/res/values/drawables.xml
@@ -17,4 +17,5 @@
<drawable name="ic_setup_shadow">@drawable/ic_setting</drawable>
<drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
<drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
+ <drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
</resources>
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 21a8fd4..c8e73ba 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -39,7 +39,6 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.widget.TextView;
@@ -109,8 +108,6 @@
private final int mDisplay;
private final CheckLongPressHelper mLongPressHelper;
- private final StylusEventHelper mStylusEventHelper;
- private final float mSlop;
private final boolean mLayoutHorizontal;
private final int mIconSize;
@@ -137,9 +134,6 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final boolean mIgnorePaddingTouch;
-
private IconLoadRequest mIconLoadRequest;
public BubbleTextView(Context context) {
@@ -153,7 +147,6 @@
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mActivity = ActivityContext.lookupContext(context);
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
@@ -166,23 +159,19 @@
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
- mIgnorePaddingTouch = true;
} else if (mDisplay == DISPLAY_ALL_APPS) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
- mIgnorePaddingTouch = true;
} else if (mDisplay == DISPLAY_FOLDER) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
- mIgnorePaddingTouch = true;
} else {
// widget_selection or shortcut_popup
defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
- mIgnorePaddingTouch = false;
}
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
@@ -192,7 +181,6 @@
a.recycle();
mLongPressHelper = new CheckLongPressHelper(this);
- mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mDotParams = new DotRenderer.DrawParams();
@@ -333,42 +321,21 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
// ignore events if they happen in padding area
- if (event.getAction() == MotionEvent.ACTION_DOWN && mIgnorePaddingTouch
+ if (event.getAction() == MotionEvent.ACTION_DOWN
&& (event.getY() < getPaddingTop()
|| event.getX() < getPaddingLeft()
|| event.getY() > getHeight() - getPaddingBottom()
|| event.getX() > getWidth() - getPaddingRight())) {
return false;
}
-
- // Call the superclass onTouchEvent first, because sometimes it changes the state to
- // isPressed() on an ACTION_UP
- boolean result = super.onTouchEvent(event);
-
- // Check for a stylus button press, if it occurs cancel any long press checks.
- if (mStylusEventHelper.onMotionEvent(event)) {
- mLongPressHelper.cancelLongPress();
- result = true;
+ if (isLongClickable()) {
+ super.onTouchEvent(event);
+ mLongPressHelper.onTouchEvent(event);
+ // Keep receiving the rest of the events
+ return true;
+ } else {
+ return super.onTouchEvent(event);
}
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- // If we're in a stylus button press, don't check for long press.
- if (!mStylusEventHelper.inStylusButtonPressed()) {
- mLongPressHelper.postCheckForLongPress();
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- mLongPressHelper.cancelLongPress();
- break;
- case MotionEvent.ACTION_MOVE:
- if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
- mLongPressHelper.cancelLongPress();
- }
- break;
- }
- return result;
}
void setStayPressed(boolean stayPressed) {
@@ -531,7 +498,6 @@
@Override
public void cancelLongPress() {
super.cancelLongPress();
-
mLongPressHelper.cancelLongPress();
}
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 639c173..ef353f9 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -16,46 +16,68 @@
package com.android.launcher3;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import com.android.launcher3.util.Thunk;
-
+/**
+ * Utility class to handle tripper long press on a view with custom timeout and stylus event
+ */
public class CheckLongPressHelper {
public static final float DEFAULT_LONG_PRESS_TIMEOUT_FACTOR = 0.75f;
- @Thunk View mView;
- @Thunk View.OnLongClickListener mListener;
- @Thunk boolean mHasPerformedLongPress;
- private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
- private CheckForLongPress mPendingCheckForLongPress;
+ private final View mView;
+ private final View.OnLongClickListener mListener;
+ private final float mSlop;
- class CheckForLongPress implements Runnable {
- public void run() {
- if ((mView.getParent() != null) && mView.hasWindowFocus()
- && !mHasPerformedLongPress) {
- boolean handled;
- if (mListener != null) {
- handled = mListener.onLongClick(mView);
- } else {
- handled = mView.performLongClick();
- }
- if (handled) {
- mView.setPressed(false);
- mHasPerformedLongPress = true;
- }
- }
- }
- }
+ private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
+
+ private boolean mHasPerformedLongPress;
+
+ private Runnable mPendingCheckForLongPress;
public CheckLongPressHelper(View v) {
- mView = v;
+ this(v, null);
}
public CheckLongPressHelper(View v, View.OnLongClickListener listener) {
mView = v;
mListener = listener;
+ mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
+ }
+
+ /**
+ * Handles the touch event on a view
+ *
+ * @see View#onTouchEvent(MotionEvent)
+ */
+ public void onTouchEvent(MotionEvent ev) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Just in case the previous long press hasn't been cleared, we make sure to
+ // start fresh on touch down.
+ cancelLongPress();
+
+ postCheckForLongPress();
+ if (isStylusButtonPressed(ev)) {
+ triggerLongPress();
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ cancelLongPress();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (!Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
+ cancelLongPress();
+ } else if (mPendingCheckForLongPress != null && isStylusButtonPressed(ev)) {
+ // Only trigger long press if it has not been cancelled before
+ triggerLongPress();
+ }
+ break;
+ }
}
/**
@@ -65,25 +87,64 @@
mLongPressTimeoutFactor = longPressTimeoutFactor;
}
- public void postCheckForLongPress() {
+ private void postCheckForLongPress() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
- mPendingCheckForLongPress = new CheckForLongPress();
+ mPendingCheckForLongPress = this::triggerLongPress;
}
mView.postDelayed(mPendingCheckForLongPress,
(long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
}
+ /**
+ * Cancels any pending long press
+ */
public void cancelLongPress() {
mHasPerformedLongPress = false;
+ clearCallbacks();
+ }
+
+ /**
+ * Returns true if long press has been performed in the current touch gesture
+ */
+ public boolean hasPerformedLongPress() {
+ return mHasPerformedLongPress;
+ }
+
+ private void triggerLongPress() {
+ if ((mView.getParent() != null) && mView.hasWindowFocus() && !mHasPerformedLongPress) {
+ boolean handled;
+ if (mListener != null) {
+ handled = mListener.onLongClick(mView);
+ } else {
+ handled = mView.performLongClick();
+ }
+ if (handled) {
+ mView.setPressed(false);
+ mHasPerformedLongPress = true;
+ }
+ clearCallbacks();
+ }
+ }
+
+ private void clearCallbacks() {
if (mPendingCheckForLongPress != null) {
mView.removeCallbacks(mPendingCheckForLongPress);
mPendingCheckForLongPress = null;
}
}
- public boolean hasPerformedLongPress() {
- return mHasPerformedLongPress;
+
+ /**
+ * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
+ * pressed.
+ *
+ * @param event The event to check.
+ * @return Whether a stylus button press occurred.
+ */
+ private static boolean isStylusButtonPressed(MotionEvent event) {
+ return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
+ && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 573aa07..5b9f676 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -271,7 +271,6 @@
// UI and state for the overview panel
private View mOverviewPanel;
- private View mActionsView;
@Thunk
boolean mWorkspaceLoading = true;
@@ -958,6 +957,8 @@
}
});
}
+
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStop");
}
@Override
@@ -971,6 +972,7 @@
mAppWidgetHost.setListenIfResumed(true);
TraceHelper.INSTANCE.endSection(traceToken);
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStart");
}
private void handleDeferredResume() {
@@ -1163,7 +1165,6 @@
mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mOverviewPanel = findViewById(R.id.overview_panel);
- mActionsView = findViewById(R.id.overview_actions_view);
mHotseat = findViewById(R.id.hotseat);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1410,10 +1411,6 @@
return (T) mOverviewPanel;
}
- public View getActionsView() {
- return mActionsView;
- }
-
public DropTargetBar getDropTargetBar() {
return mDropTargetBar;
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a699c32..8d20bd6 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -85,6 +85,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
+import java.util.function.Supplier;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -145,7 +146,7 @@
*/
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
- mOpenHelper = new DatabaseHelper(getContext());
+ mOpenHelper = DatabaseHelper.createDatabaseHelper(getContext());
if (RestoreDbTask.isPending(getContext())) {
if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
@@ -159,17 +160,17 @@
}
}
- private synchronized boolean updateCurrentOpenHelper() {
- final InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
- if (TextUtils.equals(idp.dbFile, mOpenHelper.getDatabaseName())) {
+ private synchronized boolean prepForMigration(String dbFile, String targetTableName,
+ Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
+ if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
return false;
}
- DatabaseHelper oldHelper = mOpenHelper;
- mOpenHelper = new DatabaseHelper(getContext());
- copyTable(oldHelper.getReadableDatabase(), Favorites.TABLE_NAME,
- mOpenHelper.getWritableDatabase(), Favorites.TMP_TABLE, getContext());
- oldHelper.close();
+ final DatabaseHelper helper = src.get();
+ mOpenHelper = dst.get();
+ copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
+ mOpenHelper.getWritableDatabase(), targetTableName, getContext());
+ helper.close();
return true;
}
@@ -425,7 +426,23 @@
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- updateCurrentOpenHelper());
+ prepForMigration(
+ InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
+ Favorites.TMP_TABLE,
+ () -> mOpenHelper,
+ () -> DatabaseHelper.createDatabaseHelper(getContext())));
+ return result;
+ }
+ }
+ case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
+ if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
+ Bundle result = new Bundle();
+ result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+ prepForMigration(
+ arg /* dbFile */,
+ Favorites.PREVIEW_TABLE_NAME,
+ () -> DatabaseHelper.createDatabaseHelper(getContext(), arg),
+ () -> mOpenHelper));
return result;
}
}
@@ -596,23 +613,31 @@
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
- DatabaseHelper(Context context) {
- this(context, MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
- context).dbFile : LauncherFiles.LAUNCHER_DB);
+ static DatabaseHelper createDatabaseHelper(Context context) {
+ return createDatabaseHelper(context, null);
+ }
+
+ static DatabaseHelper createDatabaseHelper(Context context, String dbName) {
+ if (dbName == null) {
+ dbName = MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
+ context).dbFile : LauncherFiles.LAUNCHER_DB;
+ }
+ DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName);
// Table creation sometimes fails silently, which leads to a crash loop.
// This way, we will try to create a table every time after crash, so the device
// would eventually be able to recover.
- if (!tableExists(getReadableDatabase(), Favorites.TABLE_NAME)) {
+ if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
// This operation is a no-op if the table already exists.
- addFavoritesTable(getWritableDatabase(), true);
+ databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
}
if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
- mBackupTableExists = tableExists(getReadableDatabase(),
- Favorites.BACKUP_TABLE_NAME);
+ databaseHelper.mBackupTableExists = tableExists(
+ databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
}
- initIds();
+ databaseHelper.initIds();
+ return databaseHelper;
}
/**
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index f516446..5262b18 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -326,10 +326,16 @@
public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
+ public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
+
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
- return cr.call(CONTENT_URI, method, null, null);
+ return call(cr, method, null);
+ }
+
+ public static Bundle call(ContentResolver cr, String method, String arg) {
+ return cr.call(CONTENT_URI, method, arg, null);
}
}
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 6ee82cd..504666a 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -73,7 +73,7 @@
public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
public static final int ALL_APPS_CONTENT = 1 << 4;
public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
- public static final int RECENTS_CLEAR_ALL_BUTTON = 1 << 6;
+ public static final int OVERVIEW_BUTTONS = 1 << 6;
/** Mask of all the items that are contained in the apps view. */
public static final int APPS_VIEW_ITEM_MASK =
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e38631d..7d7739e 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -50,6 +50,8 @@
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -133,6 +135,7 @@
protected int mActivePointerId = INVALID_POINTER;
protected boolean mIsPageInTransition = false;
+ private Runnable mOnPageTransitionEndCallback;
protected float mSpringOverScroll;
@@ -391,6 +394,22 @@
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
+ if (mOnPageTransitionEndCallback != null) {
+ mOnPageTransitionEndCallback.run();
+ mOnPageTransitionEndCallback = null;
+ }
+ }
+
+ /**
+ * Sets a callback to run once when the scrolling finishes. If there is currently
+ * no page in transition, then the callback is called immediately.
+ */
+ public void setOnPageTransitionEndCallback(@Nullable Runnable callback) {
+ if (mIsPageInTransition || callback == null) {
+ mOnPageTransitionEndCallback = callback;
+ } else {
+ callback.run();
+ }
}
protected int getUnboundedScroll() {
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 2430d5e..983c289 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -108,7 +108,7 @@
updateText(R.string.uninstall_drop_target_label);
} else if (action == DISMISS_PREDICTION) {
mHoverColor = Themes.getColorAccent(getContext());
- setDrawable(R.drawable.ic_block);
+ setDrawable(R.drawable.ic_block_shadow);
updateText(R.string.dismiss_prediction_label);
} else if (action == RECONFIGURE) {
mHoverColor = Themes.getColorAccent(getContext());
diff --git a/src/com/android/launcher3/SimpleOnStylusPressListener.java b/src/com/android/launcher3/SimpleOnStylusPressListener.java
deleted file mode 100644
index 6b97dce..0000000
--- a/src/com/android/launcher3/SimpleOnStylusPressListener.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.android.launcher3;
-
-import android.view.MotionEvent;
-import android.view.View;
-
-import com.android.launcher3.StylusEventHelper.StylusButtonListener;
-
-/**
- * Simple listener that performs a long click on the view after a stylus button press.
- */
-public class SimpleOnStylusPressListener implements StylusButtonListener {
- private View mView;
-
- public SimpleOnStylusPressListener(View view) {
- mView = view;
- }
-
- public boolean onPressed(MotionEvent event) {
- return mView.isLongClickable() && mView.performLongClick();
- }
-
- public boolean onReleased(MotionEvent event) {
- return false;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java
deleted file mode 100644
index d5fc0fa..0000000
--- a/src/com/android/launcher3/StylusEventHelper.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.android.launcher3;
-
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-/**
- * Helper for identifying when a stylus touches a view while the primary stylus button is pressed.
- * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}.
- */
-public class StylusEventHelper {
-
- /**
- * Implement this interface to receive callbacks for a stylus button press and release.
- */
- public interface StylusButtonListener {
- /**
- * Called when the stylus button is pressed.
- *
- * @param event The MotionEvent that the button press occurred for.
- * @return Whether the event was handled.
- */
- public boolean onPressed(MotionEvent event);
-
- /**
- * Called when the stylus button is released after a button press. This is also called if
- * the event is canceled or the stylus is lifted off the screen.
- *
- * @param event The MotionEvent the button release occurred for.
- * @return Whether the event was handled.
- */
- public boolean onReleased(MotionEvent event);
- }
-
- private boolean mIsButtonPressed;
- private View mView;
- private StylusButtonListener mListener;
- private final float mSlop;
-
- /**
- * Constructs a helper for listening to stylus button presses and releases. Ensure that {
- * {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on
- * the helper to correctly identify stylus events.
- *
- * @param listener The listener to call for stylus events.
- * @param view Optional view associated with the touch events.
- */
- public StylusEventHelper(StylusButtonListener listener, View view) {
- mListener = listener;
- mView = view;
- if (mView != null) {
- mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
- } else {
- mSlop = ViewConfiguration.getTouchSlop();
- }
- }
-
- public boolean onMotionEvent(MotionEvent event) {
- final boolean stylusButtonPressed = isStylusButtonPressed(event);
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mIsButtonPressed = stylusButtonPressed;
- if (mIsButtonPressed) {
- return mListener.onPressed(event);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) {
- return false;
- }
- if (!mIsButtonPressed && stylusButtonPressed) {
- mIsButtonPressed = true;
- return mListener.onPressed(event);
- } else if (mIsButtonPressed && !stylusButtonPressed) {
- mIsButtonPressed = false;
- return mListener.onReleased(event);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- if (mIsButtonPressed) {
- mIsButtonPressed = false;
- return mListener.onReleased(event);
- }
- break;
- }
- return false;
- }
-
- /**
- * Whether a stylus button press is occurring.
- */
- public boolean inStylusButtonPressed() {
- return mIsButtonPressed;
- }
-
- /**
- * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
- * pressed.
- *
- * @param event The event to check.
- * @return Whether a stylus button press occurred.
- */
- private static boolean isStylusButtonPressed(MotionEvent event) {
- return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
- && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY)
- == MotionEvent.BUTTON_SECONDARY);
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1cc8a98..92f5112 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -163,7 +163,7 @@
"Always use hardware optimization for folder animations.");
public static final BooleanFlag ENABLE_FIXED_ROTATION_TRANSFORM = getDebugFlag(
- FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, false,
+ FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, true,
"Launch/close apps without rotation animation. Fix Launcher to portrait");
public static void initialize(Context context) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index f0d18ae..8251d68 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -31,7 +31,6 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -52,8 +51,6 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
-import com.android.launcher3.SimpleOnStylusPressListener;
-import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
@@ -87,7 +84,6 @@
private FolderInfo mInfo;
private CheckLongPressHelper mLongPressHelper;
- private StylusEventHelper mStylusEventHelper;
static final int DROP_IN_ANIMATION_DURATION = 400;
@@ -110,8 +106,6 @@
boolean mAnimating = false;
- private float mSlop;
-
private Alarm mOpenAlarm = new Alarm();
private boolean mForceHideDot;
@@ -149,9 +143,7 @@
private void init() {
mLongPressHelper = new CheckLongPressHelper(this);
- mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mPreviewLayoutRule = new ClippedFolderIconLayoutRule();
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mPreviewItemManager = new PreviewItemManager(this);
mDotParams = new DotRenderer.DrawParams();
}
@@ -663,29 +655,10 @@
public boolean onTouchEvent(MotionEvent event) {
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
- boolean result = super.onTouchEvent(event);
-
- // Check for a stylus button press, if it occurs cancel any long press checks.
- if (mStylusEventHelper.onMotionEvent(event)) {
- mLongPressHelper.cancelLongPress();
- return true;
- }
-
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLongPressHelper.postCheckForLongPress();
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- mLongPressHelper.cancelLongPress();
- break;
- case MotionEvent.ACTION_MOVE:
- if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
- mLongPressHelper.cancelLongPress();
- }
- break;
- }
- return result;
+ super.onTouchEvent(event);
+ mLongPressHelper.onTouchEvent(event);
+ // Keep receiving the rest of the events
+ return true;
}
@Override
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 5bc6610..7d4eb0e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
-import static com.android.launcher3.model.GridSizeMigrationTask.needsToMigrate;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -397,7 +396,10 @@
private void populate() {
if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
- boolean needsToMigrate = needsToMigrate(mContext, mIdp);
+ boolean needsToMigrate =
+ MULTI_DB_GRID_MIRATION_ALGO.get()
+ ? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)
+ : GridSizeMigrationTask.needsToMigrate(mContext, mIdp);
boolean success = false;
if (needsToMigrate) {
success = MULTI_DB_GRID_MIRATION_ALGO.get()
diff --git a/src/com/android/launcher3/logging/DumpTargetWrapper.java b/src/com/android/launcher3/logging/DumpTargetWrapper.java
deleted file mode 100644
index 067bdfd..0000000
--- a/src/com/android/launcher3/logging/DumpTargetWrapper.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2017 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.logging;
-
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-
-import android.content.ComponentName;
-import android.os.Process;
-import android.text.TextUtils;
-
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.model.nano.LauncherDumpProto;
-import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
-import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
-import com.android.launcher3.model.nano.LauncherDumpProto.ItemType;
-import com.android.launcher3.model.nano.LauncherDumpProto.UserType;
-import com.android.launcher3.util.ShortcutUtil;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class can be used when proto definition doesn't support nesting.
- */
-public class DumpTargetWrapper {
- DumpTarget node;
- ArrayList<DumpTargetWrapper> children;
-
- public DumpTargetWrapper() {
- children = new ArrayList<>();
- }
-
- public DumpTargetWrapper(int containerType, int id) {
- this();
- node = newContainerTarget(containerType, id);
- }
-
- public DumpTargetWrapper(ItemInfo info) {
- this();
- node = newItemTarget(info);
- }
-
- public DumpTarget getDumpTarget() {
- return node;
- }
-
- public void add(DumpTargetWrapper child) {
- children.add(child);
- }
-
- public List<DumpTarget> getFlattenedList() {
- ArrayList<DumpTarget> list = new ArrayList<>();
- list.add(node);
- if (!children.isEmpty()) {
- for(DumpTargetWrapper t: children) {
- list.addAll(t.getFlattenedList());
- }
- list.add(node); // add a delimiter empty object
- }
- return list;
- }
- public DumpTarget newItemTarget(ItemInfo info) {
- DumpTarget dt = new DumpTarget();
- dt.type = DumpTarget.Type.ITEM;
- if (info == null) {
- return dt;
- }
- switch (info.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- dt.itemType = ItemType.APP_ICON;
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- dt.itemType = ItemType.WIDGET;
- break;
- case ITEM_TYPE_DEEP_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- dt.itemType = ItemType.SHORTCUT;
- break;
- default:
- dt.itemType = ItemType.UNKNOWN_ITEMTYPE;
- break;
- }
- return dt;
- }
-
- public DumpTarget newContainerTarget(int type, int id) {
- DumpTarget dt = new DumpTarget();
- dt.type = DumpTarget.Type.CONTAINER;
- dt.containerType = type;
- dt.pageId = id;
- return dt;
- }
-
- public static String getDumpTargetStr(DumpTarget t) {
- if (t == null){
- return "";
- }
- switch (t.type) {
- case LauncherDumpProto.DumpTarget.Type.ITEM:
- return getItemStr(t);
- case LauncherDumpProto.DumpTarget.Type.CONTAINER:
- String str = LoggerUtils.getFieldName(t.containerType, ContainerType.class);
- if (t.containerType == ContainerType.WORKSPACE) {
- str += " id=" + t.pageId;
- } else if (t.containerType == ContainerType.FOLDER) {
- str += " grid(" + t.gridX + "," + t.gridY+ ")";
- }
- return str;
- default:
- return "UNKNOWN TARGET TYPE";
- }
- }
-
- private static String getItemStr(DumpTarget t) {
- if (t == null) {
- return "";
- }
- String typeStr = LoggerUtils.getFieldName(t.itemType, ItemType.class);
- if (!TextUtils.isEmpty(t.packageName)) {
- typeStr += ", package=" + t.packageName;
- }
- if (!TextUtils.isEmpty(t.component)) {
- typeStr += ", component=" + t.component;
- }
- return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
- + "), pageIdx=" + t.pageId + " user=" + t.userType;
- }
-
- public DumpTarget writeToDumpTarget(ItemInfo info) {
- if (info == null) {
- return node;
- }
- if (ShortcutUtil.isDeepShortcut(info)) {
- node.component = ((WorkspaceItemInfo) info).getDeepShortcutId();
- } else {
- ComponentName cmp = info.getTargetComponent();
- node.component = cmp == null ? "" : cmp.flattenToString();
- }
- node.packageName = info.getTargetComponent() == null? "":
- info.getTargetComponent().getPackageName();
- if (info instanceof LauncherAppWidgetInfo) {
- node.component = ((LauncherAppWidgetInfo) info).providerName.flattenToString();
- node.packageName = ((LauncherAppWidgetInfo) info).providerName.getPackageName();
- }
-
- node.gridX = info.cellX;
- node.gridY = info.cellY;
- node.spanX = info.spanX;
- node.spanY = info.spanY;
- node.userType = (info.user.equals(Process.myUserHandle()))? UserType.DEFAULT : UserType.WORK;
- return node;
- }
-}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index fdfcef1..206688a 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -36,10 +36,6 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.DumpTargetWrapper;
-import com.android.launcher3.model.nano.LauncherDumpProto;
-import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
-import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.ComponentKey;
@@ -50,11 +46,7 @@
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.google.protobuf.nano.MessageNano;
-
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -150,10 +142,6 @@
public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
String[] args) {
- if (Arrays.asList(args).contains("--proto")) {
- dumpProto(prefix, fd, writer, args);
- return;
- }
writer.println(prefix + "Data Model:");
writer.println(prefix + " ---- workspace items ");
for (int i = 0; i < workspaceItems.size(); i++) {
@@ -181,89 +169,6 @@
}
}
- private synchronized void dumpProto(String prefix, FileDescriptor fd, PrintWriter writer,
- String[] args) {
-
- // Add top parent nodes. (L1)
- DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
- IntSparseArrayMap<DumpTargetWrapper> workspaces = new IntSparseArrayMap<>();
- IntArray workspaceScreens = collectWorkspaceScreens();
- for (int i = 0; i < workspaceScreens.size(); i++) {
- workspaces.put(workspaceScreens.get(i),
- new DumpTargetWrapper(ContainerType.WORKSPACE, i));
- }
- DumpTargetWrapper dtw;
- // Add non leaf / non top nodes (L2)
- for (int i = 0; i < folders.size(); i++) {
- FolderInfo fInfo = folders.valueAt(i);
- dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
- dtw.writeToDumpTarget(fInfo);
- for(WorkspaceItemInfo sInfo: fInfo.contents) {
- DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
- child.writeToDumpTarget(sInfo);
- dtw.add(child);
- }
- if (fInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- hotseat.add(dtw);
- } else if (fInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- workspaces.get(fInfo.screenId).add(dtw);
- }
- }
- // Add leaf nodes (L3): *Info
- for (int i = 0; i < workspaceItems.size(); i++) {
- ItemInfo info = workspaceItems.get(i);
- if (info instanceof FolderInfo) {
- continue;
- }
- dtw = new DumpTargetWrapper(info);
- dtw.writeToDumpTarget(info);
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- hotseat.add(dtw);
- } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- workspaces.get(info.screenId).add(dtw);
- }
- }
- for (int i = 0; i < appWidgets.size(); i++) {
- ItemInfo info = appWidgets.get(i);
- dtw = new DumpTargetWrapper(info);
- dtw.writeToDumpTarget(info);
- if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- hotseat.add(dtw);
- } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- workspaces.get(info.screenId).add(dtw);
- }
- }
-
-
- // Traverse target wrapper
- ArrayList<DumpTarget> targetList = new ArrayList<>();
- targetList.addAll(hotseat.getFlattenedList());
- for (int i = 0; i < workspaces.size(); i++) {
- targetList.addAll(workspaces.valueAt(i).getFlattenedList());
- }
-
- if (Arrays.asList(args).contains("--debug")) {
- for (int i = 0; i < targetList.size(); i++) {
- writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
- }
- return;
- } else {
- LauncherDumpProto.LauncherImpression proto = new LauncherDumpProto.LauncherImpression();
- proto.targets = new DumpTarget[targetList.size()];
- for (int i = 0; i < targetList.size(); i++) {
- proto.targets[i] = targetList.get(i);
- }
- FileOutputStream fos = new FileOutputStream(fd);
- try {
-
- fos.write(MessageNano.toByteArray(proto));
- Log.d(TAG, MessageNano.toByteArray(proto).length + "Bytes");
- } catch (IOException e) {
- Log.e(TAG, "Exception writing dumpsys --proto", e);
- }
- }
- }
-
public synchronized void removeItem(Context context, ItemInfo... items) {
removeItem(context, Arrays.asList(items));
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 0bdccfa..79ae4c5 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -123,8 +123,16 @@
}
/**
- * Run the migration algorithm if needed. For preview, we provide the intended idp because it
- * has not been changed. If idp is null, we read it from the context, for actual grid migration.
+ * When migrating the grid for preview, we copy the table
+ * {@link LauncherSettings.Favorites.TABLE_NAME} into
+ * {@link LauncherSettings.Favorites.PREVIEW_TABLE_NAME}, run grid size migration from the
+ * former to the later, then use the later table for preview.
+ *
+ * Similarly when doing the actual grid migration, the former grid option's table
+ * {@link LauncherSettings.Favorites.TABLE_NAME} is copied into the new grid option's
+ * {@link LauncherSettings.Favorites.TMP_TABLE}, we then run the grid size migration algorithm
+ * to migrate the later to the former, and load the workspace from the default
+ * {@link LauncherSettings.Favorites.TABLE_NAME}.
*
* @return false if the migration failed.
*/
@@ -151,7 +159,14 @@
HashSet<String> validPackages = getValidPackages(context);
int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
- if (!LauncherSettings.Settings.call(
+ if (migrateForPreview) {
+ if (!LauncherSettings.Settings.call(
+ context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW, idp.dbFile).getBoolean(
+ LauncherSettings.Settings.EXTRA_VALUE)) {
+ return false;
+ }
+ } else if (!LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
@@ -164,9 +179,13 @@
LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
LauncherSettings.Settings.EXTRA_VALUE)) {
- DbReader srcReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TMP_TABLE,
+ DbReader srcReader = new DbReader(t.getDb(),
+ migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
+ : LauncherSettings.Favorites.TMP_TABLE,
context, validPackages, srcHotseatCount);
- DbReader destReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TABLE_NAME,
+ DbReader destReader = new DbReader(t.getDb(),
+ migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
+ : LauncherSettings.Favorites.TABLE_NAME,
context, validPackages, idp.numHotseatIcons);
Point targetSize = new Point(idp.numColumns, idp.numRows);
@@ -174,7 +193,9 @@
srcReader, destReader, idp.numHotseatIcons, targetSize);
task.migrate();
- dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
+ if (!migrateForPreview) {
+ dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
+ }
t.commit();
return true;
@@ -186,11 +207,13 @@
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
- // Save current configuration, so that the migration does not run again.
- prefs.edit()
- .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
- .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
- .apply();
+ if (!migrateForPreview) {
+ // Save current configuration, so that the migration does not run again.
+ prefs.edit()
+ .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
+ .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+ .apply();
+ }
}
}
@@ -202,7 +225,7 @@
// Migrate hotseat
HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
- mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
+ mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
hotseatSolution.find();
// Sort the items by the reading order.
@@ -215,7 +238,7 @@
}
List<DbEntry> entries = mDestReader.loadWorkspaceEntries(screenId);
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
- mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
+ mDestReader, mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
workspaceSolution.find();
if (mWorkspaceDiff.isEmpty()) {
break;
@@ -225,7 +248,8 @@
int screenId = mDestReader.mLastScreenId + 1;
while (!mWorkspaceDiff.isEmpty()) {
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
- mContext, new ArrayList<>(), screenId, mTrgX, mTrgY, mWorkspaceDiff);
+ mDestReader, mContext, new ArrayList<>(), screenId, mTrgX, mTrgY,
+ mWorkspaceDiff);
workspaceSolution.find();
screenId++;
}
@@ -246,7 +270,8 @@
}
private static void insertEntryInDb(SQLiteDatabase db, Context context,
- ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry) {
+ ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry, String srcTableName,
+ String destTableName) {
int id = -1;
switch (entry.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
@@ -283,8 +308,8 @@
return;
}
- Cursor c = db.query(LauncherSettings.Favorites.TMP_TABLE, null,
- LauncherSettings.Favorites._ID + " = '" + id + "'", null, null, null, null);
+ Cursor c = db.query(srcTableName, null, LauncherSettings.Favorites._ID + " = '" + id + "'",
+ null, null, null, null);
while (c.moveToNext()) {
ContentValues values = new ContentValues();
@@ -294,14 +319,14 @@
LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
LauncherSettings.Settings.EXTRA_VALUE));
- db.insert(LauncherSettings.Favorites.TABLE_NAME, null, values);
+ db.insert(destTableName, null, values);
}
c.close();
}
- private static void removeEntryFromDb(SQLiteDatabase db, IntArray entryId) {
- db.delete(LauncherSettings.Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, entryId), null);
+ private static void removeEntryFromDb(SQLiteDatabase db, String tableName, IntArray entryId) {
+ db.delete(tableName,
+ Utilities.createDbSelectionQuery(LauncherSettings.Favorites._ID, entryId), null);
}
private static HashSet<String> getValidPackages(Context context) {
@@ -325,6 +350,7 @@
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
+ private final DbReader mDestReader;
private final Context mContext;
private final GridOccupancy mOccupied;
private final int mScreenId;
@@ -335,11 +361,12 @@
private int mNextStartX;
private int mNextStartY;
- GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
- List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
+ GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
+ Context context, List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
int trgY, List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
+ mDestReader = destReader;
mContext = context;
mOccupied = new GridOccupancy(trgX, trgY);
mScreenId = screenId;
@@ -362,7 +389,8 @@
continue;
}
if (findPlacement(entry)) {
- insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry);
+ insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry,
+ mSrcReader.mTableName, mDestReader.mTableName);
iterator.remove();
}
}
@@ -397,14 +425,17 @@
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
+ private final DbReader mDestReader;
private final Context mContext;
private final HotseatOccupancy mOccupied;
private final List<DbEntry> mItemsToPlace;
- HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
- int hotseatSize, List<DbEntry> placedHotseatItems, List<DbEntry> itemsToPlace) {
+ HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
+ Context context, int hotseatSize, List<DbEntry> placedHotseatItems,
+ List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
+ mDestReader = destReader;
mContext = context;
mOccupied = new HotseatOccupancy(hotseatSize);
for (DbEntry entry : placedHotseatItems) {
@@ -422,7 +453,8 @@
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
- insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry);
+ insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry,
+ mSrcReader.mTableName, mDestReader.mTableName);
mOccupied.markCells(entry, true);
}
}
@@ -519,7 +551,7 @@
}
mHotseatEntries.add(entry);
}
- removeEntryFromDb(mDb, entriesToRemove);
+ removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
return mHotseatEntries;
}
@@ -639,7 +671,7 @@
}
mWorkspaceEntries.add(entry);
}
- removeEntryFromDb(mDb, entriesToRemove);
+ removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
return mWorkspaceEntries;
}
@@ -657,7 +689,7 @@
total++;
entry.mFolderItems.add(intent);
} catch (Exception e) {
- removeEntryFromDb(mDb, IntArray.wrap(c.getInt(0)));
+ removeEntryFromDb(mDb, mTableName, IntArray.wrap(c.getInt(0)));
}
}
c.close();
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index dacea84..7e05a5a 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -129,6 +129,7 @@
toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
toDb.execSQL(
"INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable);
+ toDb.execSQL("DETACH DATABASE 'from_db'");
} else {
toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable);
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 8c8aa9b..fae0fe2 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -134,7 +134,7 @@
*/
private void updateForcedRotation(boolean setValueFromPrefs) {
boolean isForcedRotation = mFeatureFlagsPrefs
- .getBoolean(FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, false)
+ .getBoolean(FLAG_ENABLE_FIXED_ROTATION_TRANSFORM, true)
&& !getAllowRotationDefaultValue();
if (mForcedRotation == isForcedRotation) {
return;
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index dd97b10..9f20df6 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -35,6 +35,7 @@
public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static final String SEQUENCE_MAIN = "Main";
public static final String SEQUENCE_TIS = "TIS";
+ public static final String SEQUENCE_PILFER = "Pilfer";
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
@@ -96,4 +97,5 @@
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String APP_NOT_DISABLED = "b/139891609";
public static final String NO_SCROLL_END_WIDGETS = "b/152354290";
+ public static final String NO_START_FROM_RECENTS = "b/152658211";
}
diff --git a/src/com/android/launcher3/util/OverScroller.java b/src/com/android/launcher3/util/OverScroller.java
index 3c398b8..34efb12 100644
--- a/src/com/android/launcher3/util/OverScroller.java
+++ b/src/com/android/launcher3/util/OverScroller.java
@@ -165,6 +165,9 @@
/**
* Returns how long the scroll event will take, in milliseconds.
*
+ * Note that if mScroller.mState == SPRING, this duration is ignored, so can only
+ * serve as an estimate for how long the spring-controlled scroll will take.
+ *
* @return The duration of the scroll in milliseconds.
*/
public final int getDuration() {
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 868c91d..2fc3eaf 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -33,6 +33,7 @@
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
@@ -48,6 +49,7 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -273,6 +275,9 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "BaseDragLayer: " + ev);
+ }
switch (ev.getAction()) {
case ACTION_DOWN: {
mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index c1310e3..78acc34 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -27,7 +27,6 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -41,8 +40,6 @@
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
-import com.android.launcher3.SimpleOnStylusPressListener;
-import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DraggableView;
@@ -66,14 +63,11 @@
protected final LayoutInflater mInflater;
private final CheckLongPressHelper mLongPressHelper;
- private final StylusEventHelper mStylusEventHelper;
protected final Launcher mLauncher;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mReinflateOnConfigChange;
- private float mSlop;
-
private boolean mIsScrollable;
private boolean mIsAttachedToWindow;
private boolean mIsAutoAdvanceRegistered;
@@ -93,7 +87,6 @@
super(context);
mLauncher = Launcher.getLauncher(context);
mLongPressHelper = new CheckLongPressHelper(this, this);
- mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
@@ -157,68 +150,19 @@
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Just in case the previous long press hasn't been cleared, we make sure to start fresh
- // on touch down.
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mLongPressHelper.cancelLongPress();
- }
-
- // Consume any touch events for ourselves after longpress is triggered
- if (mLongPressHelper.hasPerformedLongPress()) {
- mLongPressHelper.cancelLongPress();
- return true;
- }
-
- // Watch for longpress or stylus button press events at this level to
- // make sure users can always pick up this widget
- if (mStylusEventHelper.onMotionEvent(ev)) {
- mLongPressHelper.cancelLongPress();
- return true;
- }
-
- switch (ev.getAction()) {
- case MotionEvent.ACTION_DOWN: {
- DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
-
- if (mIsScrollable) {
- dragLayer.requestDisallowInterceptTouchEvent(true);
- }
- if (!mStylusEventHelper.inStylusButtonPressed()) {
- mLongPressHelper.postCheckForLongPress();
- }
- dragLayer.setTouchCompleteListener(this);
- break;
+ DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+ if (mIsScrollable) {
+ dragLayer.requestDisallowInterceptTouchEvent(true);
}
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mLongPressHelper.cancelLongPress();
- break;
- case MotionEvent.ACTION_MOVE:
- if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
- mLongPressHelper.cancelLongPress();
- }
- break;
+ dragLayer.setTouchCompleteListener(this);
}
-
- // Otherwise continue letting touch events fall through to children
- return false;
+ mLongPressHelper.onTouchEvent(ev);
+ return mLongPressHelper.hasPerformedLongPress();
}
public boolean onTouchEvent(MotionEvent ev) {
- // If the widget does not handle touch, then cancel
- // long press when we release the touch
- switch (ev.getAction()) {
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mLongPressHelper.cancelLongPress();
- break;
- case MotionEvent.ACTION_MOVE:
- if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
- mLongPressHelper.cancelLongPress();
- }
- break;
- }
+ mLongPressHelper.onTouchEvent(ev);
// We want to keep receiving though events to be able to cancel long press on ACTION_UP
return true;
}
@@ -226,7 +170,6 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mIsAttachedToWindow = true;
checkIfAutoAdvance();
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index f055adf..4a0b4ef 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -31,10 +31,9 @@
import android.widget.TextView;
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
-import com.android.launcher3.SimpleOnStylusPressListener;
-import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.model.WidgetItem;
@@ -71,7 +70,6 @@
protected WidgetItem mItem;
private WidgetPreviewLoader mWidgetPreviewLoader;
- private StylusEventHelper mStylusEventHelper;
protected CancellationSignal mActiveRequest;
private boolean mAnimatePreview = true;
@@ -80,7 +78,8 @@
private Bitmap mDeferredBitmap;
protected final BaseActivity mActivity;
- protected DeviceProfile mDeviceProfile;
+ protected final DeviceProfile mDeviceProfile;
+ private final CheckLongPressHelper mLongPressHelper;
public WidgetCell(Context context) {
this(context, null);
@@ -95,8 +94,9 @@
mActivity = BaseActivity.fromContext(context);
mDeviceProfile = mActivity.getDeviceProfile();
- mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
+ mLongPressHelper = new CheckLongPressHelper(this);
+ mLongPressHelper.setLongPressTimeoutFactor(1);
setContainerWidth();
setWillNotDraw(false);
setClipToPadding(false);
@@ -210,11 +210,15 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
- boolean handled = super.onTouchEvent(ev);
- if (mStylusEventHelper.onMotionEvent(ev)) {
- return true;
- }
- return handled;
+ super.onTouchEvent(ev);
+ mLongPressHelper.onTouchEvent(ev);
+ return true;
+ }
+
+ @Override
+ public void cancelLongPress() {
+ super.cancelLongPress();
+ mLongPressHelper.cancelLongPress();
}
/**
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index b07a4f4..aaebedd 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -38,8 +39,10 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
@@ -68,6 +71,14 @@
}
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "WidgetsFullSheet: " + ev);
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
public WidgetsFullSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index f8bbf21..de1ada4 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -314,7 +314,7 @@
switchToAllApps();
allApps.freeze();
try {
- allApps.getAppIcon(APP_NAME).dragToWorkspace();
+ allApps.getAppIcon(APP_NAME).dragToWorkspace(false);
mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName());
} finally {
allApps.unfreeze();
@@ -342,7 +342,7 @@
getMenuItem(0);
final String shortcutName = menuItem.getText();
- menuItem.dragToWorkspace();
+ menuItem.dragToWorkspace(false);
mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
} finally {
allApps.unfreeze();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index de9757f..d93915c 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -94,7 +94,7 @@
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
widgets.
getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())).
- dragToWorkspace();
+ dragToWorkspace(true);
// Widget id for which the config activity was opened
mWidgetId = monitor.getWidgetId();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index f9d1d93..788e041 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -57,7 +57,7 @@
getWorkspace().
openAllWidgets().
getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())).
- dragToWorkspace();
+ dragToWorkspace(false);
assertTrue(mActivityMonitor.itemExists(
(info, view) -> info instanceof LauncherAppWidgetInfo &&
@@ -83,7 +83,7 @@
mDevice.pressHome();
mLauncher.getWorkspace().openAllWidgets()
.getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
- .dragToWorkspace();
+ .dragToWorkspace(false);
mLauncher.getWorkspace().getWorkspaceAppIcon("Shortcut")
.launch(getAppPackageName());
}
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 793af48..a3c70ec 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -45,6 +45,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.tapl.Widget;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
@@ -267,8 +268,10 @@
}
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
+ final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT);
+ if (widget == null) mLauncher.dumpViewHierarchy(); // b/152645831
assertTrue("Widget is not present",
- mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
+ widget != null);
}
private void verifyPendingWidgetPresent() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 2acab97..80b8e89 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -25,7 +25,6 @@
import android.view.MotionEvent;
import androidx.annotation.NonNull;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
import com.android.launcher3.testing.TestProtocol;
@@ -72,6 +71,7 @@
}
protected void goToOverviewUnchecked() {
+ final boolean launcherWasVisible = mLauncher.isLauncherVisible();
switch (mLauncher.getNavigationModel()) {
case ZERO_BUTTON: {
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
@@ -137,6 +137,15 @@
OVERVIEW_STATE_ORDINAL);
break;
}
+ expectSwitchToOverviewEvents();
+
+ if (!launcherWasVisible) {
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START_ACTIVITY);
+ }
+ }
+
+ private void expectSwitchToOverviewEvents() {
}
/**
@@ -157,6 +166,7 @@
}
protected void quickSwitchToPreviousApp(int expectedState) {
+ final boolean launcherWasVisible = mLauncher.isLauncherVisible();
boolean transposeInLandscape = false;
switch (mLauncher.getNavigationModel()) {
case TWO_BUTTON:
@@ -180,15 +190,17 @@
endX = startX;
endY = 0;
}
- final boolean launcherIsVisible =
- mLauncher.hasLauncherObject(By.textStartsWith(""));
final boolean isZeroButton = mLauncher.getNavigationModel()
== LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
+ if (!launcherWasVisible) {
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN,
+ LauncherInstrumentation.EVENT_START_ACTIVITY);
+ }
mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
- launcherIsVisible && isZeroButton
+ launcherWasVisible && isZeroButton
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
- : LauncherInstrumentation.GestureScope.OUTSIDE
- );
+ : LauncherInstrumentation.GestureScope.OUTSIDE);
break;
}
@@ -196,6 +208,11 @@
// Double press the recents button.
UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+ if (!launcherWasVisible) {
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN,
+ LauncherInstrumentation.EVENT_START_ACTIVITY);
+ }
mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
mLauncher.getOverview();
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
@@ -203,6 +220,8 @@
break;
}
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
}
protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index b20384e..d1a1254 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -25,6 +25,8 @@
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.testing.TestProtocol;
+
/**
* Ancestor for AppIcon and AppMenuItem.
*/
@@ -62,6 +64,8 @@
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
() -> "Launching an app didn't open a new window: " + mObject.getText());
expectActivityStartEvents();
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
mLauncher.assertTrue(
"App didn't start: " + selector,
@@ -72,8 +76,9 @@
/**
* Drags an object to the center of homescreen.
+ * @param startsActivity whether it's expected to start an activity.
*/
- public void dragToWorkspace() {
+ public void dragToWorkspace(boolean startsActivity) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
final Point launchableCenter = getObject().getVisibleCenter();
final Point displaySize = mLauncher.getRealDisplaySize();
@@ -86,7 +91,8 @@
? launchableCenter.x - width / 2
: launchableCenter.x + width / 2,
displaySize.y / 2),
- getLongPressIndicator());
+ getLongPressIndicator(),
+ startsActivity);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index d894843..d171a69 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -97,6 +97,8 @@
private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
+ static final Pattern EVENT_START_ACTIVITY = Pattern.compile("Activity\\.onStart");
+ static final Pattern EVENT_STOP_ACTIVITY = Pattern.compile("Activity\\.onStop");
static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
@@ -316,7 +318,7 @@
};
}
- private void dumpViewHierarchy() {
+ public void dumpViewHierarchy() {
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
mDevice.dumpWindowHierarchy(stream);
@@ -637,6 +639,7 @@
// otherwise waitForIdle may return immediately in case when there was a big enough
// pause in accessibility events prior to pressing Home.
final String action;
+ final boolean launcherWasVisible = isLauncherVisible();
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
checkForAnomaly();
@@ -665,12 +668,18 @@
displaySize.x / 2, displaySize.y - 1,
displaySize.x / 2, 0,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL,
- hasLauncherObject(By.textStartsWith(""))
+ launcherWasVisible
? GestureScope.INSIDE_TO_OUTSIDE
: GestureScope.OUTSIDE);
}
+ if (!launcherWasVisible) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_START_ACTIVITY);
+ }
}
} else {
+ if (!launcherWasVisible) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_START_ACTIVITY);
+ }
log("Hierarchy before clicking home:");
dumpViewHierarchy();
log(action = "clicking home button from " + getVisibleStateMessage());
@@ -681,6 +690,7 @@
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
}
+
runToState(
waitForSystemUiObject("home")::click,
NORMAL_STATE_ORDINAL,
@@ -697,6 +707,11 @@
}
}
+ boolean isLauncherVisible() {
+ mDevice.waitForIdle();
+ return hasLauncherObject(By.textStartsWith(""));
+ }
+
/**
* Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the
* launcher is not in that state.
@@ -1116,7 +1131,7 @@
break;
case MotionEvent.ACTION_UP:
if (notLauncher3 && gestureScope != GestureScope.INSIDE) {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
+ expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS);
}
if (gestureScope != GestureScope.OUTSIDE) {
expectEvent(TestProtocol.SEQUENCE_MAIN, gestureScope == GestureScope.INSIDE
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 0fc88ee..49901ea 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -149,23 +149,24 @@
finishSync(waitForExpectedCountMs);
final StringBuilder sb = new StringBuilder();
+ boolean hasMismatches = false;
for (Map.Entry<String, List<Pattern>> expectedEvents : mExpectedEvents.entrySet()) {
String sequence = expectedEvents.getKey();
List<String> actual = new ArrayList<>(mEvents.getNonNull(sequence));
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
- if (mismatchPosition != -1) {
- formatSequenceWithMismatch(
- sb,
- sequence,
- expectedEvents.getValue(),
- actual,
- mismatchPosition);
- }
+ hasMismatches = hasMismatches || mismatchPosition != -1;
+ formatSequenceWithMismatch(
+ sb,
+ sequence,
+ expectedEvents.getValue(),
+ actual,
+ mismatchPosition);
}
// Check for unexpected event sequences in the actual data.
for (String actualNamedSequence : mEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
+ hasMismatches = true;
formatSequenceWithMismatch(
sb,
actualNamedSequence,
@@ -175,7 +176,7 @@
}
}
- return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
+ return hasMismatches ? "mismatching events: " + sb.toString() : null;
}
// If the list of actual events matches the list of expected events, returns -1, otherwise
@@ -199,10 +200,11 @@
List<Pattern> expected,
List<String> actualEvents,
int mismatchPosition) {
- sb.append("\n>> Sequence " + sequenceName);
- sb.append("\n Expected:");
+ sb.append("\n>> SEQUENCE " + sequenceName + " - "
+ + (mismatchPosition == -1 ? "MATCH" : "MISMATCH"));
+ sb.append("\n EXPECTED:");
formatEventListWithMismatch(sb, expected, mismatchPosition);
- sb.append("\n Actual:");
+ sb.append("\n ACTUAL:");
formatEventListWithMismatch(sb, actualEvents, mismatchPosition);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java
index c2f701b..b8e6c0e 100644
--- a/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/OptionsPopupMenuItem.java
@@ -15,11 +15,15 @@
*/
package com.android.launcher3.tapl;
+import android.os.Build;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
+import com.android.launcher3.testing.TestProtocol;
+
public class OptionsPopupMenuItem {
private final LauncherInstrumentation mLauncher;
@@ -39,6 +43,12 @@
LauncherInstrumentation.log("OptionsPopupMenuItem before click "
+ mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
mLauncher.clickLauncherObject(mObject);
+ if (!Build.MODEL.contains("Cuttlefish") ||
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q &&
+ !"R".equals(Build.VERSION.CODENAME)) {
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
+ }
mLauncher.assertTrue(
"App didn't start: " + By.pkg(expectedPackageName),
mLauncher.getDevice().wait(Until.hasObject(By.pkg(expectedPackageName)),
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index f955cf2..5c51782 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -79,6 +79,8 @@
() -> "Launching task didn't open a new window: "
+ mTask.getParent().getContentDescription());
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
+ mLauncher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
}
return new Background(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 084138c..a14d2f0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -88,47 +88,50 @@
}
public Widget getWidget(String labelText) {
- final UiObject2 widgetsContainer = verifyActiveContainer();
- final Point displaySize = mLauncher.getRealDisplaySize();
- final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "getting widget " + labelText + " in widgets list")) {
+ final UiObject2 widgetsContainer = verifyActiveContainer();
+ final Point displaySize = mLauncher.getRealDisplaySize();
+ final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
- int i = 0;
- for (; ; ) {
- final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
- widgetsContainer, "widgets_scroll_container");
- mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
- for (UiObject2 cell : cells) {
- final UiObject2 label = cell.findObject(labelSelector);
- if (label == null) continue;
+ int i = 0;
+ for (; ; ) {
+ final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
+ widgetsContainer, "widgets_scroll_container");
+ mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
+ for (UiObject2 cell : cells) {
+ final UiObject2 label = cell.findObject(labelSelector);
+ if (label == null) continue;
- final UiObject2 widget = label.getParent().getParent();
- mLauncher.assertEquals(
- "View is not WidgetCell",
- "com.android.launcher3.widget.WidgetCell",
- widget.getClassName());
+ final UiObject2 widget = label.getParent().getParent();
+ mLauncher.assertEquals(
+ "View is not WidgetCell",
+ "com.android.launcher3.widget.WidgetCell",
+ widget.getClassName());
- int maxWidth = 0;
- for (UiObject2 sibling : widget.getParent().getChildren()) {
- maxWidth = Math.max(sibling.getVisibleBounds().width(), maxWidth);
+ int maxWidth = 0;
+ for (UiObject2 sibling : widget.getParent().getChildren()) {
+ maxWidth = Math.max(sibling.getVisibleBounds().width(), maxWidth);
+ }
+
+ int visibleDelta = maxWidth - widget.getVisibleBounds().width();
+ if (visibleDelta > 0) {
+ Rect parentBounds = cell.getVisibleBounds();
+ mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
+ + mLauncher.getTouchSlop(),
+ parentBounds.centerY(), parentBounds.centerX(),
+ parentBounds.centerY(), 10, true, GestureScope.INSIDE);
+ }
+
+ if (widget.getVisibleBounds().bottom
+ <= displaySize.y - mLauncher.getBottomGestureSize()) {
+ return new Widget(mLauncher, widget);
+ }
}
- int visibleDelta = maxWidth - widget.getVisibleBounds().width();
- if (visibleDelta > 0) {
- Rect parentBounds = cell.getVisibleBounds();
- mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
- + mLauncher.getTouchSlop(),
- parentBounds.centerY(), parentBounds.centerX(),
- parentBounds.centerY(), 10, true, GestureScope.INSIDE);
- }
-
- if (widget.getVisibleBounds().bottom
- <= displaySize.y - mLauncher.getBottomGestureSize()) {
- return new Widget(mLauncher, widget);
- }
+ mLauncher.assertTrue("Too many attempts", ++i <= 40);
+ mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
}
-
- mLauncher.assertTrue("Too many attempts", ++i <= 40);
- mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
}
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 3f5dc8d..9ef6476 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -177,7 +177,8 @@
getHotseatAppIcon("Chrome"),
new Point(mLauncher.getDevice().getDisplayWidth(),
workspace.getVisibleBounds().centerY()),
- "deep_shortcuts_container");
+ "deep_shortcuts_container",
+ false);
verifyActiveContainer();
}
}
@@ -198,7 +199,7 @@
static void dragIconToWorkspace(
LauncherInstrumentation launcher, Launchable launchable, Point dest,
- String longPressIndicator) {
+ String longPressIndicator, boolean startsActivity) {
LauncherInstrumentation.log("dragIconToWorkspace: begin");
final Point launchableCenter = launchable.getObject().getVisibleCenter();
final long downTime = SystemClock.uptimeMillis();
@@ -219,6 +220,10 @@
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest,
LauncherInstrumentation.GestureScope.INSIDE),
NORMAL_STATE_ORDINAL);
+ if (startsActivity) {
+ launcher.expectEvent(
+ TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_STOP_ACTIVITY);
+ }
LauncherInstrumentation.log("dragIconToWorkspace: end");
launcher.waitUntilGone("drop_target_bar");
}
@@ -281,16 +286,22 @@
@Nullable
public Widget tryGetWidget(String label, long timeout) {
- final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
- By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView").desc(label),
- timeout);
- return widget != null ? new Widget(mLauncher, widget) : null;
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "getting widget " + label + " on workspace with timeout " + timeout)) {
+ final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
+ By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView").desc(label),
+ timeout);
+ return widget != null ? new Widget(mLauncher, widget) : null;
+ }
}
@Nullable
public Widget tryGetPendingWidget(long timeout) {
- final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
- By.clazz("com.android.launcher3.widget.PendingAppWidgetHostView"), timeout);
- return widget != null ? new Widget(mLauncher, widget) : null;
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "getting pending widget on workspace with timeout " + timeout)) {
+ final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
+ By.clazz("com.android.launcher3.widget.PendingAppWidgetHostView"), timeout);
+ return widget != null ? new Widget(mLauncher, widget) : null;
+ }
}
}
\ No newline at end of file