From 020e628f22cc7975beab439c6da26af2f9ebc15b Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 28 Sep 2020 17:01:42 -0700 Subject: [PATCH 01/56] Add shadow radius to windows during app launch / close animations. Bug: 168608912 Change-Id: I2ec50b0b3711c0861659f9c641bbc05fcdeaab45 --- quickstep/res/values/dimens.xml | 2 ++ .../QuickstepAppTransitionManagerImpl.java | 12 ++++++++++-- .../android/quickstep/SwipeUpAnimationLogic.java | 13 +++++++++++-- .../android/quickstep/util/TaskViewSimulator.java | 3 ++- .../android/quickstep/util/TransformParams.java | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index c85fe6ca35..c7c0c7ed73 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -51,6 +51,8 @@ 16sp 16dp + 5dp + 40dp diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index dddcc865fa..6e567f792c 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -151,6 +151,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans private final float mContentTransY; private final float mWorkspaceTransY; private final float mClosingWindowTransY; + private final float mMaxShadowRadius; private DeviceProfile mDeviceProfile; @@ -184,6 +185,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y); mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y); mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y); + mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius); mLauncher.addOnDeviceProfileChangeListener(this); } @@ -536,6 +538,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans EXAGGERATED_EASE); FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0, RADIUS_DURATION, EXAGGERATED_EASE); + FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0, + APP_LAUNCH_DURATION, EXAGGERATED_EASE); @Override public void onUpdate(float percent) { @@ -598,7 +602,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans builder.withMatrix(matrix) .withWindowCrop(crop) .withAlpha(1f - mIconAlpha.value) - .withCornerRadius(mWindowRadius.value); + .withCornerRadius(mWindowRadius.value) + .withShadowRadius(mShadowRadius.value); } else { tmpPos.set(target.position.x, target.position.y); if (target.localBounds != null) { @@ -748,6 +753,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7); FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7); FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR); + FloatProp mShadowRadius = new FloatProp(mMaxShadowRadius, 0, 0, duration, + DEACCEL_1_7); @Override public void onUpdate(float percent) { @@ -769,7 +776,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans matrix.postTranslate(tmpPos.x, tmpPos.y); builder.withMatrix(matrix) .withAlpha(mAlpha.value) - .withCornerRadius(windowCornerRadius); + .withCornerRadius(windowCornerRadius) + .withShadowRadius(mShadowRadius.value); } else { matrix.setTranslate(tmpPos.x, tmpPos.y); builder.withMatrix(matrix) diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index b6eaa1c7b6..a46de1f601 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -29,6 +29,7 @@ import androidx.annotation.NonNull; import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; @@ -66,6 +67,8 @@ public abstract class SwipeUpAnimationLogic { // How much further we can drag past recents, as a factor of mTransitionDragLength. protected float mDragLengthFactor = 1; + protected final float mMaxShadowRadius; + protected AnimatorControllerWithResistance mWindowTransitionController; public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, @@ -80,6 +83,9 @@ public abstract class SwipeUpAnimationLogic { mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(), mDeviceState.getRotationTouchHelper().getDisplayRotation()); mTaskViewSimulator.setDrawsBelowRecents(true); + + mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius); + mTransformParams.setShadowRadius(mMaxShadowRadius); } protected void initTransitionEndpoints(DeviceProfile dp) { @@ -251,9 +257,11 @@ public abstract class SwipeUpAnimationLogic { mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); + float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0); mTransformParams .setTargetAlpha(getWindowAlpha(progress)) - .setCornerRadius(cornerRadius); + .setCornerRadius(cornerRadius) + .setShadowRadius(mMaxShadowRadius); mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius)); @@ -264,7 +272,8 @@ public abstract class SwipeUpAnimationLogic { Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { builder.withMatrix(mMatrix) .withWindowCrop(mCropRect) - .withCornerRadius(params.getCornerRadius()); + .withCornerRadius(params.getCornerRadius()) + .withShadowRadius(params.getShadowRadius()); } @Override diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java index 3a54bd61aa..08fe48d5ce 100644 --- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java +++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java @@ -322,7 +322,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy { Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { builder.withMatrix(mMatrix) .withWindowCrop(mTmpCropRect) - .withCornerRadius(getCurrentCornerRadius()); + .withCornerRadius(getCurrentCornerRadius()) + .withShadowRadius(params.getShadowRadius()); if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) { // When relativeLayer = 0, it reverts the surfaces back to the original order. diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java index 45f49bb785..c6a0c6631c 100644 --- a/quickstep/src/com/android/quickstep/util/TransformParams.java +++ b/quickstep/src/com/android/quickstep/util/TransformParams.java @@ -57,6 +57,7 @@ public class TransformParams { private float mProgress; private float mTargetAlpha; private float mCornerRadius; + private float mShadowRadius; private RemoteAnimationTargets mTargetSet; private SurfaceTransactionApplier mSyncTransactionApplier; private SurfaceControl mRecentsSurface; @@ -68,6 +69,7 @@ public class TransformParams { mProgress = 0; mTargetAlpha = 1; mCornerRadius = -1; + mShadowRadius = 0; } /** @@ -90,6 +92,14 @@ public class TransformParams { return this; } + /** + * Sets the shadow radius of the transformed window, in pixels. + */ + public TransformParams setShadowRadius(float shadowRadius) { + mShadowRadius = shadowRadius; + return this; + } + /** * Specifies the alpha of the transformed window. Default is 1. */ @@ -197,6 +207,10 @@ public class TransformParams { return mCornerRadius; } + public float getShadowRadius() { + return mShadowRadius; + } + public SurfaceControl getRecentsSurface() { return mRecentsSurface; } From 1d7ed30dba4b2c71fc7b0981532a872a13e5aedb Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 23 Sep 2020 12:15:43 -0700 Subject: [PATCH 02/56] Remove widgets that no longer fit the workspace in their current spans. This can happen when display size changes. We compare span sizes of widget in the db to the min sizes of the widget in the current display size. If the widget can no longer fit in its existing spans, we remove it. Also update test widgets to have minWidth/minHeight of 1dp. This ensures that the spanX, spanY, min* values remain consistent between different test devices. Bug: 168818961 Change-Id: I723372e4582658f78b2f23ced9073cb77977a6b8 --- src/com/android/launcher3/model/LoaderTask.java | 16 ++++++++++++++++ tests/res/xml/appwidget_hidden.xml | 4 ++-- tests/res/xml/appwidget_no_config.xml | 4 ++-- tests/res/xml/appwidget_with_config.xml | 4 ++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 2b4520b613..c0d5882b46 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -49,6 +49,7 @@ import android.util.LongSparseArray; import android.util.TimingLogger; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.Utilities; @@ -306,6 +307,7 @@ public class LoaderTask implements Runnable { final PackageManagerHelper pmHelper = new PackageManagerHelper(context); final boolean isSafeMode = pmHelper.isSafeMode(); final boolean isSdCardReady = Utilities.isBootCompleted(); + final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context); boolean clearDb = false; try { @@ -391,6 +393,7 @@ public class LoaderTask implements Runnable { WorkspaceItemInfo info; LauncherAppWidgetInfo appWidgetInfo; + LauncherAppWidgetProviderInfo widgetProviderInfo; Intent intent; String targetPkg; @@ -720,6 +723,19 @@ public class LoaderTask implements Runnable { + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); continue; } + widgetProviderInfo = + widgetHelper.getLauncherAppWidgetInfo(appWidgetId); + if (widgetProviderInfo != null + && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX + || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) { + // This can happen when display size changes. + c.markDeleted("Widget removed, min sizes not met: " + + "span=" + appWidgetInfo.spanX + "x" + + appWidgetInfo.spanY + " minSpan=" + + widgetProviderInfo.minSpanX + "x" + + widgetProviderInfo.minSpanY); + continue; + } if (!c.isOnWorkspaceOrHotseat()) { c.markDeleted("Widget found where container != " + "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); diff --git a/tests/res/xml/appwidget_hidden.xml b/tests/res/xml/appwidget_hidden.xml index 6f0e0066fd..f6cffb52bf 100644 --- a/tests/res/xml/appwidget_hidden.xml +++ b/tests/res/xml/appwidget_hidden.xml @@ -1,8 +1,8 @@ Date: Thu, 1 Oct 2020 12:26:48 -0700 Subject: [PATCH 03/56] [pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon Bug: b/169330678 Change-Id: Id5f8a0ce6d68f7ed9e4d1ff258ee3772229eb63b --- .../android/launcher3/views/ThumbnailSearchResultView.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java index bbc47739f3..81bcad97ed 100644 --- a/src/com/android/launcher3/views/ThumbnailSearchResultView.java +++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java @@ -86,9 +86,12 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction, target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START)); - ItemClickHandler.onClickRemoteAction(launcher, itemInfo); bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon() - .loadDrawable(getContext())).getBitmap(); + .loadDrawable(getContext())).getBitmap(); + Bitmap crop = Bitmap.createBitmap(bitmap, 0, + bitmap.getHeight() / 2 - bitmap.getWidth() / 2, + bitmap.getWidth(), bitmap.getWidth()); + bitmap = crop; setTag(itemInfo); } else { bitmap = (Bitmap) target.bundle.getParcelable("bitmap"); From d6b1f3c086f9ac097cd03e1ee898b153478ec11a Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 2 Oct 2020 00:26:35 -0700 Subject: [PATCH 04/56] Action icon should be used as a badge instead of main icon Bug: 169796517 Change-Id: I3f07fdc2ae6e1af463701f942c26c3ca5d836ee2 --- .../launcher3/views/SearchResultIconRow.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java index 313ae5e611..6438d1dbe4 100644 --- a/src/com/android/launcher3/views/SearchResultIconRow.java +++ b/src/com/android/launcher3/views/SearchResultIconRow.java @@ -34,6 +34,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfoWithIcon; @@ -83,7 +84,8 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements if (payload.mRemoteAction != null) { prepareUsingRemoteAction(payload.mRemoteAction, payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), - payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START)); + payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START), + payload.type == ItemType.ACTION); } else { prepareUsingShortcutInfo(payload.shortcuts.get(0)); } @@ -102,7 +104,8 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements }); } - private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) { + private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start, + boolean useIconToBadge) { RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start); applyFromRemoteActionInfo(itemInfo); @@ -110,8 +113,17 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements // If the Drawable from the remote action is not AdaptiveBitmap, styling will not work. try (LauncherIcons li = LauncherIcons.obtain(getContext())) { Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext()); - itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user, + BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user, Build.VERSION.SDK_INT); + + if (useIconToBadge) { + BitmapInfo placeholder = li.createIconBitmap( + itemInfo.getRemoteAction().getTitle().toString().substring(0, 1), + bitmap.color); + itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap); + } else { + itemInfo.bitmap = bitmap; + } reapplyItemInfoAsync(itemInfo); } }); From f622e42bf6983d3adb95386bfd6375d281f1d4f2 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 2 Oct 2020 10:35:56 -0700 Subject: [PATCH 05/56] Removing unused proto extensions Change-Id: I6d0319c99934dad5176b6f70b895a4ca772ec45f --- Android.bp | 2 -- proto_overrides/launcher_log_extension.proto | 30 -------------------- 2 files changed, 32 deletions(-) delete mode 100644 proto_overrides/launcher_log_extension.proto diff --git a/Android.bp b/Android.bp index e132854e62..081f48598e 100644 --- a/Android.bp +++ b/Android.bp @@ -35,14 +35,12 @@ java_library_static { name: "launcher_log_protos_lite", srcs: [ "protos/*.proto", - "proto_overrides/*.proto", ], sdk_version: "current", proto: { type: "lite", local_include_dirs:[ "protos", - "proto_overrides", ], }, static_libs: ["libprotobuf-java-lite"], diff --git a/proto_overrides/launcher_log_extension.proto b/proto_overrides/launcher_log_extension.proto deleted file mode 100644 index 2995aa2833..0000000000 --- a/proto_overrides/launcher_log_extension.proto +++ /dev/null @@ -1,30 +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.userevent"; -option java_outer_classname = "LauncherLogExtensions"; - -package userevent; - -// -// Use this to add any app specific extensions to the proto. -// -message LauncherEventExtension { -} - -message TargetExtension { -} From 58804ac5257f45dddbf7a6db35cf8f369ee1e88e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 16 Sep 2020 16:27:40 -0700 Subject: [PATCH 06/56] Adding stats log for add item flow Bug: 169385783 Bug: 168167693 Change-Id: I37395f1b118727f67e0f14c02f945b8213b165c8 --- .../launcher3/dragndrop/AddItemActivity.java | 8 ++++---- src/com/android/launcher3/model/data/ItemInfo.java | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java index 42e247aa9b..77d2b85ef9 100644 --- a/src/com/android/launcher3/dragndrop/AddItemActivity.java +++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java @@ -53,8 +53,8 @@ import com.android.launcher3.R; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.WidgetItem; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.PinRequestHelper; -import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; @@ -87,7 +87,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener private Bundle mWidgetOptions; private boolean mFinishOnPause = false; - private InstantAppResolver mInstantAppResolver; @Override protected void onCreate(Bundle savedInstanceState) { @@ -101,7 +100,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener mApp = LauncherAppState.getInstance(this); mIdp = mApp.getInvariantDeviceProfile(); - mInstantAppResolver = InstantAppResolver.newInstance(this); // Use the application context to get the device profile, as in multiwindow-mode, the // confirmation activity might be rotated. @@ -322,6 +320,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener } private void logCommand(StatsLogManager.EventEnum command) { - getStatsLogManager().logger().log(command); + getStatsLogManager().logger() + .withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag()) + .log(command); } } diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index e03fd72449..b11b419dde 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -32,6 +32,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SH import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET; +import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID; import android.content.ComponentName; import android.content.ContentValues; @@ -50,10 +51,10 @@ import com.android.launcher3.logger.LauncherAtom.ContainerInfo; import com.android.launcher3.logger.LauncherAtom.PredictionContainer; import com.android.launcher3.logger.LauncherAtom.SearchResultContainer; import com.android.launcher3.logger.LauncherAtom.SettingsContainer; +import com.android.launcher3.logger.LauncherAtom.Shortcut; import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; import com.android.launcher3.model.ModelWriter; -import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.util.ContentWriter; import java.util.Optional; @@ -282,9 +283,14 @@ public class ItemInfo { case ITEM_TYPE_DEEP_SHORTCUT: itemBuilder .setShortcut(nullableComponent - .map(component -> LauncherAtom.Shortcut.newBuilder() - .setShortcutName(component.flattenToShortString()) - .setShortcutId(ShortcutKey.fromItemInfo(this).getId())) + .map(component -> { + Shortcut.Builder lsb = Shortcut.newBuilder() + .setShortcutName(component.flattenToShortString()); + Optional.ofNullable(getIntent()) + .map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID)) + .ifPresent(lsb::setShortcutId); + return lsb; + }) .orElse(LauncherAtom.Shortcut.newBuilder())); break; case ITEM_TYPE_SHORTCUT: From 733e3c609b7653a36e58747c881458ec00d98df8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 29 Sep 2020 10:32:32 -0700 Subject: [PATCH 07/56] Moving some initializations to the background thread HandlerThread.getLooper blocks until the thread is ready. Instead moving all looper dependency to the new thread itself. Change-Id: I240e8c56b855a991433a7fe93875059e6dab146b --- .../launcher3/model/WellbeingModel.java | 202 +++++++----------- .../quickstep/util/RecentsOrientedState.java | 10 +- src/com/android/launcher3/Utilities.java | 15 ++ .../launcher3/states/RotationHelper.java | 41 +--- .../launcher3/util/BgObjectWithLooper.java | 46 ++++ 5 files changed, 145 insertions(+), 169 deletions(-) create mode 100644 src/com/android/launcher3/util/BgObjectWithLooper.java diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index f92b3e33b2..a9fc1aa085 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -18,8 +18,7 @@ package com.android.launcher3.model; import static android.content.ContentResolver.SCHEME_CONTENT; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.createAndStartNewLooper; +import static com.android.launcher3.Utilities.newContentObserver; import android.annotation.TargetApi; import android.app.RemoteAction; @@ -35,7 +34,7 @@ import android.os.Build; import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; -import android.os.Message; +import android.os.Looper; import android.os.Process; import android.os.UserHandle; import android.text.TextUtils; @@ -43,7 +42,7 @@ import android.util.ArrayMap; import android.util.Log; import androidx.annotation.MainThread; -import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; @@ -55,6 +54,7 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.RemoteActionShortcut; import com.android.launcher3.popup.SystemShortcut; +import com.android.launcher3.util.BgObjectWithLooper; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Preconditions; @@ -68,15 +68,11 @@ import java.util.Map; * Data model for digital wellbeing status of apps. */ @TargetApi(Build.VERSION_CODES.Q) -public final class WellbeingModel { +public final class WellbeingModel extends BgObjectWithLooper { private static final String TAG = "WellbeingModel"; private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000}; private static final boolean DEBUG = false; - private static final int MSG_PACKAGE_ADDED = 1; - private static final int MSG_PACKAGE_REMOVED = 2; - private static final int MSG_FULL_REFRESH = 3; - private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0; private static final int IN_MINIMAL_DEVICE = 2; @@ -98,9 +94,9 @@ public final class WellbeingModel { private final Context mContext; private final String mWellbeingProviderPkg; - private final Handler mWorkerHandler; - private final ContentObserver mContentObserver; + private Handler mWorkerHandler; + private ContentObserver mContentObserver; private final Object mModelLock = new Object(); // Maps the action Id to the corresponding RemoteAction @@ -111,60 +107,67 @@ public final class WellbeingModel { private WellbeingModel(final Context context) { mContext = context; - mWorkerHandler = - new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage); - mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg); - mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) { - @Override - public void onChange(boolean selfChange, Uri uri) { - if (DEBUG || mIsInTest) { - Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" - + selfChange + "], uri = [" + uri + "]"); - } - Preconditions.assertUIThread(); - - if (uri.getPath().contains(PATH_ACTIONS)) { - // Wellbeing reports that app actions have changed. - updateWellbeingData(); - } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) { - // Wellbeing reports that minimal device state or config is changed. - updateLauncherModel(context); - } - } - }; - FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () -> - updateLauncherModel(context)); + initializeInBackground("WellbeingHandler"); + } + @Override + protected void onInitialized(Looper looper) { + mWorkerHandler = new Handler(looper); + mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged); if (!TextUtils.isEmpty(mWellbeingProviderPkg)) { - context.registerReceiver( - new SimpleBroadcastReceiver(this::onWellbeingProviderChanged), + mContext.registerReceiver( + new SimpleBroadcastReceiver(t -> restartObserver()), PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg, Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED, Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED, - Intent.ACTION_PACKAGE_RESTARTED)); + Intent.ACTION_PACKAGE_RESTARTED), + null, mWorkerHandler); IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); - context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged), - filter); + mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged), + filter, null, mWorkerHandler); restartObserver(); } } + @WorkerThread + private void onWellbeingUriChanged(Uri uri) { + Preconditions.assertNonUiThread(); + if (DEBUG || mIsInTest) { + Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]"); + } + if (uri.getPath().contains(PATH_ACTIONS)) { + // Wellbeing reports that app actions have changed. + updateAllPackages(); + } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) { + // Wellbeing reports that minimal device state or config is changed. + if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { + return; + } + final Bundle extras = new Bundle(); + String dbFile; + if (isInMinimalDeviceMode()) { + dbFile = DB_NAME_MINIMAL_DEVICE; + extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY, + mWellbeingProviderPkg + ".api"); + } else { + dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile; + } + LauncherSettings.Settings.call(mContext.getContentResolver(), + LauncherSettings.Settings.METHOD_SWITCH_DATABASE, + dbFile, extras); + } + } + public void setInTest(boolean inTest) { mIsInTest = inTest; } - protected void onWellbeingProviderChanged(Intent intent) { - if (DEBUG || mIsInTest) { - Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]"); - } - restartObserver(); - } - + @WorkerThread private void restartObserver() { final ContentResolver resolver = mContext.getContentResolver(); resolver.unregisterContentObserver(mContentObserver); @@ -179,7 +182,7 @@ public final class WellbeingModel { Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e); if (mIsInTest) throw new RuntimeException(e); } - updateWellbeingData(); + updateAllPackages(); } @MainThread @@ -212,39 +215,6 @@ public final class WellbeingModel { } } - private void updateWellbeingData() { - mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH); - } - - private void updateLauncherModel(@NonNull final Context context) { - if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { - reloadLauncherInNormalMode(context); - return; - } - mWorkerHandler.post(() -> { - if (isInMinimalDeviceMode()) { - reloadLauncherInMinimalMode(context); - } else { - reloadLauncherInNormalMode(context); - } - }); - } - - private void reloadLauncherInNormalMode(@NonNull final Context context) { - LauncherSettings.Settings.call(context.getContentResolver(), - LauncherSettings.Settings.METHOD_SWITCH_DATABASE, - InvariantDeviceProfile.INSTANCE.get(context).dbFile); - } - - private void reloadLauncherInMinimalMode(@NonNull final Context context) { - final Bundle extras = new Bundle(); - extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY, - mWellbeingProviderPkg + ".api"); - LauncherSettings.Settings.call(context.getContentResolver(), - LauncherSettings.Settings.METHOD_SWITCH_DATABASE, - DB_NAME_MINIMAL_DEVICE, extras); - } - private Uri.Builder apiBuilder() { return new Uri.Builder() .scheme(SCHEME_CONTENT) @@ -277,7 +247,8 @@ public final class WellbeingModel { return false; } - private boolean updateActions(String... packageNames) { + @WorkerThread + private boolean updateActions(String[] packageNames) { if (packageNames.length == 0) { return true; } @@ -340,68 +311,51 @@ public final class WellbeingModel { return true; } - private boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_PACKAGE_REMOVED: { - String packageName = (String) msg.obj; - mWorkerHandler.removeCallbacksAndMessages(packageName); - synchronized (mModelLock) { - mPackageToActionId.remove(packageName); - } - return true; - } - case MSG_PACKAGE_ADDED: { - String packageName = (String) msg.obj; - mWorkerHandler.removeCallbacksAndMessages(packageName); - if (!updateActions(packageName)) { - scheduleRefreshRetry(msg); - } - return true; - } + @WorkerThread + private void updateActionsWithRetry(int retryCount, @Nullable String packageName) { + String[] packageNames = TextUtils.isEmpty(packageName) + ? mContext.getSystemService(LauncherApps.class) + .getActivityList(null, Process.myUserHandle()).stream() + .map(li -> li.getApplicationInfo().packageName).distinct() + .toArray(String[]::new) + : new String[] { packageName }; - case MSG_FULL_REFRESH: { - // Remove all existing messages - mWorkerHandler.removeCallbacksAndMessages(null); - final String[] packageNames = mContext.getSystemService(LauncherApps.class) - .getActivityList(null, Process.myUserHandle()).stream() - .map(li -> li.getApplicationInfo().packageName).distinct() - .toArray(String[]::new); - if (!updateActions(packageNames)) { - scheduleRefreshRetry(msg); - } - return true; - } + mWorkerHandler.removeCallbacksAndMessages(packageName); + if (updateActions(packageNames)) { + return; } - return false; - } - - private void scheduleRefreshRetry(Message originalMsg) { - int retryCount = originalMsg.arg1; if (retryCount >= RETRY_TIMES_MS.length) { // To many retries, skip return; } - - Message msg = Message.obtain(originalMsg); - msg.arg1 = retryCount + 1; - mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]); + mWorkerHandler.postDelayed( + () -> updateActionsWithRetry(retryCount + 1, packageName), + packageName, RETRY_TIMES_MS[retryCount]); } + @WorkerThread + private void updateAllPackages() { + updateActionsWithRetry(0, null); + } + + @WorkerThread private void onAppPackageChanged(Intent intent) { if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]"); - Preconditions.assertUIThread(); + Preconditions.assertNonUiThread(); final String packageName = intent.getData().getSchemeSpecificPart(); if (packageName == null || packageName.length() == 0) { // they sent us a bad intent return; } - final String action = intent.getAction(); if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget(); + mWorkerHandler.removeCallbacksAndMessages(packageName); + synchronized (mModelLock) { + mPackageToActionId.remove(packageName); + } } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget(); + updateActionsWithRetry(0, packageName); } } diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index de44e07bae..5f0ef83382 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -23,6 +23,7 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static com.android.launcher3.Utilities.newContentObserver; import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; @@ -73,12 +74,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre private static final boolean DEBUG = false; private static final String DELIMITER_DOT = "\\."; - private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - updateAutoRotateSetting(); - } - }; + private ContentObserver mSystemAutoRotateObserver = + newContentObserver(new Handler(), t -> updateAutoRotateSetting()); + @Retention(SOURCE) @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) public @interface SurfaceRotation {} diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 43ccb7990c..1e023dfc0a 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -34,6 +34,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ShortcutInfo; import android.content.res.Resources; +import android.database.ContentObserver; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Matrix; @@ -44,6 +45,7 @@ import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; +import android.net.Uri; import android.os.Build; import android.os.DeadObjectException; import android.os.Handler; @@ -83,6 +85,7 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -661,6 +664,18 @@ public final class Utilities { return slop * slop; } + /** + * Helper method to create a content provider + */ + public static ContentObserver newContentObserver(Handler handler, Consumer command) { + return new ContentObserver(handler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + command.accept(uri); + } + }; + } + private static class FixedSizeEmptyDrawable extends ColorDrawable { private final int mSize; diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java index 5a60f5328a..ecf4f36c52 100644 --- a/src/com/android/launcher3/states/RotationHelper.java +++ b/src/com/android/launcher3/states/RotationHelper.java @@ -21,14 +21,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; import android.app.Activity; -import android.content.ContentResolver; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; -import android.database.ContentObserver; -import android.os.Handler; -import android.provider.Settings; -import android.util.Log; import com.android.launcher3.R; import com.android.launcher3.Utilities; @@ -43,16 +38,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; - private final ContentResolver mContentResolver; - private boolean mSystemAutoRotateEnabled; - - private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) { - @Override - public void onChange(boolean selfChange) { - updateAutoRotateSetting(); - } - }; - public static boolean getAllowRotationDefaultValue() { // If the device's pixel density was scaled (usually via settings for A11y), use the // original dimensions to determine if rotation is allowed of not. @@ -106,20 +91,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { } else { mSharedPrefs = null; } - - mContentResolver = activity.getContentResolver(); - } - - private void updateAutoRotateSetting() { - int autoRotateEnabled = 0; - try { - autoRotateEnabled = Settings.System.getInt(mContentResolver, - Settings.System.ACCELEROMETER_ROTATION); - } catch (Settings.SettingNotFoundException e) { - Log.e(TAG, "autorotate setting not found", e); - } - - mSystemAutoRotateEnabled = autoRotateEnabled == 1; } @Override @@ -129,7 +100,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { getAllowRotationDefaultValue()); if (mHomeRotationEnabled != wasRotationEnabled) { notifyChange(); - updateAutoRotateSetting(); } } @@ -165,11 +135,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { if (!mInitialized) { mInitialized = true; notifyChange(); - - mContentResolver.registerContentObserver( - Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), - false, mSystemAutoRotateObserver); - updateAutoRotateSetting(); } } @@ -179,7 +144,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { if (mSharedPrefs != null) { mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); } - mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver); } } @@ -225,9 +189,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener { @Override public String toString() { return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d," - + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b," - + " mSystemAutoRotateEnabled=%b]", + + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]", mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags, - mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled); + mIgnoreAutoRotateSettings, mHomeRotationEnabled); } } diff --git a/src/com/android/launcher3/util/BgObjectWithLooper.java b/src/com/android/launcher3/util/BgObjectWithLooper.java new file mode 100644 index 0000000000..1483c43a11 --- /dev/null +++ b/src/com/android/launcher3/util/BgObjectWithLooper.java @@ -0,0 +1,46 @@ +/* + * 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.launcher3.util; + +import android.os.Looper; + +import androidx.annotation.WorkerThread; + +/** + * Utility class to define an object which does most of it's processing on a + * dedicated background thread. + */ +public abstract class BgObjectWithLooper { + + /** + * Start initialization of the object + */ + public final void initializeInBackground(String threadName) { + new Thread(this::runOnThread, threadName).start(); + } + + private void runOnThread() { + Looper.prepare(); + onInitialized(Looper.myLooper()); + Looper.loop(); + } + + /** + * Called on the background thread to handle initialization + */ + @WorkerThread + protected abstract void onInitialized(Looper looper); +} From a762b02418695f5a1ff2f96586660de8c3610280 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 2 Oct 2020 13:56:28 -0700 Subject: [PATCH 08/56] Annotating Quick Switch CUJ for non-3-button modes Bug: 169221288 Change-Id: I7145a9e28a2f0a789d19d2a0e3d15630c6e50f6a --- .../src/com/android/quickstep/AbsSwipeUpHandler.java | 10 ++++++++-- .../android/quickstep/RecentsAnimationController.java | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5e05a7dbe1..1439d4e97c 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -715,6 +715,8 @@ public abstract class AbsSwipeUpHandler, Q extends @UiThread public void onGestureStarted(boolean isLikelyToStartNewTask) { + InteractionJankMonitorWrapper.begin( + InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */); notifyGestureStartedAsync(); setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */); mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED); @@ -790,7 +792,8 @@ public abstract class AbsSwipeUpHandler, Q extends } private void onSettledOnEndTarget() { - switch (mGestureState.getEndTarget()) { + final GestureEndTarget endTarget = mGestureState.getEndTarget(); + switch (endTarget) { case HOME: mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); // Notify swipe-to-home (recents animation) is finished @@ -807,7 +810,10 @@ public abstract class AbsSwipeUpHandler, Q extends mStateCallback.setState(STATE_RESUME_LAST_TASK); break; } - ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget()); + ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget); + if (endTarget != NEW_TASK) { + InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); + } } /** @return Whether this was the task we were waiting to appear, and thus handled it. */ diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 51f5e5d785..798c12bcdc 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -23,6 +23,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.util.Preconditions; import com.android.systemui.shared.recents.model.ThumbnailData; +import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -149,6 +150,7 @@ public class RecentsAnimationController { mOnFinishedListener.accept(this); UI_HELPER_EXECUTOR.execute(() -> { mController.finish(toRecents, sendUserLeaveHint); + InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); if (callback != null) { MAIN_EXECUTOR.execute(callback); } From 7fcd74abb399100ac8243be6ca28c09cc8adc8c8 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 2 Oct 2020 19:20:11 -0700 Subject: [PATCH 09/56] Suggest result should launch Bug: 169980192 Change-Id: I762245a5cc4740d093c9cb3b44a508e9e3f2b763 --- src/com/android/launcher3/views/SearchResultSuggestRow.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/views/SearchResultSuggestRow.java b/src/com/android/launcher3/views/SearchResultSuggestRow.java index b5abbcc724..6543c76cfb 100644 --- a/src/com/android/launcher3/views/SearchResultSuggestRow.java +++ b/src/com/android/launcher3/views/SearchResultSuggestRow.java @@ -96,7 +96,8 @@ public class SearchResultSuggestRow extends LinearLayout implements private void handleSelection(int eventType) { ItemInfo itemInfo = (ItemInfo) getTag(); Launcher launcher = Launcher.getLauncher(getContext()); - if (itemInfo instanceof RemoteActionItemInfo) return; + + if (!(itemInfo instanceof RemoteActionItemInfo)) return; RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo; ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo); From d028937e74a9ea6d36e463de4c87ed37283bbdf6 Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Sat, 3 Oct 2020 00:36:53 -0700 Subject: [PATCH 10/56] [Live tile] Finish recents animation when the phone goes to sleep in live tile mode Fixes: 169988381 Test: manual Change-Id: Ic71d3e6767eadb6854dbd46581bf9d3242c161a4 --- quickstep/src/com/android/quickstep/views/RecentsView.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 7a8e11df78..42e43f9249 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -539,6 +539,9 @@ public abstract class RecentsView extends PagedView @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); + if (visibility == GONE && ENABLE_QUICKSTEP_LIVE_TILE.get()) { + finishRecentsAnimation(true /* toRecents */, null); + } updateTaskStackListenerState(); } From 0c943966d373d8ae7eef2b08e88ac44bf57d8a8d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 5 Oct 2020 10:23:27 -0700 Subject: [PATCH 11/56] Add null check for input receiver before updating batching - A change in the system (ie. sysui crash or nav mode change) could cause the input monitor to be disposed before the swipe animation settles Bug: 170121063 Test: Kill sysui while swiping up Change-Id: I1417b109fecdb98fae6197c7038dbe9307470853 --- .../src/com/android/quickstep/TouchInteractionService.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 7bf8fbafac..d3c4f55e9a 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -732,8 +732,11 @@ public class TouchInteractionService extends Service implements PluginListener Date: Mon, 5 Oct 2020 10:50:00 -0700 Subject: [PATCH 12/56] Avoid double search item highlight Change-Id: Ic2e28b18f6d5e3ed32cd5646bc3bb4789c378e57 --- .../android/launcher3/appprediction/PredictionRowView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index d3c4c3d62d..1855267f78 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -152,6 +152,10 @@ public class PredictionRowView extends LinearLayout implements private void updateVisibility() { setVisibility(mPredictionsEnabled ? VISIBLE : GONE); + if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mLauncher.getAppsView() != null + && mLauncher.getAppsView().getActiveRecyclerView() != null) { + mLauncher.getAppsView().invalidate(); + } } @Override From b6aff1f56d55a36256446ec3970d92e9da39b98c Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 5 Oct 2020 16:08:35 -0700 Subject: [PATCH 13/56] Fix NPE inside RecentsOrientedState Bug: 169963211 Change-Id: I86dd337dc1b862f3fa99b91b47fa250076233f96 --- .../src/com/android/quickstep/util/RecentsOrientedState.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index 5f0ef83382..bb84380edd 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -539,6 +539,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre * @return "MyObject@1234" */ private static String extractObjectNameAndAddress(String stringToExtract) { - return stringToExtract.substring(stringToExtract.lastIndexOf(DELIMITER_DOT)); + int index = stringToExtract.lastIndexOf(DELIMITER_DOT); + return index >= 0 ? stringToExtract.substring(index) : ""; } } From 3cf264f498e37c482fa4c559bf48ffa791279585 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Tue, 22 Sep 2020 12:58:38 -0700 Subject: [PATCH 14/56] Prevent hotseat updates if it is visible to the user. Test: manual Fixes: 168653219 Changing app icons under the user's finger could be disruptive. Added a checks for whether the hotseatand all apps predictions are visible and callbacks to update them when they become hidden. Change-Id: Ib9e6e904e9f662ecfaeea6a2fe21d1d81ba39b96 --- .../appprediction/PredictionRowView.java | 21 +++++++++++++++++++ .../HotseatPredictionController.java | 20 ++++++++++++++++++ src/com/android/launcher3/Hotseat.java | 19 +++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index 5b066c6be6..e7880fd739 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -108,6 +108,8 @@ public class PredictionRowView extends LinearLayout implements AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler; + @Nullable private List mPendingPredictedItems; + public PredictionRowView(@NonNull Context context) { this(context, null); } @@ -203,6 +205,16 @@ public class PredictionRowView extends LinearLayout implements * we can optimize by swapping them in place. */ public void setPredictedApps(List items) { + if (isShown() && getWindowVisibility() == View.VISIBLE) { + mPendingPredictedItems = items; + return; + } + + applyPredictedApps(items); + } + + private void applyPredictedApps(List items) { + mPendingPredictedItems = null; mPredictedApps.clear(); items.stream() .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo) @@ -341,4 +353,13 @@ public class PredictionRowView extends LinearLayout implements public View getFocusedChild() { return getChildAt(0); } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + + if (mPendingPredictedItems != null && !isVisible) { + applyPredictedApps(mPendingPredictedItems); + } + } } diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 380735044d..76bab596ac 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -251,6 +251,26 @@ public class HotseatPredictionController implements DragController.DragListener, * Sets or updates the predicted items */ public void setPredictedItems(FixedContainerItems items) { + if (mHotseat.isShown() && mHotseat.getWindowVisibility() == View.VISIBLE) { + mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> { + if (isVisible) { + return; + } + mHotseat.setOnVisibilityAggregatedCallback(null); + + applyPredictedItems(items); + }); + } else { + mHotseat.setOnVisibilityAggregatedCallback(null); + + applyPredictedItems(items); + } + } + + /** + * Sets or updates the predicted items only once the hotseat becomes hidden to the user + */ + private void applyPredictedItems(FixedContainerItems items) { mPredictedItems = items.items; if (mPredictedItems.isEmpty()) { HotseatRestoreHelper.restoreBackup(mLauncher); diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java index 6547b53940..e4bdb395e1 100644 --- a/src/com/android/launcher3/Hotseat.java +++ b/src/com/android/launcher3/Hotseat.java @@ -25,6 +25,10 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.Nullable; + +import java.util.function.Consumer; + /** * View class that represents the bottom row of the home screen. */ @@ -34,6 +38,7 @@ public class Hotseat extends CellLayout implements Insettable { private boolean mHasVerticalHotseat; private Workspace mWorkspace; private boolean mSendTouchToWorkspace; + @Nullable private Consumer mOnVisibilityAggregatedCallback; public Hotseat(Context context) { this(context, null); @@ -129,4 +134,18 @@ public class Hotseat extends CellLayout implements Insettable { } return event.getY() > getCellHeight(); } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + + if (mOnVisibilityAggregatedCallback != null) { + mOnVisibilityAggregatedCallback.accept(isVisible); + } + } + + /** Sets a callback to be called onVisibilityAggregated */ + public void setOnVisibilityAggregatedCallback(@Nullable Consumer callback) { + mOnVisibilityAggregatedCallback = callback; + } } From 8ed9707cf3a4300cb61942f08f0752c80eed086b Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Mon, 14 Sep 2020 23:25:37 -0700 Subject: [PATCH 15/56] [Live Tile] Support launching another task (other than the current running task) in Overview - Get rid of the defer cancelation logic - Render animation on the task view of the task being launched upon task view appeared callback - Finish the recents animation upon the end of the recents window animation Fixes: 164926736 Test: manual Change-Id: Ibffb6a9c74c235efc8615a22b0306551532c7b61 --- .../LauncherAppTransitionManagerImpl.java | 61 +--------- .../android/quickstep/AbsSwipeUpHandler.java | 114 ++++++++++++------ .../quickstep/RecentsAnimationController.java | 18 --- .../quickstep/TaskAnimationManager.java | 38 +++--- .../com/android/quickstep/TaskViewUtils.java | 70 ++++++++++- 5 files changed, 174 insertions(+), 127 deletions(-) diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 38adf39444..ae4bd96997 100644 --- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,14 +16,10 @@ package com.android.launcher3; -import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; -import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; @@ -32,11 +28,8 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.anim.PendingAnimation; +import com.android.quickstep.TaskViewUtils; import com.android.quickstep.views.RecentsView; -import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** @@ -53,60 +46,16 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti protected boolean isLaunchingFromRecents(@NonNull View v, @Nullable RemoteAnimationTargetCompat[] targets) { return mLauncher.getStateManager().getState().overviewUi - && findTaskViewToLaunch(mLauncher, v, targets) != null; + && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null; } @Override protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) { - RecentsView recentsView = mLauncher.getOverviewPanel(); - boolean skipLauncherChanges = !launcherClosing; - - TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets); - PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); - createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, - mLauncher.getDepthController(), pa); - anim.play(pa.buildAnim()); - - Animator childStateAnimation = null; - // Found a visible recents task that matches the opening app, lets launch the app from there - Animator launcherAnim; - final AnimatorListenerAdapter windowAnimEndListener; - if (launcherClosing) { - launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); - launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); - launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); - - // Make sure recents gets fixed up by resetting task alphas and scales, etc. - windowAnimEndListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mLauncher.getStateManager().moveToRestState(); - mLauncher.getStateManager().reapplyState(); - } - }; - } else { - AnimatorPlaybackController controller = - mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, - RECENTS_LAUNCH_DURATION); - controller.dispatchOnStart(); - childStateAnimation = controller.getTarget(); - launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION); - windowAnimEndListener = new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mLauncher.getStateManager().goToState(NORMAL, false); - } - }; - } - anim.play(launcherAnim); - - // Set the current animation first, before adding windowAnimEndListener. Setting current - // animation adds some listeners which need to be called before windowAnimEndListener - // (the ordering of listeners matter in this case). - mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation); - anim.addListener(windowAnimEndListener); + TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets, + launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(), + mLauncher.getDepthController()); } @Override diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5e05a7dbe1..941a10b6de 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -41,10 +41,14 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; +import static com.android.quickstep.TaskUtils.taskIsATargetWithMode; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.ActivityManager; @@ -104,6 +108,7 @@ import com.android.systemui.shared.system.TaskInfoCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.function.Consumer; /** @@ -1370,17 +1375,64 @@ public abstract class AbsSwipeUpHandler, Q extends private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { endLauncherTransitionController(); mActivityInterface.onSwipeUpToRecentsComplete(); - if (mRecentsAnimationController != null) { - mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */, - true /* screenshot */); - } mRecentsView.onSwipeUpAnimationSuccess(); + if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { + mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler( + this::launchOtherTaskInLiveTileMode); + } SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView()); reset(); } + private void launchOtherTaskInLiveTileMode(RemoteAnimationTargetCompat appearedTaskTarget) { + TaskView taskView = mRecentsView.getTaskView(appearedTaskTarget.taskId); + if (taskView == null) { + return; + } + + RemoteAnimationTargetCompat[] apps = Arrays.copyOf( + mRecentsAnimationTargets.apps, + mRecentsAnimationTargets.apps.length + 1); + apps[apps.length - 1] = appearedTaskTarget; + boolean launcherClosing = + taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING); + + AnimatorSet anim = new AnimatorSet(); + TaskViewUtils.composeRecentsLaunchAnimator( + anim, taskView, apps, + mRecentsAnimationTargets.wallpapers, launcherClosing, + mActivity.getStateManager(), mRecentsView, + mActivityInterface.getDepthController()); + anim.addListener(new AnimatorListenerAdapter(){ + + @Override + public void onAnimationEnd(Animator animator) { + cleanUp(false); + } + + @Override + public void onAnimationCancel(Animator animator) { + cleanUp(true); + } + + private void cleanUp(boolean canceled) { + if (mRecentsAnimationController != null) { + mRecentsAnimationController.finish(false /* toRecents */, + null /* onFinishComplete */); + if (canceled) { + mRecentsAnimationController = null; + } else { + mActivityInterface.onLaunchTaskSuccess(); + } + ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false); + } + } + }); + anim.start(); + } + private void addLiveTileOverlay() { if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) { mRecentsView.setLiveTileOverlayAttached(true); @@ -1438,39 +1490,33 @@ public abstract class AbsSwipeUpHandler, Q extends protected void startNewTask(Consumer resultCallback) { // 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 (!mCanceled) { - TaskView nextTask = mRecentsView.getNextPageTaskView(); - if (nextTask != null) { - int taskId = nextTask.getTask().key.id; - mGestureState.updateLastStartedTaskId(taskId); - boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() - .contains(taskId); - nextTask.launchTask(false /* animate */, true /* freezeTaskList */, - success -> { - resultCallback.accept(success); - if (success) { - if (hasTaskPreviouslyAppeared) { - onRestartPreviouslyAppearedTask(); - } - } else { - mActivityInterface.onLaunchTaskFailed(); - nextTask.notifyTaskLaunchFailed(TAG); - mRecentsAnimationController.finish(true /* toRecents */, null); + if (!mCanceled) { + TaskView nextTask = mRecentsView.getNextPageTaskView(); + if (nextTask != null) { + int taskId = nextTask.getTask().key.id; + mGestureState.updateLastStartedTaskId(taskId); + boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() + .contains(taskId); + nextTask.launchTask(false /* animate */, true /* freezeTaskList */, + success -> { + resultCallback.accept(success); + if (success) { + if (hasTaskPreviouslyAppeared) { + onRestartPreviouslyAppearedTask(); } - }, MAIN_EXECUTOR.getHandler()); - } else { - mActivityInterface.onLaunchTaskFailed(); - Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show(); - mRecentsAnimationController.finish(true /* toRecents */, null); - } + } else { + mActivityInterface.onLaunchTaskFailed(); + nextTask.notifyTaskLaunchFailed(TAG); + mRecentsAnimationController.finish(true /* toRecents */, null); + } + }, MAIN_EXECUTOR.getHandler()); + } else { + mActivityInterface.onLaunchTaskFailed(); + Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show(); + mRecentsAnimationController.finish(true /* toRecents */, null); } - mCanceled = false; } + mCanceled = false; } /** diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 51f5e5d785..21e6689072 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -88,24 +88,6 @@ public class RecentsAnimationController { } } - /** - * Notifies the controller that we want to defer cancel until the next app transition starts. - * If {@param screenshot} is set, then we will receive a screenshot on the next - * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call - * {@link #cleanupScreenshot()} when that screenshot is no longer used. - */ - public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { - mController.setDeferCancelUntilNextTransition(defer, screenshot); - } - - /** - * Cleans up the screenshot previously returned from - * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}. - */ - public void cleanupScreenshot() { - UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot()); - } - /** * Remove task remote animation target from * {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}. diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index cad51f4cc6..f38c1ea886 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED; @@ -31,6 +32,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; +import java.util.function.Consumer; + public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { private RecentsAnimationController mController; @@ -39,6 +42,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn // Temporary until we can hook into gesture state events private GestureState mLastGestureState; private RemoteAnimationTargetCompat mLastAppearedTaskTarget; + private Consumer mLaunchOtherTaskHandler; /** * Preloads the recents animation. @@ -88,22 +92,21 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @Override public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { - if (thumbnailData != null) { - // If a screenshot is provided, switch to the screenshot before cleaning up - activityInterface.switchRunningTaskViewToScreenshot(thumbnailData, - () -> cleanUpRecentsAnimation(thumbnailData)); - } else { - cleanUpRecentsAnimation(null /* canceledThumbnail */); - } + cleanUpRecentsAnimation(); } @Override public void onRecentsAnimationFinished(RecentsAnimationController controller) { - cleanUpRecentsAnimation(null /* canceledThumbnail */); + cleanUpRecentsAnimation(); } @Override public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) { + if (mLaunchOtherTaskHandler != null + && mLastGestureState.getEndTarget() == RECENTS) { + mLaunchOtherTaskHandler.accept(appearedTaskTarget); + return; + } if (mController != null) { if (mLastAppearedTaskTarget == null || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) { @@ -137,6 +140,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn return mCallbacks; } + /** + * The passed-in handler is used to render side task launch animation in recents in live tile + * mode. + */ + public void setLaunchOtherTaskInLiveTileModeHandler( + Consumer handler) { + mLaunchOtherTaskHandler = handler; + } + /** * Finishes the running recents animation. */ @@ -146,7 +158,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome ? mController::finishAnimationToHome : mController::finishAnimationToApp); - cleanUpRecentsAnimation(null /* canceledThumbnail */); + cleanUpRecentsAnimation(); } } @@ -173,12 +185,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn /** * Cleans up the recents animation entirely. */ - private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) { - // Clean up the screenshot if necessary - if (mController != null && canceledThumbnail != null) { - mController.cleanupScreenshot(); - } - + private void cleanUpRecentsAnimation() { // Release all the target leashes if (mTargets != null) { mTargets.release(); @@ -194,6 +201,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn mTargets = null; mLastGestureState = null; mLastAppearedTaskTarget = null; + mLaunchOtherTaskHandler = null; } public void dump() { diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index a5af1814d9..7299c3899e 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -17,15 +17,20 @@ package com.android.quickstep; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.statehandlers.DepthController.DEPTH; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.annotation.TargetApi; import android.content.ComponentName; import android.content.Context; @@ -35,12 +40,16 @@ import android.graphics.RectF; import android.os.Build; import android.view.View; +import androidx.annotation.NonNull; + import com.android.launcher3.BaseActivity; -import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.anim.AnimatorPlaybackController; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statehandlers.DepthController; +import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.util.DisplayController; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskViewSimulator; @@ -67,8 +76,7 @@ public final class TaskViewUtils { * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView. */ public static TaskView findTaskViewToLaunch( - BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) { - RecentsView recentsView = activity.getOverviewPanel(); + RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) { if (v instanceof TaskView) { TaskView taskView = (TaskView) v; return recentsView.isTaskViewVisible(taskView) ? taskView : null; @@ -130,7 +138,8 @@ public final class TaskViewUtils { SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); final RemoteAnimationTargets targets = - new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); + new RemoteAnimationTargets(appTargets, wallpaperTargets, + ENABLE_QUICKSTEP_LIVE_TILE.get() ? MODE_CLOSING : MODE_OPENING); targets.addReleaseCheck(applier); TransformParams params = new TransformParams() @@ -235,4 +244,57 @@ public final class TaskViewUtils { TOUCH_RESPONSE_INTERPOLATOR); } } + + public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, + @NonNull RemoteAnimationTargetCompat[] appTargets, + @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing, + @NonNull StateManager stateManager, @NonNull RecentsView recentsView, + @NonNull DepthController depthController) { + boolean skipLauncherChanges = !launcherClosing; + + TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets); + PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION); + createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets, + depthController, pa); + anim.play(pa.buildAnim()); + + Animator childStateAnimation = null; + // Found a visible recents task that matches the opening app, lets launch the app from there + Animator launcherAnim; + final AnimatorListenerAdapter windowAnimEndListener; + if (launcherClosing) { + launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); + launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); + launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); + + // Make sure recents gets fixed up by resetting task alphas and scales, etc. + windowAnimEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + stateManager.moveToRestState(); + stateManager.reapplyState(); + } + }; + } else { + AnimatorPlaybackController controller = + stateManager.createAnimationToNewWorkspace(NORMAL, + RECENTS_LAUNCH_DURATION); + controller.dispatchOnStart(); + childStateAnimation = controller.getTarget(); + launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION); + windowAnimEndListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + stateManager.goToState(NORMAL, false); + } + }; + } + anim.play(launcherAnim); + + // Set the current animation first, before adding windowAnimEndListener. Setting current + // animation adds some listeners which need to be called before windowAnimEndListener + // (the ordering of listeners matter in this case). + stateManager.setCurrentAnimation(anim, childStateAnimation); + anim.addListener(windowAnimEndListener); + } } From be17bdcd221f501c45876abe2249c1007858d0c0 Mon Sep 17 00:00:00 2001 From: jayaprakashs Date: Mon, 5 Oct 2020 09:01:52 -0700 Subject: [PATCH 16/56] [Search] Add logging to People and badding as to icons. Change-Id: I65948a2faca436216a94aa46139d425b8eade827 --- .../views/SearchResultPeopleView.java | 77 ++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java index 0c9a22f672..f20b08016f 100644 --- a/src/com/android/launcher3/views/SearchResultPeopleView.java +++ b/src/com/android/launcher3/views/SearchResultPeopleView.java @@ -23,9 +23,12 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; +import android.os.Process; import android.util.AttributeSet; import android.view.View; import android.widget.ImageButton; @@ -41,7 +44,8 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.search.AllAppsSearchBarController; -import com.android.launcher3.util.Themes; +import com.android.launcher3.icons.BitmapInfo; +import com.android.launcher3.icons.LauncherIcons; import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -105,15 +109,14 @@ public class SearchResultPeopleView extends LinearLayout implements mPlugin = adapterItemWithPayload.getPlugin(); mTitleView.setText(payload.getString("title")); mIntent = payload.getParcelable("intent"); - Bitmap icon = payload.getParcelable("icon"); - if (icon != null) { - RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon); - float radius = Themes.getDialogCornerRadius(getContext()); - d.setCornerRadius(radius); - d.setBounds(0, 0, mIconSize, mIconSize); - BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), - Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false)); - mIconView.setBackground(d); + Bitmap contactIcon = payload.getParcelable("icon"); + try (LauncherIcons li = LauncherIcons.obtain(getContext())) { + BitmapInfo badgeInfo = li.createBadgedIconBitmap( + getAppIcon(mIntent.getPackage()), Process.myUserHandle(), + Build.VERSION.SDK_INT); + setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false); + } catch (Exception e) { + setIcon(contactIcon, true); } ArrayList providers = payload.getParcelableArrayList("providers"); @@ -123,17 +126,14 @@ public class SearchResultPeopleView extends LinearLayout implements Bundle provider = providers.get(i); Intent intent = provider.getParcelable("intent"); setupProviderButton(button, provider, intent, adapterItemWithPayload); - String pkg = provider.getString("package_name"); UI_HELPER_EXECUTOR.post(() -> { - try { - ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo( - pkg, 0); - Drawable appIcon = applicationInfo.loadIcon(mPackageManager); + String pkg = provider.getString("package_name"); + Drawable appIcon = getAppIcon(pkg); + if (appIcon != null) { MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon)); - } catch (PackageManager.NameNotFoundException ignored) { } - }); + button.setVisibility(VISIBLE); } else { button.setVisibility(GONE); } @@ -141,6 +141,45 @@ public class SearchResultPeopleView extends LinearLayout implements adapterItemWithPayload.setSelectionHandler(this::handleSelection); } + /** + * Normalizes the bitmap to look like rounded App Icon + * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate + * bitmap from it. + */ + private Bitmap roundBitmap(Bitmap icon) { + final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon); + d.setCornerRadius(R.attr.folderIconRadius); + d.setBounds(0, 0, mIconSize, mIconSize); + final Bitmap bitmap = Bitmap.createBitmap(d.getBounds().width(), d.getBounds().height(), + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + d.draw(canvas); + return bitmap; + } + + private void setIcon(Bitmap icon, Boolean round) { + if (round) { + RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon); + d.setCornerRadius(R.attr.folderIconRadius); + d.setBounds(0, 0, mIconSize, mIconSize); + mIconView.setBackground(d); + } else { + mIconView.setBackground(new BitmapDrawable(getResources(), icon)); + } + } + + + private Drawable getAppIcon(String pkg) { + try { + ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo( + pkg, 0); + return applicationInfo.loadIcon(mPackageManager); + } catch (PackageManager.NameNotFoundException ignored) { + return null; + } + } + @Override public Object[] getTargetInfo() { return mTargetInfo; @@ -155,7 +194,8 @@ public class SearchResultPeopleView extends LinearLayout implements SearchTarget.ItemType.PEOPLE, SearchTargetEvent.CHILD_SELECT); searchTargetEvent.bundle = new Bundle(); - searchTargetEvent.bundle.putParcelable("intent", mIntent); + searchTargetEvent.bundle.putParcelable("intent", intent); + searchTargetEvent.bundle.putString("title", mTitleView.getText().toString()); searchTargetEvent.bundle.putBundle("provider", provider); if (mPlugin != null) { mPlugin.notifySearchTargetEvent(searchTargetEvent); @@ -172,6 +212,7 @@ public class SearchResultPeopleView extends LinearLayout implements eventType); searchTargetEvent.bundle = new Bundle(); searchTargetEvent.bundle.putParcelable("intent", mIntent); + searchTargetEvent.bundle.putString("title", mTitleView.getText().toString()); if (mPlugin != null) { mPlugin.notifySearchTargetEvent(searchTargetEvent); } From 565ed4ff69b534812818a2b9aa8789a1aea210eb Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 30 Sep 2020 10:42:07 -0700 Subject: [PATCH 17/56] Update Search UI [preview attached to bug] Bug: 169438169 Test: Manual Change-Id: I085f3dd38ac373c1afab82a637ec08715a6e0cc5 --- res/drawable/ic_allapps_search.xml | 2 +- res/layout/all_apps.xml | 6 ++---- res/values/strings.xml | 2 +- .../android/launcher3/allapps/AllAppsSectionDecorator.java | 4 ++-- src/com/android/launcher3/views/ScrimView.java | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/res/drawable/ic_allapps_search.xml b/res/drawable/ic_allapps_search.xml index 2aeb9479bd..c0e20f1bb8 100644 --- a/res/drawable/ic_allapps_search.xml +++ b/res/drawable/ic_allapps_search.xml @@ -19,6 +19,6 @@ android:viewportHeight="24.0" android:viewportWidth="24.0"> diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml index 67ec6641db..0041c9a7c8 100644 --- a/res/layout/all_apps.xml +++ b/res/layout/all_apps.xml @@ -1,5 +1,4 @@ - - - Search apps - Search this phone and more... + Search this phone and more… Loading apps… diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java index 0214c3547d..f2a1f852be 100644 --- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java +++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java @@ -101,8 +101,8 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration { * Handles grouping and drawing of items in the same all apps sections. */ public static class SectionDecorationHandler { - private static final int FILL_ALPHA = (int) (.3f * 255); - private static final int FOCUS_ALPHA = (int) (.8f * 255); + private static final int FILL_ALPHA = 0; + private static final int FOCUS_ALPHA = (int) (.9f * 255); protected RectF mBounds = new RectF(); private final boolean mIsFullWidth; diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 77cec80fd7..15ff2f575b 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -42,7 +42,7 @@ import com.android.launcher3.util.Themes; */ public class ScrimView extends View implements Insettable, OnChangeListener { - private static final float SCRIM_ALPHA = .75f; + private static final float SCRIM_ALPHA = .8f; protected final T mLauncher; private final WallpaperColorInfo mWallpaperColorInfo; protected final int mEndScrim; From 0ebbc18803aaf8ef2f6db7d628d7ae1ce322e842 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 6 Oct 2020 14:52:27 -0700 Subject: [PATCH 18/56] Removing tracing for a gone flake Bug: 156044202 Change-Id: Ice142bb941fee7b731f46c2073fab17d83bbc871 --- tests/tapl/com/android/launcher3/tapl/Background.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java index c9c846fc1a..153b3ceefb 100644 --- a/tests/tapl/com/android/launcher3/tapl/Background.java +++ b/tests/tapl/com/android/launcher3/tapl/Background.java @@ -88,10 +88,6 @@ public class Background extends LauncherInstrumentation.VisibleContainer { ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; - // b/156044202 - mLauncher.log("Hierarchy before swiping up to overview:"); - mLauncher.dumpViewHierarchy(); - mLauncher.sendPointer( downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope); mLauncher.executeAndWaitForLauncherEvent( From 1fddddb4f30505e0fc9bb2e7c0d88b38ad900e54 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 29 Sep 2020 17:29:06 -0700 Subject: [PATCH 19/56] Update launcher_trace.proto for quick switch Sample output from one entry: entry { elapsed_realtime_nanos: 440461382888540 launcher { touch_interaction_service { service_connected: true overview_component_obvserver { overview_activity_started: true overview_activity_resumed: false } input_consumer { name: "TYPE_OTHER_ACTIVITY:TYPE_ONE_HANDED" swipe_handler { gesture_state { endTarget: NEW_TASK } is_recents_attached_to_app_window: true scroll_offset: 846 app_to_overview_progress: 0 } } } } } Bug: 167259591 Change-Id: I7f199d88f1d736efcea6b9165b8c4b77a5d27c58 --- protos/launcher_trace.proto | 36 +++++++++++++++++++ .../android/quickstep/AbsSwipeUpHandler.java | 24 +++++++++++++ .../quickstep/BaseActivityInterface.java | 9 +++++ .../com/android/quickstep/GestureState.java | 32 +++++++++++++---- .../com/android/quickstep/InputConsumer.java | 20 +++++++++++ .../quickstep/OverviewComponentObserver.java | 15 ++++++++ .../quickstep/TouchInteractionService.java | 6 ++++ .../inputconsumers/DelegateInputConsumer.java | 6 ++++ .../OtherActivityInputConsumer.java | 8 +++++ 9 files changed, 150 insertions(+), 6 deletions(-) diff --git a/protos/launcher_trace.proto b/protos/launcher_trace.proto index c6f3543c09..65fcfe512b 100644 --- a/protos/launcher_trace.proto +++ b/protos/launcher_trace.proto @@ -28,4 +28,40 @@ message LauncherTraceProto { message TouchInteractionServiceProto { optional bool service_connected = 1; + optional OverviewComponentObserverProto overview_component_obvserver = 2; + optional InputConsumerProto input_consumer = 3; +} + +message OverviewComponentObserverProto { + + optional bool overview_activity_started = 1; + optional bool overview_activity_resumed = 2; +} + +message InputConsumerProto { + + optional string name = 1; + optional SwipeHandlerProto swipe_handler = 2; +} + +message SwipeHandlerProto { + + optional GestureStateProto gesture_state = 1; + optional bool is_recents_attached_to_app_window = 2; + optional int32 scroll_offset = 3; + // Swipe up progress from 0 (app) to 1 (overview); can be > 1 if swiping past overview. + optional float app_to_overview_progress = 4; +} + +message GestureStateProto { + + optional GestureEndTarget endTarget = 1 [default = UNSET]; + + enum GestureEndTarget { + UNSET = 0; + HOME = 1; + RECENTS = 2; + NEW_TASK = 3; + LAST_TASK = 4; + } } diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5e05a7dbe1..cee9854880 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -77,6 +77,8 @@ import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.tracing.InputConsumerProto; +import com.android.launcher3.tracing.SwipeHandlerProto; import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.WindowBounds; @@ -88,6 +90,7 @@ import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AnimatorControllerWithResistance; import com.android.quickstep.util.InputConsumerProxy; import com.android.quickstep.util.MotionPauseDetector; +import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TransformParams; @@ -1556,6 +1559,27 @@ public abstract class AbsSwipeUpHandler, Q extends } mTaskViewSimulator.apply(mTransformParams); } + ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate(); + } + + /** + * Used for winscope tracing, see launcher_trace.proto + * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto + * @param inputConsumerProto The parent of this proto message. + */ + public void writeToProto(InputConsumerProto.Builder inputConsumerProto) { + SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder(); + + mGestureState.writeToProto(swipeHandlerProto); + + swipeHandlerProto.setIsRecentsAttachedToAppWindow( + mAnimationFactory.isRecentsAttachedToAppWindow()); + swipeHandlerProto.setScrollOffset(mRecentsView == null + ? 0 + : mRecentsView.getScrollOffset()); + swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value); + + inputConsumerProto.setSwipeHandler(swipeHandlerProto); } public interface Factory { diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index d35e270dd5..9089ae5f96 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -297,6 +297,10 @@ public abstract class BaseActivityInterface4[I] 0x110000->20[I] that means 4=by recents and 20=latency 20ms. Change-Id: I81ee804895b7712f4d925736f5b4694c11a12cbe (cherry picked from commit 63623967b83edad56db58173ebb6687c685b9177) --- quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java | 5 +++++ .../com/android/quickstep/RecentsAnimationCallbacks.java | 7 +++++++ .../src/com/android/quickstep/TaskAnimationManager.java | 5 +++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index dbf75fa154..85995a998f 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1476,6 +1476,11 @@ public abstract class AbsSwipeUpHandler, Q extends mGestureEndCallback = gestureEndCallback; } + @Override + public long getStartTouchTime() { + return mTouchTimeMs; + } + protected void linkRecentsViewScroll() { SurfaceTransactionApplier.create(mRecentsView, applier -> { mTransformParams.setSyncTransactionApplier(applier); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index a21c7140de..f319b943c6 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -160,5 +160,12 @@ public class RecentsAnimationCallbacks implements * Callback made when a task started from the recents is ready for an app transition. */ default void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {} + + /** + * The time in milliseconds of the touch event that starts the recents animation. + */ + default long getStartTouchTime() { + return 0; + } } } diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index f38c1ea886..797797f3d4 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -50,7 +50,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn public void preloadRecentsAnimation(Intent intent) { // Pass null animation handler to indicate this start is for preloading UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() - .startRecentsActivity(intent, null, null, null, null)); + .startRecentsActivity(intent, 0, null, null, null)); } /** @@ -119,10 +119,11 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn } } }); + final long eventTime = listener.getStartTouchTime(); mCallbacks.addListener(gestureState); mCallbacks.addListener(listener); UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() - .startRecentsActivity(intent, null, mCallbacks, null, null)); + .startRecentsActivity(intent, eventTime, mCallbacks, null, null)); gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED); return mCallbacks; } From 222afb970434c7972589adfc509bd2c256ca6556 Mon Sep 17 00:00:00 2001 From: Hongwei Wang Date: Fri, 2 Oct 2020 13:51:36 -0700 Subject: [PATCH 21/56] Comply with the ISystemUiProxy.aidl change Two methods are added to support communications between Launcher and SysUI when user swipes an auto PiP-able Activity to home. Bug: 143965596 Test: N/A Change-Id: I2c73a287a094e882bde3cd71c27f9f66ae20e64a (cherry picked from commit 88ddae38db924f700082a113670ce5a719116a95) --- .../com/android/quickstep/SystemUiProxy.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 2666869136..a214d81e76 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -17,7 +17,10 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import android.app.PictureInPictureParams; +import android.content.ComponentName; import android.content.Context; +import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; @@ -380,4 +383,29 @@ public class SystemUiProxy implements ISystemUiProxy { } } } + + @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) { + if (mSystemUiProxy != null) { + try { + return mSystemUiProxy.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight); + } catch (RemoteException e) { + Log.w(TAG, "Failed call startSwipePipToHome", e); + } + } + return null; + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + if (mSystemUiProxy != null) { + try { + mSystemUiProxy.stopSwipePipToHome(componentName, destinationBounds); + } catch (RemoteException e) { + Log.w(TAG, "Failed call stopSwipePipToHome"); + } + } + } } From 4b7f38b8fa004b514244304fcc07ff514a2fa46b Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Tue, 6 Oct 2020 18:37:46 -0700 Subject: [PATCH 22/56] Align fallback result query with result text screenshot: https://screenshot.googleplex.com/6Daj5vdmz2jmznX bug: 169438169 test: Manual Change-Id: Ie621ed3c834aec5e9467607da4f685d05d152183 --- res/layout/search_result_icon_row.xml | 2 +- res/layout/search_result_play_item.xml | 1 - res/layout/search_result_suggest.xml | 37 +++-- res/layout/search_section_title.xml | 1 + res/values/attrs.xml | 7 + src/com/android/launcher3/BubbleTextView.java | 2 +- .../allapps/AllAppsTransitionController.java | 21 ++- .../launcher3/views/HeroSearchResultView.java | 2 - .../launcher3/views/SearchResultIconRow.java | 84 ++++++++--- .../views/SearchResultSuggestRow.java | 132 ------------------ 10 files changed, 99 insertions(+), 190 deletions(-) delete mode 100644 src/com/android/launcher3/views/SearchResultSuggestRow.java diff --git a/res/layout/search_result_icon_row.xml b/res/layout/search_result_icon_row.xml index 5ecc0c27b6..ef3c8b2857 100644 --- a/res/layout/search_result_icon_row.xml +++ b/res/layout/search_result_icon_row.xml @@ -1,4 +1,4 @@ - - + android:textSize="16sp" + android:padding="@dimen/dynamic_grid_edge_margin" + launcher:iconDisplay="hero_app" + android:drawableTint="?android:attr/textColorPrimary" + launcher:customIcon="@drawable/ic_allapps_search" + launcher:iconSizeOverride="24dp" + launcher:matchTextInsetWithQuery="true" + launcher:layoutHorizontal="true" + android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding" + > - - - \ No newline at end of file + \ No newline at end of file diff --git a/res/layout/search_section_title.xml b/res/layout/search_section_title.xml index 941901591c..c541631cd1 100644 --- a/res/layout/search_section_title.xml +++ b/res/layout/search_section_title.xml @@ -18,6 +18,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:fontFamily="@style/TextHeadline" + android:textStyle="bold" android:padding="4dp" android:textColor="?android:attr/textColorPrimary" android:textSize="14sp" /> \ No newline at end of file diff --git a/res/values/attrs.xml b/res/values/attrs.xml index acb8221d7c..6b0f30082c 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -69,6 +69,13 @@ + + + + + + + diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 1e5a9e44d5..817d02827c 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -627,7 +627,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, /** * Sets the icon for this view based on the layout direction. */ - private void setIcon(Drawable icon) { + protected void setIcon(Drawable icon) { if (mIsIconVisible) { applyCompoundDrawables(icon); } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 4195a056fe..0c488a609b 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -68,16 +68,16 @@ public class AllAppsTransitionController implements StateHandler, public static final FloatProperty ALL_APPS_PROGRESS = new FloatProperty("allAppsProgress") { - @Override - public Float get(AllAppsTransitionController controller) { - return controller.mProgress; - } + @Override + public Float get(AllAppsTransitionController controller) { + return controller.mProgress; + } - @Override - public void setValue(AllAppsTransitionController controller, float progress) { - controller.setProgress(progress); - } - }; + @Override + public void setValue(AllAppsTransitionController controller, float progress) { + controller.setProgress(progress); + } + }; private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0; @@ -133,7 +133,6 @@ public class AllAppsTransitionController implements StateHandler, * in xml-based animations which also handle updating the appropriate UI. * * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace - * * @see #setState(LauncherState) * @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation) */ @@ -238,7 +237,7 @@ public class AllAppsTransitionController implements StateHandler, mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView); mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); } } diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java index dd322d921a..91337ba824 100644 --- a/src/com/android/launcher3/views/HeroSearchResultView.java +++ b/src/com/android/launcher3/views/HeroSearchResultView.java @@ -96,8 +96,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, launcher.getItemOnClickListener().onClick(view); }); mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this)); - setLayoutParams( - new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx)); mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0); diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java index 6438d1dbe4..c73eeaefdf 100644 --- a/src/com/android/launcher3/views/SearchResultIconRow.java +++ b/src/com/android/launcher3/views/SearchResultIconRow.java @@ -22,16 +22,19 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.app.RemoteAction; import android.content.Context; import android.content.pm.ShortcutInfo; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.util.AttributeSet; +import android.widget.EditText; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.icons.BitmapInfo; @@ -53,25 +56,51 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements AllAppsSearchBarController.PayloadResultHandler { private final Object[] mTargetInfo = createTargetInfo(); + private final int mCustomIconResId; + private final boolean mMatchesInset; + private ShortcutInfo mShortcutInfo; private AllAppsSearchPlugin mPlugin; private AdapterItemWithPayload mAdapterItem; public SearchResultIconRow(@NonNull Context context) { - super(context); + this(context, null, 0); } public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); + this(context, attrs, 0); } public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SearchResultIconRow, defStyleAttr, 0); + mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0); + mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery, + false); + + a.recycle(); } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Launcher launcher = Launcher.getLauncher(getContext()); + if (mMatchesInset && launcher.getAppsView() != null && getParent() != null) { + EditText editText = launcher.getAppsView().getSearchUiManager().getEditText(); + if (editText != null) { + int counterOffset = getIconSize() + getCompoundDrawablePadding() / 2; + setPadding(editText.getLeft() - counterOffset, getPaddingTop(), + getPaddingRight(), getPaddingBottom()); + } + } + } + + @Override public void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload) { if (mAdapterItem != null) { @@ -98,10 +127,12 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext()); applyFromWorkspaceItem(workspaceItemInfo); LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext()); - MODEL_EXECUTOR.execute(() -> { - launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo); - reapplyItemInfoAsync(workspaceItemInfo); - }); + if (!loadIconFromResource()) { + MODEL_EXECUTOR.execute(() -> { + launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo); + reapplyItemInfoAsync(workspaceItemInfo); + }); + } } private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start, @@ -109,27 +140,36 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start); applyFromRemoteActionInfo(itemInfo); - UI_HELPER_EXECUTOR.post(() -> { - // If the Drawable from the remote action is not AdaptiveBitmap, styling will not work. - try (LauncherIcons li = LauncherIcons.obtain(getContext())) { - Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext()); - BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user, - Build.VERSION.SDK_INT); + if (!loadIconFromResource()) { + UI_HELPER_EXECUTOR.post(() -> { + // If the Drawable from the remote action is not AdaptiveBitmap, styling will not + // work. + try (LauncherIcons li = LauncherIcons.obtain(getContext())) { + Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext()); + BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user, + Build.VERSION.SDK_INT); - if (useIconToBadge) { - BitmapInfo placeholder = li.createIconBitmap( - itemInfo.getRemoteAction().getTitle().toString().substring(0, 1), - bitmap.color); - itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap); - } else { - itemInfo.bitmap = bitmap; + if (useIconToBadge) { + BitmapInfo placeholder = li.createIconBitmap( + itemInfo.getRemoteAction().getTitle().toString().substring(0, 1), + bitmap.color); + itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap); + } else { + itemInfo.bitmap = bitmap; + } + reapplyItemInfoAsync(itemInfo); } - reapplyItemInfoAsync(itemInfo); - } - }); + }); + } } + private boolean loadIconFromResource() { + if (mCustomIconResId == 0) return false; + setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId)); + return true; + } + void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) { MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon)); } diff --git a/src/com/android/launcher3/views/SearchResultSuggestRow.java b/src/com/android/launcher3/views/SearchResultSuggestRow.java deleted file mode 100644 index 6543c76cfb..0000000000 --- a/src/com/android/launcher3/views/SearchResultSuggestRow.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * 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.launcher3.views; - -import static com.android.systemui.plugins.shared.SearchTarget.ItemType.SUGGEST; - -import android.content.Context; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.View; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.Launcher; -import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; -import com.android.launcher3.allapps.search.AllAppsSearchBarController; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.RemoteActionItemInfo; -import com.android.launcher3.touch.ItemClickHandler; -import com.android.systemui.plugins.AllAppsSearchPlugin; -import com.android.systemui.plugins.shared.SearchTarget; -import com.android.systemui.plugins.shared.SearchTargetEvent; - -/** - * A view representing a fallback search suggestion row. - */ -public class SearchResultSuggestRow extends LinearLayout implements - View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler { - - private final Object[] mTargetInfo = createTargetInfo(); - private AllAppsSearchPlugin mPlugin; - private AdapterItemWithPayload mAdapterItem; - private TextView mTitle; - - - public SearchResultSuggestRow(@NonNull Context context) { - super(context); - } - - public SearchResultSuggestRow(@NonNull Context context, - @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public SearchResultSuggestRow(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mTitle = findViewById(R.id.title); - setOnClickListener(this); - } - @Override - public void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload) { - mAdapterItem = adapterItemWithPayload; - SearchTarget payload = adapterItemWithPayload.getPayload(); - mPlugin = adapterItemWithPayload.getPlugin(); - - if (payload.mRemoteAction != null) { - RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(payload.mRemoteAction, - payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), - payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START)); - setTag(itemInfo); - } - showIfAvailable(mTitle, payload.mRemoteAction.getTitle().toString()); - setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); - adapterItemWithPayload.setSelectionHandler(this::handleSelection); - } - - @Override - public Object[] getTargetInfo() { - return mTargetInfo; - } - - private void handleSelection(int eventType) { - ItemInfo itemInfo = (ItemInfo) getTag(); - Launcher launcher = Launcher.getLauncher(getContext()); - - if (!(itemInfo instanceof RemoteActionItemInfo)) return; - - RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo; - ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo); - SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SUGGEST, eventType); - searchTargetEvent.bundle = new Bundle(); - searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction(); - searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START, - remoteItemInfo.shouldStartInLauncher()); - searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN, - remoteItemInfo.getToken()); - - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(searchTargetEvent); - } - } - - @Override - public void onClick(View view) { - handleSelection(SearchTargetEvent.SELECT); - } - - private void showIfAvailable(TextView view, @Nullable String string) { - System.out.println("Plugin suggest string:" + string); - if (TextUtils.isEmpty(string)) { - view.setVisibility(GONE); - } else { - System.out.println("Plugin suggest string:" + string); - view.setVisibility(VISIBLE); - view.setText(string); - } - } -} From 7a6e4c931f13b369bfa4328196b4632d6d848a19 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 6 Oct 2020 14:09:16 -0700 Subject: [PATCH 23/56] Annotating Quick Switch CUJ for 3-button mode Bug: 169221288 Change-Id: Ief62345fe6004dde699f44aa0c90329b7cd84e8b --- .../quickstep/OverviewCommandHelper.java | 4 ++++ .../com/android/quickstep/TaskViewUtils.java | 15 ++++++++++++++- .../android/quickstep/views/RecentsView.java | 17 ++++++++++++----- .../com/android/quickstep/views/TaskView.java | 10 ++++++++++ 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index b1b9396658..9f7871a29c 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -35,6 +35,7 @@ import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -174,6 +175,9 @@ public class OverviewCommandHelper { return; } + InteractionJankMonitorWrapper.begin( + InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timout */); + // Otherwise, start overview. mListener = mActivityInterface.createActivityInitListener(this::onActivityReady); mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 7299c3899e..5520ef7751 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -44,6 +44,7 @@ import androidx.annotation.NonNull; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; @@ -58,6 +59,7 @@ import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; /** @@ -135,6 +137,8 @@ public final class TaskViewUtils { RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController, PendingAnimation out) { + boolean isQuickSwitch = v.isEndQuickswitchCuj(); + v.setEndQuickswitchCuj(false); SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); final RemoteAnimationTargets targets = @@ -232,10 +236,19 @@ public final class TaskViewUtils { }); } - out.addListener(new AnimatorListenerAdapter() { + out.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + if (isQuickSwitch) { + InteractionJankMonitorWrapper.end( + InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH); + } + } + @Override public void onAnimationEnd(Animator animation) { targets.release(); + super.onAnimationEnd(animation); } }); diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 7a8e11df78..a817bd3b5c 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1292,19 +1292,26 @@ public abstract class RecentsView extends PagedView } public void showNextTask() { - TaskView runningTaskView = getRunningTaskView(); + final TaskView runningTaskView = getRunningTaskView(); + final TaskView targetTask; + if (runningTaskView == null) { // Launch the first task if (getTaskViewCount() > 0) { - getTaskViewAt(0).launchTask(true); + targetTask = getTaskViewAt(0); + } else { + return; } } else { - if (getNextTaskView() != null) { - getNextTaskView().launchTask(true); + final TaskView nextTask = getNextTaskView(); + if (nextTask != null) { + targetTask = nextTask; } else { - runningTaskView.launchTask(true); + targetTask = runningTaskView; } } + targetTask.setEndQuickswitchCuj(true); + targetTask.launchTask(true); } public void setRunningTaskIconScaledDown(boolean isScaledDown) { diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 686f8785c5..54a793c8e0 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -189,6 +189,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private CancellableTask mThumbnailLoadRequest; private CancellableTask mIconLoadRequest; + private boolean mEndQuickswitchCuj; + // Order in which the footers appear. Lower order appear below higher order. public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0; private final FooterWrapper[] mFooters = new FooterWrapper[2]; @@ -807,6 +809,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { return false; } + public boolean isEndQuickswitchCuj() { + return mEndQuickswitchCuj; + } + + public void setEndQuickswitchCuj(boolean endQuickswitchCuj) { + mEndQuickswitchCuj = endQuickswitchCuj; + } + private static final class TaskOutlineProvider extends ViewOutlineProvider { private final int mMarginTop; From 68d7a6e5b28af8cc55bdae7efc24cc7ebee81257 Mon Sep 17 00:00:00 2001 From: Andy Wickham Date: Wed, 7 Oct 2020 14:27:17 -0700 Subject: [PATCH 24/56] Adds feature flag for BC Smartspace. Change-Id: Iaf9fb7507d0ccd004a4e00188c75dadd6a059246 --- src/com/android/launcher3/config/FeatureFlags.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 4175280991..58b0a87468 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -156,10 +156,14 @@ public final class FeatureFlags { "ENABLE_DATABASE_RESTORE", true, "Enable database restore when new restore session is created"); - public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag( - "ENABLE_UNIVERSAL_SMARTSPACE", false, + public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag( + "ENABLE_SMARTSPACE_UNIVERSAL", false, "Replace Smartspace with a version rendered by System UI."); + public static final BooleanFlag ENABLE_SMARTSPACE_BLUECHIP = getDebugFlag( + "ENABLE_SMARTSPACE_BLUECHIP", false, + "Replace Smartspace with the Bluechip version. Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled."); + public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag( "ENABLE_SYSTEM_VELOCITY_PROVIDER", true, "Use system VelocityTracker's algorithm for motion pause detection."); From ab9ad20be600d1cbdc6b54a491d5fbb4c2cf9c16 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 7 Oct 2020 15:18:24 -0700 Subject: [PATCH 25/56] Search UI cleanup - offset all apps header padding with search input margin - avoid check shouldDraw check on HeaderRow. (race condition) Bug: 170263425 Change-Id: I11a1fbb448aa6afd18ec0984af9bb8b1d7600f69 --- .../android/launcher3/allapps/FloatingHeaderView.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java index 11d3fb995a..4bb6a00dd0 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderView.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -111,8 +111,8 @@ public class FloatingHeaderView extends LinearLayout implements public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); - mHeaderTopPadding = context.getResources() - .getDimensionPixelSize(R.dimen.all_apps_header_top_padding); + mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : + context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding); } @Override @@ -130,6 +130,7 @@ public class FloatingHeaderView extends LinearLayout implements } } mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]); + setPadding(0, mHeaderTopPadding, 0, 0); mAllRows = mFixedRows; } @@ -199,7 +200,7 @@ public class FloatingHeaderView extends LinearLayout implements public View getFocusedChild() { if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { for (FloatingHeaderRow row : mAllRows) { - if (row.hasVisibleContent() && row.shouldDraw()) { + if (row.hasVisibleContent()) { return row.getFocusedChild(); } } @@ -247,7 +248,9 @@ public class FloatingHeaderView extends LinearLayout implements public int getMaxTranslation() { if (mMaxTranslation == 0 && mTabsHidden) { - return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding); + int paddingOffset = getResources().getDimensionPixelSize( + R.dimen.all_apps_search_bar_bottom_padding); + return FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : paddingOffset; } else if (mMaxTranslation > 0 && mTabsHidden) { return mMaxTranslation + getPaddingTop(); } else { From 16045060c35639aea85afc572bea768d16e6c9f9 Mon Sep 17 00:00:00 2001 From: Hilary Huo Date: Thu, 8 Oct 2020 10:18:41 -0700 Subject: [PATCH 26/56] [pixel-search] add escape hatch Change-Id: I33ffea1fc0859564955380d7d1db317293d1a2cb --- .../android/launcher3/model/data/RemoteActionItemInfo.java | 4 ++++ src/com/android/launcher3/views/SearchResultIconRow.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/model/data/RemoteActionItemInfo.java b/src/com/android/launcher3/model/data/RemoteActionItemInfo.java index 81f7f3a0a2..d988bf9641 100644 --- a/src/com/android/launcher3/model/data/RemoteActionItemInfo.java +++ b/src/com/android/launcher3/model/data/RemoteActionItemInfo.java @@ -61,4 +61,8 @@ public class RemoteActionItemInfo extends ItemInfoWithIcon { public boolean shouldStartInLauncher() { return mShouldStart; } + + public boolean isEscapeHatch() { + return mToken.contains("item_type:[ESCAPE_HATCH]"); + } } diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java index c73eeaefdf..6d9c86a9a7 100644 --- a/src/com/android/launcher3/views/SearchResultIconRow.java +++ b/src/com/android/launcher3/views/SearchResultIconRow.java @@ -140,7 +140,7 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start); applyFromRemoteActionInfo(itemInfo); - if (!loadIconFromResource()) { + if (itemInfo.isEscapeHatch() || !loadIconFromResource()) { UI_HELPER_EXECUTOR.post(() -> { // If the Drawable from the remote action is not AdaptiveBitmap, styling will not // work. From 8d5b118060bff7f7518a9a14c0be5d265621f14c Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Thu, 8 Oct 2020 13:11:25 -0700 Subject: [PATCH 27/56] Revert PredictionRow shuoldDraw check + Show Rounded play result icons Bug: 168805872 Test: Manual Change-Id: I663c7f7ca1f1ac072e5e9c441deabef7c3fbd97b --- .../appprediction/PredictionRowView.java | 2 +- .../search/AllAppsSearchBarController.java | 2 +- .../launcher3/views/SearchResultPlayItem.java | 41 +++++++++++++++++-- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index b5ba8a6161..dbdfd8d410 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -184,7 +184,7 @@ public class PredictionRowView extends LinearLayout implements @Override public boolean shouldDraw() { - return getVisibility() == VISIBLE; + return getVisibility() != GONE; } @Override diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index d7fa5bc479..82c4db4da3 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -114,7 +114,7 @@ public class AllAppsSearchBarController @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { - if (actionId == EditorInfo.IME_ACTION_SEARCH) { + if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) { // selectFocusedView should return SearchTargetEvent that is passed onto onClick if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) { return true; diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java index ff3ecc8bc3..d05a29eab9 100644 --- a/src/com/android/launcher3/views/SearchResultPlayItem.java +++ b/src/com/android/launcher3/views/SearchResultPlayItem.java @@ -21,6 +21,11 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; import android.os.Bundle; @@ -38,6 +43,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.icons.BitmapRenderer; import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -50,6 +56,10 @@ import java.net.URL; */ public class SearchResultPlayItem extends LinearLayout implements AllAppsSearchBarController.PayloadResultHandler { + + private static final int BITMAP_CROP_MASK_COLOR = 0xff424242; + private static final float ICON_RADIUS_FACTOR = .5f; + private final DeviceProfile mDeviceProfile; private View mIconView; private TextView mTitleView; @@ -60,6 +70,8 @@ public class SearchResultPlayItem extends LinearLayout implements private AllAppsSearchPlugin mPlugin; private final Object[] mTargetInfo = createTargetInfo(); + final Paint mIconPaint = new Paint(); + public SearchResultPlayItem(Context context) { this(context, null, 0); @@ -109,7 +121,6 @@ public class SearchResultPlayItem extends LinearLayout implements // TODO: Should use a generic type to get values b/165320033 showIfNecessary(mDetailViews[0], bundle.getString("price")); showIfNecessary(mDetailViews[1], bundle.getString("rating")); - showIfNecessary(mDetailViews[2], bundle.getString("category")); mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder); UI_HELPER_EXECUTOR.execute(() -> { @@ -118,8 +129,9 @@ public class SearchResultPlayItem extends LinearLayout implements URL url = new URL(bundle.getString("icon_url")); Bitmap bitmap = BitmapFactory.decodeStream(url.openStream()); BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), - Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx, - mDeviceProfile.allAppsIconSizePx, false)); + Bitmap.createScaledBitmap(getRoundedBitmap(bitmap), + mDeviceProfile.allAppsIconSizePx, mDeviceProfile.allAppsIconSizePx, + false)); mIconView.post(() -> mIconView.setBackground(bitmapDrawable)); } catch (IOException e) { e.printStackTrace(); @@ -127,6 +139,29 @@ public class SearchResultPlayItem extends LinearLayout implements }); } + + private Bitmap getRoundedBitmap(Bitmap bitmap) { + int iconSize = bitmap.getWidth(); + + Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> { + final Rect rect = new Rect(0, 0, iconSize, iconSize); + final RectF rectF = new RectF(rect); + + mIconPaint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + mIconPaint.setColor(BITMAP_CROP_MASK_COLOR); + int radius = (int) (iconSize * ICON_RADIUS_FACTOR); + canvas.drawRoundRect(rectF, radius, radius, mIconPaint); + + mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(bitmap, rect, rect, mIconPaint); + }); + + return output; + + } + + @Override public Object[] getTargetInfo() { return mTargetInfo; From 692d2109a6702706d24b3b819d115882f7362509 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Thu, 8 Oct 2020 18:42:48 -0700 Subject: [PATCH 28/56] invalidate itemDecoration on predictedRow focus draw Change-Id: I66c731f00ae1c1292c51ff281957f05fd2d70dfa --- .../android/launcher3/appprediction/PredictionRowView.java | 7 ++----- src/com/android/launcher3/allapps/FloatingHeaderView.java | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index dbdfd8d410..37336d180e 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -148,10 +148,6 @@ public class PredictionRowView extends LinearLayout implements private void updateVisibility() { setVisibility(mPredictionsEnabled ? VISIBLE : GONE); - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mLauncher.getAppsView() != null - && mLauncher.getAppsView().getActiveRecyclerView() != null) { - mLauncher.getAppsView().invalidate(); - } } @Override @@ -170,6 +166,7 @@ public class PredictionRowView extends LinearLayout implements } mDecorationHandler.onDraw(canvas); mDecorationHandler.onFocusDraw(canvas, getFocusedChild()); + mLauncher.getAppsView().getActiveRecyclerView().invalidateItemDecorations(); } mFocusHelper.draw(canvas); super.dispatchDraw(canvas); @@ -184,7 +181,7 @@ public class PredictionRowView extends LinearLayout implements @Override public boolean shouldDraw() { - return getVisibility() != GONE; + return getVisibility() == VISIBLE; } @Override diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java index 4bb6a00dd0..813db7d0a0 100644 --- a/src/com/android/launcher3/allapps/FloatingHeaderView.java +++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java @@ -200,7 +200,7 @@ public class FloatingHeaderView extends LinearLayout implements public View getFocusedChild() { if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { for (FloatingHeaderRow row : mAllRows) { - if (row.hasVisibleContent()) { + if (row.hasVisibleContent() && row.shouldDraw()) { return row.getFocusedChild(); } } From b5334e3f07f0561808a2d6e9bba55f1e3a89191e Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 9 Oct 2020 00:50:48 -0700 Subject: [PATCH 29/56] Improve search section header Change-Id: I47cf207f0d0ab792c0e7a47c9d1185eec087ec88 --- res/layout/search_section_title.xml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/res/layout/search_section_title.xml b/res/layout/search_section_title.xml index c541631cd1..b7ba83e71b 100644 --- a/res/layout/search_section_title.xml +++ b/res/layout/search_section_title.xml @@ -17,8 +17,9 @@ android:id="@+id/section_title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:fontFamily="@style/TextHeadline" - android:textStyle="bold" - android:padding="4dp" + style="@style/TextHeadline" + android:paddingStart="4dp" + android:paddingBottom="2dp" + android:paddingTop="12dp" android:textColor="?android:attr/textColorPrimary" - android:textSize="14sp" /> \ No newline at end of file + android:textSize="18sp" /> \ No newline at end of file From 1b9e199b3d9c81c793758d96bb03e0c51c1b3fb1 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Thu, 8 Oct 2020 15:50:22 -0400 Subject: [PATCH 30/56] Fix hotseat and prediction row to allow updates when empty. Rotating the screen in the homescreen empties the hotseat, however it does not get populated while it is visible to the user. The user should not be able to see an empty hotseat or prediction row if predictions are available. It should therefore be possible to populate these when they are empty even if they are visible to the user. Change-Id: I8e5252bd29050c2cd9d443aedcb3f3e305c0e2d7 --- .../android/launcher3/appprediction/PredictionRowView.java | 2 +- .../launcher3/hybridhotseat/HotseatPredictionController.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java index 37336d180e..f313d7539e 100644 --- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -206,7 +206,7 @@ public class PredictionRowView extends LinearLayout implements * we can optimize by swapping them in place. */ public void setPredictedApps(List items) { - if (isShown() && getWindowVisibility() == View.VISIBLE) { + if (!mLauncher.isWorkspaceLoading() && isShown() && getWindowVisibility() == View.VISIBLE) { mPendingPredictedItems = items; return; } diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index 76bab596ac..d3b7e22299 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -251,7 +251,9 @@ public class HotseatPredictionController implements DragController.DragListener, * Sets or updates the predicted items */ public void setPredictedItems(FixedContainerItems items) { - if (mHotseat.isShown() && mHotseat.getWindowVisibility() == View.VISIBLE) { + if (!mLauncher.isWorkspaceLoading() + && mHotseat.isShown() + && mHotseat.getWindowVisibility() == View.VISIBLE) { mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> { if (isVisible) { return; From 979da64d8254599c332d83bf94f3f1fc3fe45fef Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Tue, 22 Sep 2020 21:52:40 +0800 Subject: [PATCH 31/56] Add app start source info of apps launched from launcher Bug: 166614700 Test: Enable statsd log: "adb shell cmd stats print-logs" adb logcat | grep statsd | grep "(48)" The line may contain 0x100000->1[I] 0x110000->10[I] that means 1=from launcher and 10=latency 10ms. Change-Id: Iddaff7066b66e241ba58ec87129ddbe2c531dc7e (cherry picked from commit 7bdf3574a3bff06a377b4364877687bfa7619d06) --- .../com/android/launcher3/BaseQuickstepLauncher.java | 12 ++++++++++++ src/com/android/launcher3/Launcher.java | 7 ++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index 54c2383be9..44d43c6784 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -26,10 +26,12 @@ import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import android.animation.AnimatorSet; import android.animation.ValueAnimator; +import android.app.ActivityOptions; import android.content.Intent; import android.content.IntentSender; import android.os.Bundle; import android.os.CancellationSignal; +import android.view.View; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WellbeingModel; @@ -51,6 +53,7 @@ import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.views.OverviewActionsView; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.ActivityOptionsCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.stream.Stream; @@ -306,6 +309,15 @@ public abstract class BaseQuickstepLauncher extends Launcher Stream.of(WellbeingModel.SHORTCUT_FACTORY)); } + @Override + public ActivityOptions getActivityLaunchOptions(View v) { + ActivityOptions activityOptions = super.getActivityLaunchOptions(v); + if (activityOptions != null && mLastTouchUpTime > 0) { + ActivityOptionsCompat.setLauncherSourceInfo(activityOptions, mLastTouchUpTime); + } + return activityOptions; + } + public void setHintUserWillBeActive() { addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 4b75a33fc1..ab8d7a5a69 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -77,6 +77,7 @@ import android.os.CancellationSignal; import android.os.Parcelable; import android.os.Process; import android.os.StrictMode; +import android.os.SystemClock; import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; @@ -350,7 +351,7 @@ public class Launcher extends StatefulActivity implements Launche private boolean mDeferOverlayCallbacks; private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred; - private long mLastTouchUpTime = -1; + protected long mLastTouchUpTime = -1; private boolean mTouchInProgress; private SafeCloseable mUserChangedCallbackCloseable; @@ -1828,7 +1829,7 @@ public class Launcher extends StatefulActivity implements Launche mTouchInProgress = true; break; case MotionEvent.ACTION_UP: - mLastTouchUpTime = System.currentTimeMillis(); + mLastTouchUpTime = ev.getEventTime(); // Follow through case MotionEvent.ACTION_CANCEL: mTouchInProgress = false; @@ -2461,7 +2462,7 @@ public class Launcher extends StatefulActivity implements Launche if (mDragController.isDragging()) { return false; } else { - return (System.currentTimeMillis() - mLastTouchUpTime) + return (SystemClock.uptimeMillis() - mLastTouchUpTime) > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); } } From 0471b9836c9e382dc14bdc3abdf8502fb2b9f266 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 23 Sep 2020 13:54:37 -0700 Subject: [PATCH 32/56] Using FrameCallbacks instead of windowCallbacks for surface removal WindowCallbacks is called during the draw pass, before the frame has been sent to the surfaceFlinger. Frame callback will provide a closer approximation for when the frame is actually rendered on screen. Bug: 141126144 Change-Id: I62aab526c2ca24b00b5e7b312b36080f26c7b439 --- .../android/quickstep/AbsSwipeUpHandler.java | 2 +- .../src/com/android/quickstep/ViewUtils.java | 86 ++++++++++++------- .../android/quickstep/views/RecentsView.java | 2 +- 3 files changed, 55 insertions(+), 35 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index dbf75fa154..678278e2b4 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1336,7 +1336,7 @@ public abstract class AbsSwipeUpHandler, Q extends if (taskView != null && !mCanceled) { // Defer finishing the animation until the next launcher frame with the // new thumbnail - finishTransitionPosted = ViewUtils.postDraw(taskView, + finishTransitionPosted = ViewUtils.postFrameDrawn(taskView, () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), this::isCanceled); } diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java index cbb6ad4188..184ab17ae2 100644 --- a/quickstep/src/com/android/quickstep/ViewUtils.java +++ b/quickstep/src/com/android/quickstep/ViewUtils.java @@ -15,21 +15,23 @@ */ package com.android.quickstep; -import android.graphics.Canvas; +import android.os.Handler; import android.view.View; -import com.android.systemui.shared.system.WindowCallbacksCompat; +import com.android.launcher3.Utilities; +import com.android.systemui.shared.system.ViewRootImplCompat; import java.util.function.BooleanSupplier; +import java.util.function.LongConsumer; /** * Utility class for helpful methods related to {@link View} objects. */ public class ViewUtils { - /** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */ - public static boolean postDraw(View view, Runnable onFinishRunnable) { - return postDraw(view, onFinishRunnable, () -> false); + /** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */ + public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) { + return postFrameDrawn(view, onFinishRunnable, () -> false); } /** @@ -38,37 +40,55 @@ public class ViewUtils { * * @param onFinishRunnable runnable to be run right after the view finishes drawing. */ - public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) { - // Defer finishing the animation until the next launcher frame with the - // new thumbnail - return new WindowCallbacksCompat(view) { - // The number of frames to defer until we actually finish the animation - private int mDeferFrameCount = 2; + public static boolean postFrameDrawn( + View view, Runnable onFinishRunnable, BooleanSupplier canceled) { + return new FrameHandler(view, onFinishRunnable, canceled).schedule(); + } - @Override - public void onPostDraw(Canvas canvas) { - // If we were cancelled after this was attached, do not update - // the state. - if (canceled.getAsBoolean()) { - detach(); - return; - } + private static class FrameHandler implements LongConsumer { - if (mDeferFrameCount > 0) { - mDeferFrameCount--; - // Workaround, detach and reattach to invalidate the root node for - // another draw - detach(); - attach(); - view.invalidate(); - return; - } + final ViewRootImplCompat mViewRoot; + final Runnable mFinishCallback; + final BooleanSupplier mCancelled; + final Handler mHandler; - if (onFinishRunnable != null) { - onFinishRunnable.run(); - } - detach(); + int mDeferFrameCount = 1; + + FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) { + mViewRoot = new ViewRootImplCompat(view); + mFinishCallback = finishCallback; + mCancelled = cancelled; + mHandler = new Handler(); + } + + @Override + public void accept(long l) { + Utilities.postAsyncCallback(mHandler, this::onFrame); + } + + private void onFrame() { + if (mCancelled.getAsBoolean()) { + return; } - }.attach(); + + if (mDeferFrameCount > 0) { + mDeferFrameCount--; + schedule(); + return; + } + + if (mFinishCallback != null) { + mFinishCallback.run(); + } + } + + private boolean schedule() { + if (mViewRoot.isValid()) { + mViewRoot.registerRtFrameCallback(this); + mViewRoot.getView().invalidate(); + return true; + } + return false; + } } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 7a8e11df78..47abf04f5e 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2442,7 +2442,7 @@ public abstract class RecentsView extends PagedView } else { taskView.getThumbnail().refresh(); } - ViewUtils.postDraw(taskView, onFinishRunnable); + ViewUtils.postFrameDrawn(taskView, onFinishRunnable); } else { onFinishRunnable.run(); } From 2de606fe731573c081fd2d6ba166e21ea6aa2e9c Mon Sep 17 00:00:00 2001 From: Yogisha Dixit Date: Mon, 12 Oct 2020 15:36:07 +0100 Subject: [PATCH 33/56] Delete the minimal database to force refresh. Bug: 169771796 Test: manual Change-Id: Ic2188bb162f295c208346861fddc137ace19ddcb --- .../src/com/android/launcher3/model/WellbeingModel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index a9fc1aa085..995c4b04d4 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -148,6 +148,12 @@ public final class WellbeingModel extends BgObjectWithLooper { if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) { return; } + + // Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when + // minimal device is enabled. We always want to reload the configuration from Wellbeing + // since the layout configuration might have changed. + mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE); + final Bundle extras = new Bundle(); String dbFile; if (isInMinimalDeviceMode()) { From 9a6145efb85f2bbdaccc07166a55e22c15fe27db Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Mon, 12 Oct 2020 09:33:00 -0700 Subject: [PATCH 34/56] Introduce shortcut container for hotseat event reporting Bug: 170636685 Test: Manual Change-Id: I5abeb17976bbafdc8cc74fb8b9a586d544c682fc --- .../src/com/android/launcher3/model/AppEventProducer.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java index 364a321f75..58937c3350 100644 --- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java +++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java @@ -250,6 +250,9 @@ public class AppEventProducer implements StatsLogConsumer { case PREDICTION_CONTAINER: { return "predictions"; } + case SHORTCUTS_CONTAINER: { + return "shortcuts"; + } case FOLDER: { FolderContainer fc = ci.getFolder(); switch (fc.getParentContainerCase()) { From 2d7bfc8782e9ed01178672aeb09ba2a6a07f4f4c Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Mon, 12 Oct 2020 12:09:22 -0700 Subject: [PATCH 35/56] Fix shadowRadius not being used in swipe up animation. Bug: 168608912 Change-Id: I08f7bb057237e5061d5f1fc29afb488b204ee385 --- quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java index a46de1f601..7406dea486 100644 --- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java +++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java @@ -261,7 +261,7 @@ public abstract class SwipeUpAnimationLogic { mTransformParams .setTargetAlpha(getWindowAlpha(progress)) .setCornerRadius(cornerRadius) - .setShadowRadius(mMaxShadowRadius); + .setShadowRadius(shadowRadius); mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius)); From 2afcab804b638ff3b9da5bad40c8f70bdcaae78d Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Mon, 12 Oct 2020 15:38:14 -0700 Subject: [PATCH 36/56] Search UI clean up - Resolve spacing issue when work profile is installed - Cache play icons and use icon shape - Only draw focus indicator for the first result Bug: 170487752 Bug: 170665892 Change-Id: I864d2e796786637132e127ef9b418c0a76c74d6e --- res/layout/all_apps_tabs.xml | 1 - .../allapps/AllAppsContainerView.java | 4 --- .../launcher3/allapps/AllAppsPagedView.java | 5 +++ .../allapps/AllAppsSectionDecorator.java | 6 +++- .../launcher3/views/SearchResultPlayItem.java | 31 ++++++++++--------- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml index 2accd2d21f..c684881195 100644 --- a/res/layout/all_apps_tabs.xml +++ b/res/layout/all_apps_tabs.xml @@ -26,7 +26,6 @@ android:clipChildren="true" android:clipToPadding="false" android:descendantFocusability="afterDescendants" - android:paddingTop="@dimen/all_apps_header_top_padding" launcher:pageIndicator="@+id/tabs" > diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 5079469f4a..75ab00a7dd 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -585,10 +585,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo int padding = mHeader.getMaxTranslation(); for (int i = 0; i < mAH.length; i++) { mAH[i].padding.top = padding; - if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mUsingTabs) { - //add extra space between tabs and recycler view - mAH[i].padding.top += mLauncher.getDeviceProfile().edgeMarginPx; - } mAH[i].applyPadding(); } } diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java index f640c3e8d0..eae9c0a6b6 100644 --- a/src/com/android/launcher3/allapps/AllAppsPagedView.java +++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java @@ -20,6 +20,8 @@ import android.util.AttributeSet; import android.view.MotionEvent; import com.android.launcher3.PagedView; +import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; public class AllAppsPagedView extends PagedView { @@ -37,6 +39,9 @@ public class AllAppsPagedView extends PagedView { public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 + : context.getResources().getDimensionPixelOffset( + R.dimen.all_apps_header_top_padding); } @Override diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java index f2a1f852be..c131697c89 100644 --- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java +++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java @@ -26,6 +26,7 @@ import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.RecyclerView; import com.android.launcher3.R; +import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager; import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.util.Themes; @@ -90,7 +91,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration { if (mAppsView.getFloatingHeaderView().getFocusedChild() == null && mAppsView.getApps().getFocusedChild() != null) { int index = mAppsView.getApps().getFocusedChildIndex(); - if (index >= 0 && index < parent.getChildCount()) { + AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) + mAppsView.getActiveRecyclerView().getLayoutManager(); + if (layoutManager.findFirstVisibleItemPosition() == index + && index < parent.getChildCount()) { decorationHandler.onFocusDraw(c, parent.getChildAt(index)); } } diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java index d05a29eab9..c7133fd8d8 100644 --- a/src/com/android/launcher3/views/SearchResultPlayItem.java +++ b/src/com/android/launcher3/views/SearchResultPlayItem.java @@ -44,12 +44,14 @@ import com.android.launcher3.R; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.icons.BitmapRenderer; +import com.android.launcher3.util.Themes; import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; import java.io.IOException; import java.net.URL; +import java.net.URLConnection; /** * A View representing a PlayStore item. @@ -58,7 +60,6 @@ public class SearchResultPlayItem extends LinearLayout implements AllAppsSearchBarController.PayloadResultHandler { private static final int BITMAP_CROP_MASK_COLOR = 0xff424242; - private static final float ICON_RADIUS_FACTOR = .5f; private final DeviceProfile mDeviceProfile; private View mIconView; @@ -71,6 +72,7 @@ public class SearchResultPlayItem extends LinearLayout implements private final Object[] mTargetInfo = createTargetInfo(); final Paint mIconPaint = new Paint(); + final Rect mTempRect = new Rect(); public SearchResultPlayItem(Context context) { @@ -125,13 +127,15 @@ public class SearchResultPlayItem extends LinearLayout implements mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder); UI_HELPER_EXECUTOR.execute(() -> { try { -// TODO: Handle caching URL url = new URL(bundle.getString("icon_url")); - Bitmap bitmap = BitmapFactory.decodeStream(url.openStream()); - BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), - Bitmap.createScaledBitmap(getRoundedBitmap(bitmap), - mDeviceProfile.allAppsIconSizePx, mDeviceProfile.allAppsIconSizePx, - false)); + URLConnection con = url.openConnection(); +// TODO: monitor memory and investigate if it's better to use glide + con.addRequestProperty("Cache-Control", "max-age: 0"); + con.setUseCaches(true); + Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream()); + BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), getRoundedBitmap( + Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx, + mDeviceProfile.allAppsIconSizePx, false))); mIconView.post(() -> mIconView.setBackground(bitmapDrawable)); } catch (IOException e) { e.printStackTrace(); @@ -141,24 +145,23 @@ public class SearchResultPlayItem extends LinearLayout implements private Bitmap getRoundedBitmap(Bitmap bitmap) { - int iconSize = bitmap.getWidth(); + final int iconSize = bitmap.getWidth(); + final float radius = Themes.getDialogCornerRadius(getContext()); Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> { - final Rect rect = new Rect(0, 0, iconSize, iconSize); - final RectF rectF = new RectF(rect); + mTempRect.set(0, 0, iconSize, iconSize); + final RectF rectF = new RectF(mTempRect); mIconPaint.setAntiAlias(true); + mIconPaint.reset(); canvas.drawARGB(0, 0, 0, 0); mIconPaint.setColor(BITMAP_CROP_MASK_COLOR); - int radius = (int) (iconSize * ICON_RADIUS_FACTOR); canvas.drawRoundRect(rectF, radius, radius, mIconPaint); mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(bitmap, rect, rect, mIconPaint); + canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint); }); - return output; - } From 26c1105fa04c2bcc156051e51df90a6a253349bb Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Tue, 13 Oct 2020 01:12:03 -0700 Subject: [PATCH 37/56] [search api part 1] Setup centralized SearchEventTracker - Rename AdapterItemWIthPayload to SearchAdapterItem, PayloadResultHandler to SearchTargetHandler - Setup SliceViewWrapper for self contained slices Bug: 170702596 Change-Id: I0baf984ec8123c95011abcc17372f8d055e98ad7 --- .../allapps/AllAppsContainerView.java | 21 ++-- .../launcher3/allapps/AllAppsGridAdapter.java | 119 +++++++----------- .../launcher3/allapps/AllAppsPagedView.java | 9 +- .../search/AllAppsSearchBarController.java | 30 +++-- .../allapps/search/SearchEventTracker.java | 91 ++++++++++++++ .../launcher3/views/HeroSearchResultView.java | 73 ++++++----- .../launcher3/views/SearchResultIconRow.java | 38 ++---- .../views/SearchResultPeopleView.java | 55 ++++---- .../launcher3/views/SearchResultPlayItem.java | 72 +++++------ .../views/SearchSectionHeaderView.java | 8 +- .../views/SearchSettingsRowView.java | 21 ++-- .../launcher3/views/SearchSliceWrapper.java | 91 ++++++++++++++ .../views/ThumbnailSearchResultView.java | 28 ++--- 13 files changed, 393 insertions(+), 263 deletions(-) create mode 100644 src/com/android/launcher3/allapps/search/SearchEventTracker.java create mode 100644 src/com/android/launcher3/views/SearchSliceWrapper.java diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 75ab00a7dd..5d5e017372 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -16,7 +16,7 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; -import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; +import static com.android.launcher3.allapps.AllAppsGridAdapter.SearchAdapterItem; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; @@ -57,6 +57,7 @@ import com.android.launcher3.Insettable; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.model.data.AppInfo; @@ -67,9 +68,7 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.Themes; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.SpringRelativeLayout; -import com.android.systemui.plugins.shared.SearchTargetEvent; - -import java.util.function.IntConsumer; +import com.android.systemui.plugins.shared.SearchTarget; /** * The all apps view container. @@ -546,13 +545,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem); } AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild(); - if (focusedItem instanceof AdapterItemWithPayload) { - IntConsumer onSelection = - ((AdapterItemWithPayload) focusedItem).getSelectionHandler(); - if (onSelection != null) { - onSelection.accept(SearchTargetEvent.QUICK_SELECT); - return true; - } + if (focusedItem instanceof SearchAdapterItem) { + SearchTarget searchTarget = ((SearchAdapterItem) focusedItem).getSearchTarget(); + SearchEventTracker.INSTANCE.get(getContext()).quickSelect(searchTarget); } if (focusedItem.appInfo != null) { ItemInfo itemInfo = focusedItem.appInfo; @@ -585,6 +580,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo int padding = mHeader.getMaxTranslation(); for (int i = 0; i < mAH.length; i++) { mAH[i].padding.top = padding; + if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mUsingTabs) { + //add extra space between tabs and recycler view + mAH[i].padding.top += mLauncher.getDeviceProfile().edgeMarginPx; + } mAH[i].applyPadding(); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 8bc8e53af3..603e9df16c 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -20,8 +20,6 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.net.Uri; -import android.os.Bundle; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -37,29 +35,25 @@ import androidx.annotation.Nullable; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; -import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import androidx.slice.Slice; -import androidx.slice.widget.SliceLiveData; import androidx.slice.widget.SliceView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; -import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; +import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.views.HeroSearchResultView; -import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.launcher3.views.SearchSliceWrapper; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; import java.util.List; -import java.util.function.IntConsumer; /** * The grid view adapter of all the apps. @@ -201,28 +195,13 @@ public class AllAppsGridAdapter extends * * @param Play load Type */ - public static class AdapterItemWithPayload extends AdapterItem { - private T mPayload; + public static class SearchAdapterItem extends AdapterItem { + private SearchTarget mSearchTarget; private String mSearchSessionId; - private AllAppsSearchPlugin mPlugin; - private IntConsumer mSelectionHandler; - public AllAppsSearchPlugin getPlugin() { - return mPlugin; - } - - public void setPlugin(AllAppsSearchPlugin plugin) { - mPlugin = plugin; - } - - public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) { - mPayload = payload; + public SearchAdapterItem(SearchTarget searchTarget, int type) { + mSearchTarget = searchTarget; viewType = type; - mPlugin = plugin; - } - - public void setSelectionHandler(IntConsumer runnable) { - mSelectionHandler = runnable; } public void setSearchSessionId(String searchSessionId) { @@ -233,15 +212,9 @@ public class AllAppsGridAdapter extends return mSearchSessionId; } - public IntConsumer getSelectionHandler() { - return mSelectionHandler; + public SearchTarget getSearchTarget() { + return mSearchTarget; } - - public T getPayload() { - return mPayload; - } - - } /** @@ -492,26 +465,35 @@ public class AllAppsGridAdapter extends } //TODO: replace with custom TopHitBubbleTextView with support for both shortcut // and apps - if (adapterItem instanceof AdapterItemWithPayload) { - AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem; - item.setSelectionHandler(type -> { - SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP, - type, item.position, item.getSearchSessionId()); - e.bundle = HeroSearchResultView.getAppBundle(info); - if (item.getPlugin() != null) { - item.getPlugin().notifySearchTargetEvent(e); + if (adapterItem instanceof SearchAdapterItem) { + SearchAdapterItem item = (SearchAdapterItem) adapterItem; + SearchTargetHandler searchTargetHandler = new SearchTargetHandler() { + @Override + public void applySearchTarget(SearchTarget searchTarget) { + // Does nothing } - }); + + @Override + public void handleSelection(int type) { + SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP, + type, item.position, item.getSearchSessionId()); + e.bundle = HeroSearchResultView.getAppBundle(info); + SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(e); + } + }; + SearchEventTracker.INSTANCE.get(mLauncher).registerWeakHandler( + ((SearchAdapterItem) adapterItem).getSearchTarget(), + searchTargetHandler); + icon.setOnClickListener(view -> { - item.getSelectionHandler().accept(SearchTargetEvent.SELECT); + searchTargetHandler.handleSelection(SearchTargetEvent.SELECT); mOnIconClickListener.onClick(view); }); icon.setOnLongClickListener(view -> { - item.getSelectionHandler().accept(SearchTargetEvent.SELECT); + searchTargetHandler.handleSelection(SearchTargetEvent.LONG_PRESS); return mOnIconLongClickListener.onLongClick(view); }); - } - else { + } else { icon.setOnClickListener(mOnIconClickListener); icon.setOnLongClickListener(mOnIconLongClickListener); } @@ -532,26 +514,12 @@ public class AllAppsGridAdapter extends break; case VIEW_TYPE_SEARCH_SLICE: SliceView sliceView = (SliceView) holder.itemView; - AdapterItemWithPayload slicePayload = - (AdapterItemWithPayload) mApps.getAdapterItems().get(position); - sliceView.setOnSliceActionListener((info1, s) -> { - if (slicePayload.getPlugin() != null) { - SearchTargetEvent searchTargetEvent = new SearchTargetEvent( - SearchTarget.ItemType.SETTINGS_SLICE, - SearchTargetEvent.CHILD_SELECT, slicePayload.position, - slicePayload.getSearchSessionId()); - searchTargetEvent.bundle = new Bundle(); - searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload()); - slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent); - } - }); - try { - LiveData liveData = SliceLiveData.fromUri(mLauncher, - slicePayload.getPayload()); - liveData.observe((Launcher) mLauncher, sliceView); - sliceView.setTag(liveData); - } catch (Exception ignored) { - } + SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get( + position); + SearchTarget searchTarget = slicePayload.getSearchTarget(); + sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget, + slicePayload.getSearchSessionId(), slicePayload.position)); + break; case VIEW_TYPE_SEARCH_CORPUS_TITLE: case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON: @@ -561,9 +529,9 @@ public class AllAppsGridAdapter extends case VIEW_TYPE_SEARCH_PEOPLE: case VIEW_TYPE_SEARCH_THUMBNAIL: case VIEW_TYPE_SEARCH_SUGGEST: - AdapterItemWithPayload item = - (AdapterItemWithPayload) mApps.getAdapterItems().get(position); - PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView; + SearchAdapterItem item = + (SearchAdapterItem) mApps.getAdapterItems().get(position); + SearchTargetHandler payloadResultView = (SearchTargetHandler) holder.itemView; payloadResultView.setup(item); break; case VIEW_TYPE_ALL_APPS_DIVIDER: @@ -582,11 +550,10 @@ public class AllAppsGridAdapter extends icon.setOnLongClickListener(null); } else if (holder.itemView instanceof SliceView) { SliceView sliceView = (SliceView) holder.itemView; - sliceView.setOnSliceActionListener(null); - if (sliceView.getTag() instanceof LiveData) { - LiveData sliceLiveData = (LiveData) sliceView.getTag(); - sliceLiveData.removeObservers((Launcher) mLauncher); + if (sliceView.getTag() instanceof SearchSliceWrapper) { + ((SearchSliceWrapper) sliceView.getTag()).destroy(); } + sliceView.setTag(null); } } diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java index eae9c0a6b6..e2550f597a 100644 --- a/src/com/android/launcher3/allapps/AllAppsPagedView.java +++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java @@ -25,11 +25,11 @@ import com.android.launcher3.config.FeatureFlags; public class AllAppsPagedView extends PagedView { - final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6; - final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3; - final static float TOUCH_SLOP_DAMPING_FACTOR = 4; + static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6; + static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3; + static final float TOUCH_SLOP_DAMPING_FACTOR = 4; - public AllAppsPagedView(Context context) { + public AllAppsPagedView(Context context) { this(context, null); } @@ -42,6 +42,7 @@ public class AllAppsPagedView extends PagedView { int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : context.getResources().getDimensionPixelOffset( R.dimen.all_apps_header_top_padding); + setPadding(0, topPadding, 0, 0); } @Override diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java index 82c4db4da3..6ba0421d57 100644 --- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java +++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java @@ -31,7 +31,7 @@ import com.android.launcher3.ExtendedEditText; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsGridAdapter; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; +import com.android.launcher3.allapps.AllAppsGridAdapter.SearchAdapterItem; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.util.PackageManagerHelper; import com.android.systemui.plugins.AllAppsSearchPlugin; @@ -211,26 +211,31 @@ public class AllAppsSearchBarController * * @param Type of payload */ - public interface PayloadResultHandler { + public interface SearchTargetHandler { /** * Updates View using Adapter's payload */ - default void setup(AdapterItemWithPayload adapterItemWithPayload) { + default void setup(SearchAdapterItem searchAdapterItem) { Object[] targetInfo = getTargetInfo(); if (targetInfo != null) { - targetInfo[0] = adapterItemWithPayload.getSearchSessionId(); - targetInfo[1] = adapterItemWithPayload.position; + targetInfo[0] = searchAdapterItem.getSearchSessionId(); + targetInfo[1] = searchAdapterItem.position; } - applyAdapterInfo(adapterItemWithPayload); + applySearchTarget(searchAdapterItem.getSearchTarget()); } - void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload); + /** + * Update view using values from {@link SearchTarget} + */ + void applySearchTarget(SearchTarget searchTarget); /** - * Gets object created by {@link PayloadResultHandler#createTargetInfo()} + * Gets object created by {@link SearchTargetHandler#createTargetInfo()} */ - Object[] getTargetInfo(); + default Object[] getTargetInfo() { + return null; + } /** * Creates a wrapper object to hold searchSessionId and item position @@ -252,6 +257,13 @@ public class AllAppsSearchBarController return new SearchTargetEvent(itemType, eventType, position, searchSessionId); } + + /** + * Handles selection of SearchTarget + */ + default void handleSelection(int eventType) { + } + } diff --git a/src/com/android/launcher3/allapps/search/SearchEventTracker.java b/src/com/android/launcher3/allapps/search/SearchEventTracker.java new file mode 100644 index 0000000000..6bcde6c53b --- /dev/null +++ b/src/com/android/launcher3/allapps/search/SearchEventTracker.java @@ -0,0 +1,91 @@ +/* + * 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.allapps.search; + +import android.content.Context; + +import androidx.annotation.Nullable; + +import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler; +import com.android.launcher3.util.MainThreadInitializedObject; +import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; + +import java.util.WeakHashMap; + +/** + * A singleton class to track and report search events to search provider + */ +public class SearchEventTracker { + @Nullable + private AllAppsSearchPlugin mPlugin; + private final WeakHashMap + mCallbacks = new WeakHashMap<>(); + + public static final MainThreadInitializedObject INSTANCE = + new MainThreadInitializedObject<>(SearchEventTracker::new); + + private SearchEventTracker(Context context) { + } + + /** + * Returns instance of SearchEventTracker + */ + public static SearchEventTracker getInstance(Context context) { + return SearchEventTracker.INSTANCE.get(context); + } + + /** + * Sets current connected plugin for event reporting + */ + public void setPlugin(@Nullable AllAppsSearchPlugin plugin) { + mPlugin = plugin; + } + + /** + * Sends SearchTargetEvent to search provider + */ + public void notifySearchTargetEvent(SearchTargetEvent searchTargetEvent) { + if (mPlugin != null) { + mPlugin.notifySearchTargetEvent(searchTargetEvent); + } + } + + /** + * Registers a {@link SearchTargetHandler} to handle quick launch for specified SearchTarget. + */ + public void registerWeakHandler(SearchTarget searchTarget, SearchTargetHandler targetHandler) { + mCallbacks.put(searchTarget, targetHandler); + } + + /** + * Handles quick select for SearchTarget + */ + public void quickSelect(SearchTarget searchTarget) { + SearchTargetHandler searchTargetHandler = mCallbacks.get(searchTarget); + if (searchTargetHandler != null) { + searchTargetHandler.handleSelection(SearchTargetEvent.QUICK_SELECT); + } + } + + /** + * flushes all registered quick select handlers + */ + public void clearHandlers() { + mCallbacks.clear(); + } +} diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java index 91337ba824..9e56e00d60 100644 --- a/src/com/android/launcher3/views/HeroSearchResultView.java +++ b/src/com/android/launcher3/views/HeroSearchResultView.java @@ -19,11 +19,13 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APP import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.graphics.Point; import android.os.Bundle; +import android.os.UserHandle; import android.util.AttributeSet; import android.util.Pair; import android.view.View; @@ -37,8 +39,9 @@ import com.android.launcher3.DropTarget; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; -import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; +import com.android.launcher3.allapps.AllAppsStore; +import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.graphics.DragPreviewProvider; @@ -48,24 +51,25 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.touch.ItemLongClickListener; -import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.launcher3.util.ComponentKey; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; -import java.util.List; +import java.util.ArrayList; /** * A view representing a high confidence app search result that includes shortcuts */ -public class HeroSearchResultView extends LinearLayout implements DragSource, - PayloadResultHandler>> { +public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetHandler { public static final int MAX_SHORTCUTS_COUNT = 2; + public static final String SHORTCUTS_KEY = "shortcut_infos"; + + private final Object[] mTargetInfo = createTargetInfo(); BubbleTextView mBubbleTextView; View mIconView; BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2]; - AllAppsSearchPlugin mPlugin; public HeroSearchResultView(Context context) { super(context); @@ -111,30 +115,35 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetEvent.CHILD_SELECT); event.bundle = getAppBundle(itemInfo); event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId()); - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(event); - } launcher.getItemOnClickListener().onClick(view); }); } } - /** - * Apply {@link ItemInfo} for appIcon and shortcut Icons - */ @Override - public void applyAdapterInfo( - AdapterItemWithPayload>> adapterItem) { - mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo); + public void applySearchTarget(SearchTarget searchTarget) { + AppInfo appInfo = getAppInfo(searchTarget.bundle); +// TODO: replace this with searchTarget.shortcuts + ArrayList infos = searchTarget.bundle.getParcelableArrayList( + SHORTCUTS_KEY); + + ArrayList> shortcuts = new ArrayList<>(); + for (int i = 0; infos != null && i < infos.size() && i < MAX_SHORTCUTS_COUNT; i++) { + ShortcutInfo shortcutInfo = infos.get(i); + ItemInfoWithIcon si = new WorkspaceItemInfo(shortcutInfo, getContext()); + shortcuts.add(new Pair<>(shortcutInfo, si)); + } + + + mBubbleTextView.applyFromApplicationInfo(appInfo); mIconView.setBackground(mBubbleTextView.getIcon()); - mIconView.setTag(adapterItem.appInfo); - List> shortcutDetails = adapterItem.getPayload(); + mIconView.setTag(appInfo); LauncherAppState appState = LauncherAppState.getInstance(getContext()); for (int i = 0; i < mDeepShortcutTextViews.length; i++) { BubbleTextView shortcutView = mDeepShortcutTextViews[i]; - mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE); - if (i < shortcutDetails.size()) { - Pair p = shortcutDetails.get(i); + mDeepShortcutTextViews[i].setVisibility(shortcuts.size() > i ? VISIBLE : GONE); + if (i < shortcuts.size()) { + Pair p = shortcuts.get(i); //apply ItemInfo and prepare view shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second); MODEL_EXECUTOR.execute(() -> { @@ -144,8 +153,14 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, }); } } - mPlugin = adapterItem.getPlugin(); - adapterItem.setSelectionHandler(this::handleSelection); + SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this); + } + + private AppInfo getAppInfo(Bundle bundle) { + AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore(); + ComponentName cn = bundle.getParcelable("component_name"); + UserHandle userHandle = bundle.getParcelable("user_handle"); + return (cn != null) ? apps.getApp(new ComponentKey(cn, userHandle)) : null; } @Override @@ -191,15 +206,13 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetEvent event = mContainer.getSearchTargetEvent( SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS); event.bundle = getAppBundle(itemInfo); - if (mContainer.mPlugin != null) { - mContainer.mPlugin.notifySearchTargetEvent(event); - } - + SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event); return false; } } - private void handleSelection(int eventType) { + @Override + public void handleSelection(int eventType) { ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag(); if (itemInfo == null) return; Launcher launcher = Launcher.getLauncher(getContext()); @@ -208,9 +221,7 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetEvent event = getSearchTargetEvent( SearchTarget.ItemType.APP_HERO, eventType); event.bundle = getAppBundle(itemInfo); - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(event); - } + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(event); } /** diff --git a/src/com/android/launcher3/views/SearchResultIconRow.java b/src/com/android/launcher3/views/SearchResultIconRow.java index 6d9c86a9a7..ddeefaff3a 100644 --- a/src/com/android/launcher3/views/SearchResultIconRow.java +++ b/src/com/android/launcher3/views/SearchResultIconRow.java @@ -35,8 +35,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.data.ItemInfo; @@ -44,7 +44,6 @@ import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.RemoteActionItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.touch.ItemClickHandler; -import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget.ItemType; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -53,15 +52,13 @@ import com.android.systemui.plugins.shared.SearchTargetEvent; * A view representing a stand alone shortcut search result */ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements - AllAppsSearchBarController.PayloadResultHandler { + AllAppsSearchBarController.SearchTargetHandler { private final Object[] mTargetInfo = createTargetInfo(); private final int mCustomIconResId; private final boolean mMatchesInset; private ShortcutInfo mShortcutInfo; - private AllAppsSearchPlugin mPlugin; - private AdapterItemWithPayload mAdapterItem; public SearchResultIconRow(@NonNull Context context) { @@ -100,26 +97,18 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements } } - @Override - public void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload) { - if (mAdapterItem != null) { - mAdapterItem.setSelectionHandler(null); - } - mAdapterItem = adapterItemWithPayload; - SearchTarget payload = adapterItemWithPayload.getPayload(); - mPlugin = adapterItemWithPayload.getPlugin(); - - if (payload.mRemoteAction != null) { - prepareUsingRemoteAction(payload.mRemoteAction, - payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), - payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START), - payload.type == ItemType.ACTION); + public void applySearchTarget(SearchTarget searchTarget) { + if (searchTarget.mRemoteAction != null) { + prepareUsingRemoteAction(searchTarget.mRemoteAction, + searchTarget.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), + searchTarget.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START), + searchTarget.type == ItemType.ACTION); } else { - prepareUsingShortcutInfo(payload.shortcuts.get(0)); + prepareUsingShortcutInfo(searchTarget.shortcuts.get(0)); } setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); - adapterItemWithPayload.setSelectionHandler(this::handleSelection); + SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this); } private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) { @@ -179,7 +168,8 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements return mTargetInfo; } - private void handleSelection(int eventType) { + @Override + public void handleSelection(int eventType) { ItemInfo itemInfo = (ItemInfo) getTag(); Launcher launcher = Launcher.getLauncher(getContext()); final SearchTargetEvent searchTargetEvent; @@ -200,8 +190,6 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN, remoteItemInfo.getToken()); } - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(searchTargetEvent); - } + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(searchTargetEvent); } } diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java index f20b08016f..18bc99a3f9 100644 --- a/src/com/android/launcher3/views/SearchResultPeopleView.java +++ b/src/com/android/launcher3/views/SearchResultPeopleView.java @@ -42,11 +42,10 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.LauncherIcons; -import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -56,7 +55,7 @@ import java.util.ArrayList; * A view representing a single people search result in all apps */ public class SearchResultPeopleView extends LinearLayout implements - AllAppsSearchBarController.PayloadResultHandler { + AllAppsSearchBarController.SearchTargetHandler { private final int mIconSize; private final int mButtonSize; @@ -64,7 +63,6 @@ public class SearchResultPeopleView extends LinearLayout implements private View mIconView; private TextView mTitleView; private ImageButton[] mProviderButtons = new ImageButton[3]; - private AllAppsSearchPlugin mPlugin; private Intent mIntent; private final Object[] mTargetInfo = createTargetInfo(); @@ -103,10 +101,8 @@ public class SearchResultPeopleView extends LinearLayout implements } @Override - public void applyAdapterInfo( - AllAppsGridAdapter.AdapterItemWithPayload adapterItemWithPayload) { - Bundle payload = adapterItemWithPayload.getPayload(); - mPlugin = adapterItemWithPayload.getPlugin(); + public void applySearchTarget(SearchTarget searchTarget) { + Bundle payload = searchTarget.bundle; mTitleView.setText(payload.getString("title")); mIntent = payload.getParcelable("intent"); Bitmap contactIcon = payload.getParcelable("icon"); @@ -125,7 +121,7 @@ public class SearchResultPeopleView extends LinearLayout implements if (providers != null && i < providers.size()) { Bundle provider = providers.get(i); Intent intent = provider.getParcelable("intent"); - setupProviderButton(button, provider, intent, adapterItemWithPayload); + setupProviderButton(button, provider, intent); UI_HELPER_EXECUTOR.post(() -> { String pkg = provider.getString("package_name"); Drawable appIcon = getAppIcon(pkg); @@ -138,13 +134,13 @@ public class SearchResultPeopleView extends LinearLayout implements button.setVisibility(GONE); } } - adapterItemWithPayload.setSelectionHandler(this::handleSelection); + SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this); } /** - * Normalizes the bitmap to look like rounded App Icon - * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate - * bitmap from it. + * Normalizes the bitmap to look like rounded App Icon + * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate + * bitmap from it. */ private Bitmap roundBitmap(Bitmap icon) { final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon); @@ -185,37 +181,32 @@ public class SearchResultPeopleView extends LinearLayout implements return mTargetInfo; } - private void setupProviderButton(ImageButton button, Bundle provider, Intent intent, - AllAppsGridAdapter.AdapterItem adapterItem) { + private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) { Launcher launcher = Launcher.getLauncher(getContext()); button.setOnClickListener(b -> { launcher.startActivitySafely(b, intent, null); - SearchTargetEvent searchTargetEvent = getSearchTargetEvent( + SearchTargetEvent event = getSearchTargetEvent( SearchTarget.ItemType.PEOPLE, SearchTargetEvent.CHILD_SELECT); - searchTargetEvent.bundle = new Bundle(); - searchTargetEvent.bundle.putParcelable("intent", intent); - searchTargetEvent.bundle.putString("title", mTitleView.getText().toString()); - searchTargetEvent.bundle.putBundle("provider", provider); - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(searchTargetEvent); - } + event.bundle = new Bundle(); + event.bundle.putParcelable("intent", intent); + event.bundle.putString("title", mTitleView.getText().toString()); + event.bundle.putBundle("provider", provider); + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(event); }); } - - private void handleSelection(int eventType) { + @Override + public void handleSelection(int eventType) { if (mIntent != null) { Launcher launcher = Launcher.getLauncher(getContext()); launcher.startActivitySafely(this, mIntent, null); - SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE, + SearchTargetEvent event = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE, eventType); - searchTargetEvent.bundle = new Bundle(); - searchTargetEvent.bundle.putParcelable("intent", mIntent); - searchTargetEvent.bundle.putString("title", mTitleView.getText().toString()); - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(searchTargetEvent); - } + event.bundle = new Bundle(); + event.bundle.putParcelable("intent", mIntent); + event.bundle.putString("title", mTitleView.getText().toString()); + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(event); } } } diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java index c7133fd8d8..39a83045e4 100644 --- a/src/com/android/launcher3/views/SearchResultPlayItem.java +++ b/src/com/android/launcher3/views/SearchResultPlayItem.java @@ -41,11 +41,10 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.icons.BitmapRenderer; import com.android.launcher3.util.Themes; -import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -57,22 +56,19 @@ import java.net.URLConnection; * A View representing a PlayStore item. */ public class SearchResultPlayItem extends LinearLayout implements - AllAppsSearchBarController.PayloadResultHandler { + AllAppsSearchBarController.SearchTargetHandler { private static final int BITMAP_CROP_MASK_COLOR = 0xff424242; - + final Paint mIconPaint = new Paint(); + final Rect mTempRect = new Rect(); private final DeviceProfile mDeviceProfile; + private final Object[] mTargetInfo = createTargetInfo(); private View mIconView; private TextView mTitleView; private TextView[] mDetailViews = new TextView[3]; private Button mPreviewButton; private String mPackageName; private boolean mIsInstantGame; - private AllAppsSearchPlugin mPlugin; - private final Object[] mTargetInfo = createTargetInfo(); - - final Paint mIconPaint = new Paint(); - final Rect mTempRect = new Rect(); public SearchResultPlayItem(Context context) { @@ -108,11 +104,32 @@ public class SearchResultPlayItem extends LinearLayout implements } + + private Bitmap getRoundedBitmap(Bitmap bitmap) { + final int iconSize = bitmap.getWidth(); + final float radius = Themes.getDialogCornerRadius(getContext()); + + Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> { + mTempRect.set(0, 0, iconSize, iconSize); + final RectF rectF = new RectF(mTempRect); + + mIconPaint.setAntiAlias(true); + mIconPaint.reset(); + canvas.drawARGB(0, 0, 0, 0); + mIconPaint.setColor(BITMAP_CROP_MASK_COLOR); + canvas.drawRoundRect(rectF, radius, radius, mIconPaint); + + mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint); + }); + return output; + } + + @Override - public void applyAdapterInfo(AdapterItemWithPayload adapterItemWithPayload) { - Bundle bundle = adapterItemWithPayload.getPayload(); - mPlugin = adapterItemWithPayload.getPlugin(); - adapterItemWithPayload.setSelectionHandler(this::handleSelection); + public void applySearchTarget(SearchTarget searchTarget) { + Bundle bundle = searchTarget.bundle; + SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this); if (bundle.getString("package", "").equals(mPackageName)) { return; } @@ -143,28 +160,6 @@ public class SearchResultPlayItem extends LinearLayout implements }); } - - private Bitmap getRoundedBitmap(Bitmap bitmap) { - final int iconSize = bitmap.getWidth(); - final float radius = Themes.getDialogCornerRadius(getContext()); - - Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> { - mTempRect.set(0, 0, iconSize, iconSize); - final RectF rectF = new RectF(mTempRect); - - mIconPaint.setAntiAlias(true); - mIconPaint.reset(); - canvas.drawARGB(0, 0, 0, 0); - mIconPaint.setColor(BITMAP_CROP_MASK_COLOR); - canvas.drawRoundRect(rectF, radius, radius, mIconPaint); - - mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint); - }); - return output; - } - - @Override public Object[] getTargetInfo() { return mTargetInfo; @@ -179,7 +174,8 @@ public class SearchResultPlayItem extends LinearLayout implements } } - private void handleSelection(int eventType) { + @Override + public void handleSelection(int eventType) { if (mPackageName == null) return; Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse( "https://play.google.com/store/apps/details?id=" @@ -209,8 +205,6 @@ public class SearchResultPlayItem extends LinearLayout implements SearchTarget.ItemType.PLAY_RESULTS, eventType); searchTargetEvent.bundle = new Bundle(); searchTargetEvent.bundle.putString("package_name", mPackageName); - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(searchTargetEvent); - } + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(searchTargetEvent); } } diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java index 0fe0a43ff5..370b92148c 100644 --- a/src/com/android/launcher3/views/SearchSectionHeaderView.java +++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java @@ -21,14 +21,14 @@ import android.widget.TextView; import androidx.annotation.Nullable; -import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.systemui.plugins.shared.SearchTarget; /** * Header text view that shows a title for a given section in All apps search */ public class SearchSectionHeaderView extends TextView implements - AllAppsSearchBarController.PayloadResultHandler { + AllAppsSearchBarController.SearchTargetHandler { public SearchSectionHeaderView(Context context) { super(context); } @@ -43,8 +43,8 @@ public class SearchSectionHeaderView extends TextView implements } @Override - public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload adapterItem) { - String title = adapterItem.getPayload(); + public void applySearchTarget(SearchTarget searchTarget) { + String title = searchTarget.type.getTitle(); if (title == null || !title.isEmpty()) { setText(title); setVisibility(VISIBLE); diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java index a1a0172547..ac69548840 100644 --- a/src/com/android/launcher3/views/SearchSettingsRowView.java +++ b/src/com/android/launcher3/views/SearchSettingsRowView.java @@ -29,9 +29,8 @@ import androidx.annotation.Nullable; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.search.AllAppsSearchBarController; -import com.android.systemui.plugins.AllAppsSearchPlugin; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -41,13 +40,12 @@ import java.util.ArrayList; * A row of tappable TextViews with a breadcrumb for settings search. */ public class SearchSettingsRowView extends LinearLayout implements - View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler { + View.OnClickListener, AllAppsSearchBarController.SearchTargetHandler { private TextView mTitleView; private TextView mDescriptionView; private TextView mBreadcrumbsView; private Intent mIntent; - private AllAppsSearchPlugin mPlugin; private final Object[] mTargetInfo = createTargetInfo(); @@ -75,10 +73,8 @@ public class SearchSettingsRowView extends LinearLayout implements } @Override - public void applyAdapterInfo( - AllAppsGridAdapter.AdapterItemWithPayload adapterItemWithPayload) { - Bundle bundle = adapterItemWithPayload.getPayload(); - mPlugin = adapterItemWithPayload.getPlugin(); + public void applySearchTarget(SearchTarget searchTarget) { + Bundle bundle = searchTarget.bundle; mIntent = bundle.getParcelable("intent"); showIfAvailable(mTitleView, bundle.getString("title")); showIfAvailable(mDescriptionView, bundle.getString("description")); @@ -86,7 +82,7 @@ public class SearchSettingsRowView extends LinearLayout implements //TODO: implement RTL friendly breadcrumbs view showIfAvailable(mBreadcrumbsView, breadcrumbs != null ? String.join(" > ", breadcrumbs) : null); - adapterItemWithPayload.setSelectionHandler(this::handleSelection); + SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this); } @Override @@ -108,7 +104,8 @@ public class SearchSettingsRowView extends LinearLayout implements handleSelection(SearchTargetEvent.SELECT); } - private void handleSelection(int eventType) { + @Override + public void handleSelection(int eventType) { if (mIntent == null) return; // TODO: create ItemInfo object and then use it to call startActivityForResult for proper // WW logging @@ -119,8 +116,6 @@ public class SearchSettingsRowView extends LinearLayout implements SearchTarget.ItemType.SETTINGS_ROW, eventType); searchTargetEvent.bundle = new Bundle(); searchTargetEvent.bundle.putParcelable("intent", mIntent); - if (mPlugin != null) { - mPlugin.notifySearchTargetEvent(searchTargetEvent); - } + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(searchTargetEvent); } } diff --git a/src/com/android/launcher3/views/SearchSliceWrapper.java b/src/com/android/launcher3/views/SearchSliceWrapper.java new file mode 100644 index 0000000000..b088237034 --- /dev/null +++ b/src/com/android/launcher3/views/SearchSliceWrapper.java @@ -0,0 +1,91 @@ +/* + * 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.launcher3.views; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.widget.EventInfo; +import androidx.slice.widget.SliceLiveData; +import androidx.slice.widget.SliceView; + +import com.android.launcher3.Launcher; +import com.android.launcher3.allapps.search.SearchEventTracker; +import com.android.systemui.plugins.shared.SearchTarget; +import com.android.systemui.plugins.shared.SearchTargetEvent; + +/** + * A Wrapper class for {@link SliceView} search results + */ +public class SearchSliceWrapper implements SliceView.OnSliceActionListener { + + private static final String TAG = "SearchSliceController"; + private static final String URI_EXTRA_KEY = "slice_uri"; + + + private final Launcher mLauncher; + private final SearchTarget mSearchTarget; + private final SliceView mSliceView; + //TODO: remove these as we move to tracking search results individually with unique ID + private final int mPosition; + private final String mSessionId; + private LiveData mSliceLiveData; + + public SearchSliceWrapper(Context context, SliceView sliceView, + SearchTarget searchTarget, String sessionId, int position) { + mLauncher = Launcher.getLauncher(context); + mPosition = position; + mSessionId = sessionId; + mSearchTarget = searchTarget; + mSliceView = sliceView; + sliceView.setOnSliceActionListener(this); + try { + mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri()); + mSliceLiveData.observe((Launcher) mLauncher, sliceView); + } catch (Exception ex) { + Log.e(TAG, "unable to bind slice", ex); + } + } + + /** + * Unregisters event handlers and removes lifecycle observer + */ + public void destroy() { + mSliceView.setOnSliceActionListener(null); + mSliceLiveData.removeObservers(mLauncher); + } + + @Override + public void onSliceAction(@NonNull EventInfo info, @NonNull SliceItem item) { + SearchTargetEvent searchTargetEvent = new SearchTargetEvent( + SearchTarget.ItemType.SETTINGS_SLICE, + SearchTargetEvent.CHILD_SELECT, mPosition, + mSessionId); + searchTargetEvent.bundle = new Bundle(); + searchTargetEvent.bundle.putParcelable(URI_EXTRA_KEY, getSliceUri()); + SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(searchTargetEvent); + } + + private Uri getSliceUri() { + return mSearchTarget.bundle.getParcelable(URI_EXTRA_KEY); + } +} diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java index 81bcad97ed..21212327b6 100644 --- a/src/com/android/launcher3/views/ThumbnailSearchResultView.java +++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java @@ -26,14 +26,13 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import com.android.launcher3.Launcher; -import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.search.AllAppsSearchBarController; +import com.android.launcher3.allapps.search.SearchEventTracker; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.RemoteActionItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.util.Themes; -import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTargetEvent; @@ -41,11 +40,9 @@ import com.android.systemui.plugins.shared.SearchTargetEvent; * A view representing a high confidence app search result that includes shortcuts */ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView - implements AllAppsSearchBarController.PayloadResultHandler { + implements AllAppsSearchBarController.SearchTargetHandler { private final Object[] mTargetInfo = createTargetInfo(); - AllAppsSearchPlugin mPlugin; - int mPosition; public ThumbnailSearchResultView(Context context) { super(context); @@ -59,7 +56,8 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp super(context, attrs, defStyleAttr); } - private void handleSelection(int eventType) { + @Override + public void handleSelection(int eventType) { Launcher launcher = Launcher.getLauncher(getContext()); ItemInfo itemInfo = (ItemInfo) getTag(); if (itemInfo instanceof RemoteActionItemInfo) { @@ -68,26 +66,19 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp } else { ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher); } - if (mPlugin != null) { - SearchTargetEvent event = getSearchTargetEvent( - SearchTarget.ItemType.SCREENSHOT, eventType); - mPlugin.notifySearchTargetEvent(event); - } + SearchTargetEvent e = getSearchTargetEvent(SearchTarget.ItemType.SCREENSHOT, eventType); + SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(e); } @Override - public void applyAdapterInfo(AdapterItemWithPayload adapterItem) { - Launcher launcher = Launcher.getLauncher(getContext()); - mPosition = adapterItem.position; - - SearchTarget target = adapterItem.getPayload(); + public void applySearchTarget(SearchTarget target) { Bitmap bitmap; if (target.mRemoteAction != null) { RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction, target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START)); bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon() - .loadDrawable(getContext())).getBitmap(); + .loadDrawable(getContext())).getBitmap(); Bitmap crop = Bitmap.createBitmap(bitmap, 0, bitmap.getHeight() / 2 - bitmap.getWidth() / 2, bitmap.getWidth(), bitmap.getWidth()); @@ -106,8 +97,7 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext())); setImageDrawable(drawable); setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); - mPlugin = adapterItem.getPlugin(); - adapterItem.setSelectionHandler(this::handleSelection); + SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(target, this); } @Override From d5bbe6809dcc056fbfc307909b171651f0fb3044 Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 14 Oct 2020 15:39:38 -0700 Subject: [PATCH 38/56] Rename shrotcut container to deep-shrotcuts Change-Id: If94f0dfa447235f3b1a652f7b6c749695b42d97c --- quickstep/src/com/android/launcher3/model/AppEventProducer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java index 58937c3350..9944270c95 100644 --- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java +++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java @@ -251,7 +251,7 @@ public class AppEventProducer implements StatsLogConsumer { return "predictions"; } case SHORTCUTS_CONTAINER: { - return "shortcuts"; + return "deep-shortcuts"; } case FOLDER: { FolderContainer fc = ci.getFolder(); From cf0b275a48d3c9f91a346f7fc24b9604f6dde25a Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Tue, 6 Oct 2020 09:33:40 -0400 Subject: [PATCH 39/56] Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent. Added tutorial_steps string array in the intent to allow specifying an ordered list of tutorial steps. Change-Id: Ic42a65598a74a64f8441a22f58c6cd988a5762e3 --- .../AssistantGestureTutorialController.java | 8 +- .../BackGestureTutorialController.java | 8 +- .../interaction/GestureSandboxActivity.java | 76 ++++++++++++++++--- .../HomeGestureTutorialController.java | 9 ++- .../OverviewGestureTutorialController.java | 9 ++- .../interaction/TutorialFragment.java | 24 ++++++ .../settings/DeveloperOptionsFragment.java | 19 +++-- 7 files changed, 131 insertions(+), 22 deletions(-) diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java index 6862f071bc..5c81e5f37b 100644 --- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java @@ -104,7 +104,13 @@ final class AssistantGestureTutorialController extends TutorialController { hideFeedback(); hideHandCoachingAnimation(); showRippleEffect( - () -> mTutorialFragment.changeController(ASSISTANT_COMPLETE)); + () -> { + if (mTutorialFragment.isTutorialComplete()) { + mTutorialFragment.changeController(ASSISTANT_COMPLETE); + } else { + mTutorialFragment.continueTutorial(); + } + }); break; case ASSISTANT_NOT_STARTED_BAD_ANGLE: showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal); diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java index 921e5681c6..161e015481 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java @@ -130,7 +130,13 @@ final class BackGestureTutorialController extends TutorialController { hideFeedback(); hideHandCoachingAnimation(); showRippleEffect( - () -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE)); + () -> { + if (mTutorialFragment.isTutorialComplete()) { + mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE); + } else { + mTutorialFragment.continueTutorial(); + } + }); break; case BACK_CANCELLED_FROM_LEFT: showFeedback(R.string.back_gesture_feedback_cancelled_left_edge); diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java index f8d9d8d502..8b6777b6ac 100644 --- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java @@ -15,8 +15,6 @@ */ package com.android.quickstep.interaction; -import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE; - import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; @@ -25,11 +23,14 @@ import android.view.Display; import android.view.View; import android.view.Window; +import androidx.annotation.NonNull; import androidx.fragment.app.FragmentActivity; import com.android.launcher3.R; import com.android.quickstep.interaction.TutorialController.TutorialType; +import java.util.ArrayDeque; +import java.util.Deque; import java.util.List; /** Shows the gesture interactive sandbox in full screen mode. */ @@ -37,6 +38,9 @@ public class GestureSandboxActivity extends FragmentActivity { private static final String LOG_TAG = "GestureSandboxActivity"; + private static final String KEY_TUTORIAL_STEPS = "tutorial_steps"; + + private Deque mTutorialSteps; private TutorialFragment mFragment; @Override @@ -45,7 +49,9 @@ public class GestureSandboxActivity extends FragmentActivity { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.gesture_tutorial_activity); - mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras())); + Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState; + mTutorialSteps = getTutorialSteps(args); + mFragment = TutorialFragment.newInstance(mTutorialSteps.pop()); getSupportFragmentManager().beginTransaction() .add(R.id.gesture_tutorial_fragment_container, mFragment) .commit(); @@ -72,17 +78,65 @@ public class GestureSandboxActivity extends FragmentActivity { } } - private TutorialType getTutorialType(Bundle extras) { - TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION; + @Override + protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) { + savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames()); + super.onSaveInstanceState(savedInstanceState); + } - if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) { - return defaultType; + /** Returns true iff there aren't anymore tutorial types to display to the user. */ + public boolean isTutorialComplete() { + return mTutorialSteps.isEmpty(); + } + + /** + * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one. + * + * If there is no following step, the tutorial is closed. + */ + public void continueTutorial() { + if (isTutorialComplete()) { + mFragment.closeTutorial(); + return; } - try { - return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, "")); - } catch (IllegalArgumentException e) { - return defaultType; + mFragment = TutorialFragment.newInstance(mTutorialSteps.pop()); + getSupportFragmentManager().beginTransaction() + .replace(R.id.gesture_tutorial_fragment_container, mFragment) + .runOnCommit(() -> mFragment.onAttachedToWindow()) + .commit(); + } + + private String[] getTutorialStepNames() { + String[] tutorialStepNames = new String[mTutorialSteps.size()]; + + int i = 0; + for (TutorialType tutorialStep : mTutorialSteps) { + tutorialStepNames[i++] = tutorialStep.name(); } + + return tutorialStepNames; + } + + private Deque getTutorialSteps(Bundle extras) { + Deque defaultSteps = new ArrayDeque<>(); + defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION); + + if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) { + return defaultSteps; + } + + String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS); + + if (tutorialStepNames == null) { + return defaultSteps; + } + + Deque tutorialSteps = new ArrayDeque<>(); + for (String tutorialStepName : tutorialStepNames) { + tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName)); + } + + return tutorialSteps; } private void hideSystemUI() { diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java index 0edabd45c5..95352d1a23 100644 --- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java @@ -94,8 +94,13 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll case HOME_NAVIGATION: switch (result) { case HOME_GESTURE_COMPLETED: { - animateFakeTaskViewHome(finalVelocity, () -> - mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE)); + animateFakeTaskViewHome(finalVelocity, () -> { + if (mTutorialFragment.isTutorialComplete()) { + mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE); + } else { + mTutorialFragment.continueTutorial(); + } + }); break; } case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java index c636ebaea9..45cbd0b571 100644 --- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java @@ -104,8 +104,13 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); break; case OVERVIEW_GESTURE_COMPLETED: - fadeOutFakeTaskView(true, () -> - mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE)); + fadeOutFakeTaskView(true, () -> { + if (mTutorialFragment.isTutorialComplete()) { + mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE); + } else { + mTutorialFragment.continueTutorial(); + } + }); break; case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: case HOME_OR_OVERVIEW_CANCELLED: diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java index f297d5a835..608fe72ff9 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java @@ -54,6 +54,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener { fragment = new BackGestureTutorialFragment(); tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION; } + Bundle args = new Bundle(); args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType); fragment.setArguments(args); @@ -197,6 +198,20 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener { return mHandCoachingAnimation; } + void continueTutorial() { + if (!(getContext() instanceof GestureSandboxActivity)) { + closeTutorial(); + return; + } + GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext(); + + if (gestureSandboxActivity == null) { + closeTutorial(); + return; + } + gestureSandboxActivity.continueTutorial(); + } + void closeTutorial() { FragmentActivity activity = getActivity(); if (activity != null) { @@ -207,4 +222,13 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener { void startSystemNavigationSetting() { startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS")); } + + boolean isTutorialComplete() { + if (!(getContext() instanceof GestureSandboxActivity)) { + return true; + } + GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext(); + + return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete(); + } } diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java index f4b059d8d8..47d214d0a8 100644 --- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java +++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java @@ -275,7 +275,8 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { launchBackTutorialPreference.setSummary("Learn how to use the Back gesture"); launchBackTutorialPreference.setOnPreferenceClickListener(preference -> { startActivity(launchSandboxIntent.putExtra( - "tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION")); + "tutorial_steps", + new String[] {"RIGHT_EDGE_BACK_NAVIGATION"})); return true; }); sandboxCategory.addPreference(launchBackTutorialPreference); @@ -284,7 +285,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { launchHomeTutorialPreference.setTitle("Launch Home Tutorial"); launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture"); launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> { - startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION")); + startActivity(launchSandboxIntent.putExtra( + "tutorial_steps", + new String[] {"HOME_NAVIGATION"})); return true; }); sandboxCategory.addPreference(launchHomeTutorialPreference); @@ -293,7 +296,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial"); launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture"); launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> { - startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION")); + startActivity(launchSandboxIntent.putExtra( + "tutorial_steps", + new String[] {"OVERVIEW_NAVIGATION"})); return true; }); sandboxCategory.addPreference(launchOverviewTutorialPreference); @@ -302,7 +307,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial"); launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture"); launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> { - startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT")); + startActivity(launchSandboxIntent.putExtra( + "tutorial_steps", + new String[] {"ASSISTANT"})); return true; }); sandboxCategory.addPreference(launchAssistantTutorialPreference); @@ -311,7 +318,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat { launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode"); launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures"); launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> { - startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE")); + startActivity(launchSandboxIntent.putExtra( + "tutorial_steps", + new String[] {"SANDBOX_MODE"})); return true; }); sandboxCategory.addPreference(launchSandboxModeTutorialPreference); From 702ed2788678ac744c768aad6a6302e7cf91a26b Mon Sep 17 00:00:00 2001 From: Pinyao Ting Date: Wed, 14 Oct 2020 11:17:04 -0700 Subject: [PATCH 40/56] Fix the issue where shortcuts are removed in minimal device mode When loading the workspace, Launcher pins/unpins shortcuts in comply with the loaded workspace. Since minimal device mode creates a mostly empty workspace, existing shortcuts are getting unpinned as a result. To mitigate the issue this CL compares the db name and only invoke sanitizeData when it matches the one defined in InvariantDeviceProfile. Bug: 170611866 Test: manual 1. add some deep shortcut in workspace (e.g. long tap on chrome, drag "incognito tab" to workspace) 2. opt-in to sunshine fishfood (g/sunshine-teamfood) 3. enable bedtime mode with minimal device in Settings -> Digital Wellbeing -> Show Your Data -> Bedtime mode -> Customize -> minimal device 4. toggle bedtime mode, wait for apps in minimal device to show, then toggle off bedtime mode 5. verify the deep shortcut still exist Change-Id: Ie18216ecb288e7481aa2404c4cb3ea418aee85cb --- src/com/android/launcher3/LauncherProvider.java | 3 +++ src/com/android/launcher3/LauncherSettings.java | 2 ++ src/com/android/launcher3/model/LoaderTask.java | 15 +++++++++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 2973cf7278..aeed16aa28 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -188,6 +188,9 @@ public class LauncherProvider extends ContentProvider { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder); + final Bundle extra = new Bundle(); + extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName()); + result.setExtras(extra); result.setNotificationUri(getContext().getContentResolver(), uri); return result; diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 58a418edec..d2758f53ad 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -358,6 +358,8 @@ public class LauncherSettings { public static final String EXTRA_VALUE = "value"; + public static final String EXTRA_DB_NAME = "db_name"; + public static Bundle call(ContentResolver cr, String method) { return call(cr, method, null /* arg */); } diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index c0d5882b46..d47fafd40b 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -41,6 +41,7 @@ import android.content.pm.PackageInstaller.SessionInfo; import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; import android.net.Uri; +import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; @@ -130,6 +131,7 @@ public class LoaderTask implements Runnable { private final Set mPendingPackages = new HashSet<>(); private boolean mItemsDeleted = false; + private String mDbName; public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel, ModelDelegate modelDelegate, LoaderResults results) { @@ -274,7 +276,14 @@ public class LoaderTask implements Runnable { if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) { loadFolderNames(); } - sanitizeData(); + + // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db. + // sanitizeData should not be invoked if the workspace is loaded from a db different + // from the main db as defined in the invariant device profile. + // (e.g. both grid preview and minimal device mode uses a different db) + if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) { + sanitizeData(); + } verifyNotStopped(); updateHandler.finish(); @@ -349,7 +358,9 @@ public class LoaderTask implements Runnable { final LoaderCursor c = new LoaderCursor( contentResolver.query(contentUri, null, selection, null, null), contentUri, mApp, mUserManagerState); - + final Bundle extras = c.getExtras(); + mDbName = extras == null + ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME); try { final int appWidgetIdIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.APPWIDGET_ID); From 314761a80819a6e64a136161f51eebb0f0528c4d Mon Sep 17 00:00:00 2001 From: Samuel Fufa Date: Wed, 14 Oct 2020 10:15:07 -0700 Subject: [PATCH 41/56] Setup SearchResultIcon for single cell results SearchResultIcon will be able to render apps, shortcuts and remote actions. It can also handle its own focused state drawing. Screenshot: https://screenshot.googleplex.com/C3KgjJtLQTBPgaf Bug: 170752716 Test: Manual Change-Id: I460a9c128ea3f5814784e342c5d5fa5b7e310882 --- AndroidManifest-common.xml | 1 + res/layout/all_apps_icon.xml | 15 +-- res/layout/search_result_icon.xml | 20 ++++ res/values/styles.xml | 10 ++ src/com/android/launcher3/BubbleTextView.java | 71 +++++++++++- .../launcher3/allapps/AllAppsGridAdapter.java | 69 +++--------- .../allapps/AllAppsSectionDecorator.java | 23 +++- .../launcher3/allapps/AllAppsStore.java | 16 +++ .../allapps/search/SearchEventTracker.java | 4 +- .../launcher3/views/HeroSearchResultView.java | 13 +-- .../android/launcher3/views/ScrimView.java | 2 +- .../launcher3/views/SearchResultIcon.java | 103 ++++++++++++++++++ .../launcher3/views/SearchResultIconRow.java | 10 ++ 13 files changed, 277 insertions(+), 80 deletions(-) create mode 100644 res/layout/search_result_icon.xml create mode 100644 src/com/android/launcher3/views/SearchResultIcon.java diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml index 97e3786a36..a47a500b8e 100644 --- a/AndroidManifest-common.xml +++ b/AndroidManifest-common.xml @@ -30,6 +30,7 @@ with some minor changed based on the derivative app. --> + diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml index 79fb6126ad..069954c56b 100644 --- a/res/layout/all_apps_icon.xml +++ b/res/layout/all_apps_icon.xml @@ -1,5 +1,4 @@ - - - + launcher:centerVertically="true" /> diff --git a/res/layout/search_result_icon.xml b/res/layout/search_result_icon.xml new file mode 100644 index 0000000000..3c1dd49109 --- /dev/null +++ b/res/layout/search_result_icon.xml @@ -0,0 +1,20 @@ + + + + diff --git a/res/values/styles.xml b/res/values/styles.xml index fd3d873635..067cf7feac 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -223,6 +223,16 @@ 1 + + + +