Merge "Add shadow to "Don't suggest app" drop target" into ub-launcher3-master
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/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/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/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/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..40d7c7a 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 translatable="false" 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 translatable="false" 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/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/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
new file mode 100644
index 0000000..7760255
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -0,0 +1,164 @@
+/*
+ * 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.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();
+ }
+ intent.setAction(Intent.ACTION_SEND)
+ .setComponent(null)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .setType("image/png")
+ .setData(uri)
+ .setFlags(FLAG_GRANT_READ_URI_PERMISSION)
+ .putExtra(Intent.EXTRA_STREAM, uri);
+ 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/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 573aa07..c00a679 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -958,6 +958,8 @@
}
});
}
+
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStop");
}
@Override
@@ -971,6 +973,7 @@
mAppWidgetHost.setListenIfResumed(true);
TraceHelper.INSTANCE.endSection(traceToken);
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStart");
}
private void handleDeferredResume() {
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/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/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/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..e70b570 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) {
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/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