Merge "Scale task view in TaskViewSimulator before translating it" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 810f4e3..f92b3e3 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -63,7 +63,6 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import java.util.function.Consumer;
/**
* Data model for digital wellbeing status of apps.
@@ -222,9 +221,8 @@
reloadLauncherInNormalMode(context);
return;
}
- runWithMinimalDeviceConfigs((bundle) -> {
- if (bundle.getInt(EXTRA_MINIMAL_DEVICE_STATE, UNKNOWN_MINIMAL_DEVICE_STATE)
- == IN_MINIMAL_DEVICE) {
+ mWorkerHandler.post(() -> {
+ if (isInMinimalDeviceMode()) {
reloadLauncherInMinimalMode(context);
} else {
reloadLauncherInNormalMode(context);
@@ -253,31 +251,30 @@
.authority(mWellbeingProviderPkg + ".api");
}
- /**
- * Fetch most up-to-date minimal device config.
- */
@WorkerThread
- private void runWithMinimalDeviceConfigs(Consumer<Bundle> consumer) {
+ private boolean isInMinimalDeviceMode() {
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
- return;
+ return false;
}
if (DEBUG || mIsInTest) {
- Log.d(TAG, "runWithMinimalDeviceConfigs() called");
+ Log.d(TAG, "isInMinimalDeviceMode() called");
}
Preconditions.assertNonUiThread();
final Uri contentUri = apiBuilder().build();
- final Bundle remoteBundle;
try (ContentProviderClient client = mContext.getContentResolver()
.acquireUnstableContentProviderClient(contentUri)) {
- remoteBundle = client.call(
+ final Bundle remoteBundle = client == null ? null : client.call(
METHOD_GET_MINIMAL_DEVICE_CONFIG, null /* args */, null /* extras */);
- consumer.accept(remoteBundle);
+ return remoteBundle != null
+ && remoteBundle.getInt(EXTRA_MINIMAL_DEVICE_STATE,
+ UNKNOWN_MINIMAL_DEVICE_STATE) == IN_MINIMAL_DEVICE;
} catch (Exception e) {
Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
- if (DEBUG || mIsInTest) Log.i(TAG, "runWithMinimalDeviceConfigs(): finished");
+ if (DEBUG || mIsInTest) Log.i(TAG, "isInMinimalDeviceMode(): finished");
+ return false;
}
private boolean updateActions(String... packageNames) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 26ad377..22e6755 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -434,9 +434,13 @@
mOnDeferredActivityLaunch);
mGestureState.runOnceAtState(STATE_END_TARGET_SET,
- () -> mDeviceState.getRotationTouchHelper().
- onEndTargetCalculated(mGestureState.getEndTarget(),
- mActivityInterface));
+ () -> {
+ mDeviceState.getRotationTouchHelper()
+ .onEndTargetCalculated(mGestureState.getEndTarget(),
+ mActivityInterface);
+
+ mRecentsView.onGestureEndTargetCalculated(mGestureState.getEndTarget());
+ });
notifyGestureStartedAsync();
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index f5f5259..8f2356c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
@@ -27,6 +28,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -74,14 +76,14 @@
}
/**
- * When the gesture ends and recents view become interactive, we also remove the temporary
+ * When the gesture ends and we're going to recents view, we also remove the temporary
* invisible tile added for the home task. This also pushes the remaining tiles back
* to the center.
*/
@Override
- public void onGestureAnimationEnd() {
- super.onGestureAnimationEnd();
- if (mHomeTaskInfo != null) {
+ public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+ super.onGestureEndTargetCalculated(endTarget);
+ if (mHomeTaskInfo != null && endTarget == RECENTS) {
TaskView tv = getTaskView(mHomeTaskInfo.taskId);
if (tv != null) {
PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 0362377..b617817 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -118,6 +118,7 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
@@ -1183,7 +1184,14 @@
}
/**
- * Called when a gesture from an app has finished.
+ * Called when a gesture from an app has finished, and an end target has been determined.
+ */
+ public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+
+ }
+
+ /**
+ * Called when a gesture from an app has finished, and the animation to the target has ended.
*/
public void onGestureAnimationEnd() {
if (mOrientationState.setGestureActive(false)) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 656d59e..d47eba6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -246,7 +246,7 @@
setScaleX(taskView.getScaleX());
setScaleY(taskView.getScaleY());
boolean canActivityRotate = taskView.getRecentsView()
- .mOrientationState.canRecentsActivityRotate();
+ .mOrientationState.isRecentsActivityRotationAllowed();
mOptionLayout.setOrientation(orientationHandler
.getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout));
setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top,
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index ccfa3fc..9a053f2 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -3,6 +3,8 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -11,12 +13,12 @@
import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
import android.content.Intent;
-import android.os.Build;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.views.DigitalWellBeingToast;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -33,10 +35,9 @@
resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
@Test
+ // b/150303529
+ @TestStabilityRule.Stability(flavors = UNBUNDLED_POSTSUBMIT | PLATFORM_POSTSUBMIT)
public void testToast() throws Exception {
- // b/150303529
- if (Build.MODEL.contains("Cuttlefish")) return;
-
startAppFast(CALCULATOR_PACKAGE);
final UsageStatsManager usageStatsManager =
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ecd4e2b..c5863c1 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -201,6 +201,12 @@
@PortraitLandscape
public void testOverviewActions() throws Exception {
if (mLauncher.getNavigationModel() != NavigationModel.TWO_BUTTON) {
+ // Experimenting for b/165029151:
+ final Overview overview = mLauncher.pressHome().switchToOverview();
+ if (overview.hasTasks()) overview.dismissAllTasks();
+ mLauncher.pressHome();
+ //
+
startTestAppsWithCheck();
OverviewActions actionsView =
mLauncher.pressHome().switchToOverview().getOverviewActions();
diff --git a/res/layout/search_result_thumbnail.xml b/res/layout/search_result_thumbnail.xml
new file mode 100644
index 0000000..0cc5a29
--- /dev/null
+++ b/res/layout/search_result_thumbnail.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.views.ThumbnailSearchResultView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="125dp"
+ android:layout_height="125dp"/>
\ No newline at end of file
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 90566f3..1cf2e89 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
@@ -70,6 +71,7 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -88,9 +90,10 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.CallSuper;
@@ -117,6 +120,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
@@ -269,6 +273,8 @@
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
+ private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
+
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
@@ -400,6 +406,7 @@
inflateRootView(R.layout.launcher);
setupViews();
+ crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
@@ -476,7 +483,7 @@
() -> getStateManager().goToState(NORMAL));
if (Utilities.ATLEAST_R) {
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+ getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
mLifecycleRegistry = new LifecycleRegistry(this);
@@ -1364,6 +1371,18 @@
closeContextMenu();
}
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ int width = mDragLayer.getWidth();
+ int height = mDragLayer.getHeight();
+
+ if (width <= 0 || height <= 0) {
+ return null;
+ }
+
+ return BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
+ }
+
public AllAppsTransitionController getAllAppsController() {
return mAllAppsController;
}
@@ -2758,4 +2777,40 @@
void onLauncherResume();
}
+
+ /**
+ * Cross-fades the launcher's updated appearance with its previous appearance.
+ *
+ * This method is used to cross-fade UI updates on activity creation, specifically dark mode
+ * updates.
+ */
+ private void crossFadeWithPreviousAppearance() {
+ Bitmap previousAppearanceBitmap = (Bitmap) getLastNonConfigurationInstance();
+
+ if (previousAppearanceBitmap == null) {
+ return;
+ }
+
+ ImageView crossFadeHelper = new ImageView(this);
+
+ crossFadeHelper.setImageBitmap(previousAppearanceBitmap);
+ crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
+ InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
+ InsettableFrameLayout.LayoutParams.MATCH_PARENT,
+ InsettableFrameLayout.LayoutParams.MATCH_PARENT);
+
+ layoutParams.ignoreInsets = true;
+
+ crossFadeHelper.setLayoutParams(layoutParams);
+
+ getRootView().addView(crossFadeHelper);
+
+ crossFadeHelper
+ .animate()
+ .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION)
+ .alpha(0f)
+ .withEndAction(() -> getRootView().removeView(crossFadeHelper))
+ .start();
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index bfe327e..a4181c5 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -105,7 +105,7 @@
new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
- .registerInstallTracker(mModel, MODEL_EXECUTOR);
+ .registerInstallTracker(mModel);
// Register an observer to rebind the notification listener when dots are re-enabled.
mNotificationDotsObserver =
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 007e5f5..1bbbb2b 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -25,8 +25,11 @@
import android.os.UserHandle;
import android.text.TextUtils;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.util.Executors;
/**
* BroadcastReceiver to handle session commit intent.
@@ -38,6 +41,11 @@
@Override
public void onReceive(Context context, Intent intent) {
+ Executors.MODEL_EXECUTOR.execute(() -> processIntent(context, intent));
+ }
+
+ @WorkerThread
+ private static void processIntent(Context context, Intent intent) {
if (!isEnabled(context)) {
// User has decided to not add icons on homescreen.
return;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index dea2a8d..43ccb79 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -133,6 +133,10 @@
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
public static final String EXTRA_WALLPAPER_FLAVOR = "com.android.launcher3.WALLPAPER_FLAVOR";
+ // An intent extra to indicate the launch source by launcher.
+ public static final String EXTRA_WALLPAPER_LAUNCH_SOURCE =
+ "com.android.wallpaper.LAUNCH_SOURCE";
+
public static boolean IS_RUNNING_IN_TEST_HARNESS =
ActivityManager.isRunningInTestHarness();
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index d1340fa..f01f90b 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -96,6 +96,8 @@
public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11;
+ public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12;
+
// Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
@@ -186,6 +188,7 @@
|| viewType == VIEW_TYPE_SEARCH_SLICE
|| viewType == VIEW_TYPE_SEARCH_ROW
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
+ || viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|| viewType == VIEW_TYPE_SEARCH_SHORTCUT;
}
}
@@ -197,6 +200,7 @@
*/
public static class AdapterItemWithPayload<T> extends AdapterItem {
private T mPayload;
+ private String mSearchSessionId;
private AllAppsSearchPlugin mPlugin;
private IntConsumer mSelectionHandler;
@@ -218,6 +222,14 @@
mSelectionHandler = runnable;
}
+ public void setSearchSessionId(String searchSessionId) {
+ mSearchSessionId = searchSessionId;
+ }
+
+ public String getSearchSessionId() {
+ return mSearchSessionId;
+ }
+
public IntConsumer getSelectionHandler() {
return mSelectionHandler;
}
@@ -225,6 +237,8 @@
public T getPayload() {
return mPayload;
}
+
+
}
/**
@@ -307,15 +321,21 @@
@Override
public int getSpanSize(int position) {
- if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
- return 1;
+ int viewType = mApps.getAdapterItems().get(position).viewType;
+ if (isIconViewType(viewType)) {
+ return 1 * SPAN_MULTIPLIER;
+ } else if (viewType == VIEW_TYPE_SEARCH_THUMBNAIL) {
+ return mAppsPerRow;
} else {
// Section breaks span the full width
- return mAppsPerRow;
+ return mAppsPerRow * SPAN_MULTIPLIER;
}
}
}
+ // multiplier to support adapter item column count that is not mAppsPerRow.
+ private static final int SPAN_MULTIPLIER = 3;
+
private final BaseDraggingActivity mLauncher;
private final LayoutInflater mLayoutInflater;
private final AlphabeticalAppsList mApps;
@@ -352,7 +372,7 @@
public void setAppsPerRow(int appsPerRow) {
mAppsPerRow = appsPerRow;
- mGridLayoutMgr.setSpanCount(mAppsPerRow);
+ mGridLayoutMgr.setSpanCount(mAppsPerRow * SPAN_MULTIPLIER);
}
/**
@@ -444,6 +464,9 @@
case VIEW_TYPE_SEARCH_PEOPLE:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.search_result_people_item, parent, false));
+ case VIEW_TYPE_SEARCH_THUMBNAIL:
+ return new ViewHolder(mLayoutInflater.inflate(
+ R.layout.search_result_thumbnail, parent, false));
default:
throw new RuntimeException("Unexpected view type");
}
@@ -464,24 +487,27 @@
//TODO: replace with custom TopHitBubbleTextView with support for both shortcut
// and apps
if (adapterItem instanceof AdapterItemWithPayload) {
- AdapterItemWithPayload withPayload = (AdapterItemWithPayload) adapterItem;
- IntConsumer selectionHandler = type -> {
+ AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
+ item.setSelectionHandler(type -> {
SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
- type);
+ type, item.position, item.getSearchSessionId());
e.bundle = HeroSearchResultView.getAppBundle(info);
- if (withPayload.getPlugin() != null) {
- withPayload.getPlugin().notifySearchTargetEvent(e);
+ if (item.getPlugin() != null) {
+ item.getPlugin().notifySearchTargetEvent(e);
}
- };
+ });
icon.setOnClickListener(view -> {
- selectionHandler.accept(SearchTargetEvent.SELECT);
+ item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
mOnIconClickListener.onClick(view);
});
icon.setOnLongClickListener(view -> {
- selectionHandler.accept(SearchTargetEvent.LONG_PRESS);
+ item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
return mOnIconLongClickListener.onLongClick(view);
});
- withPayload.setSelectionHandler(selectionHandler);
+ }
+ else {
+ icon.setOnClickListener(mOnIconClickListener);
+ icon.setOnLongClickListener(mOnIconLongClickListener);
}
break;
case VIEW_TYPE_EMPTY_SEARCH:
@@ -500,20 +526,22 @@
break;
case VIEW_TYPE_SEARCH_SLICE:
SliceView sliceView = (SliceView) holder.itemView;
- AdapterItemWithPayload<Uri> item =
+ AdapterItemWithPayload<Uri> slicePayload =
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
sliceView.setOnSliceActionListener((info1, s) -> {
- if (item.getPlugin() != null) {
+ if (slicePayload.getPlugin() != null) {
SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
SearchTarget.ItemType.SETTINGS_SLICE,
- SearchTargetEvent.CHILD_SELECT);
+ SearchTargetEvent.CHILD_SELECT, slicePayload.position,
+ slicePayload.getSearchSessionId());
searchTargetEvent.bundle = new Bundle();
- searchTargetEvent.bundle.putParcelable("uri", item.getPayload());
- item.getPlugin().notifySearchTargetEvent(searchTargetEvent);
+ searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
+ slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
}
});
try {
- LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher, item.getPayload());
+ LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
+ slicePayload.getPayload());
liveData.observe((Launcher) mLauncher, sliceView);
sliceView.setTag(liveData);
} catch (Exception ignored) {
@@ -525,9 +553,11 @@
case VIEW_TYPE_SEARCH_ROW:
case VIEW_TYPE_SEARCH_SHORTCUT:
case VIEW_TYPE_SEARCH_PEOPLE:
+ case VIEW_TYPE_SEARCH_THUMBNAIL:
+ AdapterItemWithPayload item =
+ (AdapterItemWithPayload) mApps.getAdapterItems().get(position);
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
- payloadResultView.applyAdapterInfo(
- (AdapterItemWithPayload) mApps.getAdapterItems().get(position));
+ payloadResultView.setup(item);
break;
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
@@ -553,7 +583,6 @@
}
}
-
@Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 3320189..d7fa5bc 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -35,6 +35,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.ArrayList;
import java.util.List;
@@ -213,6 +215,44 @@
/**
* Updates View using Adapter's payload
*/
+
+ default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
+ Object[] targetInfo = getTargetInfo();
+ if (targetInfo != null) {
+ targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
+ targetInfo[1] = adapterItemWithPayload.position;
+ }
+ applyAdapterInfo(adapterItemWithPayload);
+ }
+
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
+
+ /**
+ * Gets object created by {@link PayloadResultHandler#createTargetInfo()}
+ */
+ Object[] getTargetInfo();
+
+ /**
+ * Creates a wrapper object to hold searchSessionId and item position
+ */
+ default Object[] createTargetInfo() {
+ return new Object[2];
+ }
+
+ /**
+ * Generates a SearchTargetEvent object for a PayloadHandlerView
+ */
+ default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
+ int eventType) {
+ Object[] targetInfo = getTargetInfo();
+ if (targetInfo == null) return null;
+
+ String searchSessionId = (String) targetInfo[0];
+ int position = (int) targetInfo[1];
+ return new SearchTargetEvent(itemType, eventType,
+ position, searchSessionId);
+ }
}
+
+
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2627149..4175280 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -177,8 +177,8 @@
"SEPARATE_RECENTS_ACTIVITY", false,
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
- public static final BooleanFlag ENABLE_MINIMAL_DEVICE = new DeviceFlag(
- "ENABLE_MINIMAL_DEVICE", false,
+ public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
+ "ENABLE_MINIMAL_DEVICE", true,
"Allow user to toggle minimal device mode in launcher.");
public static void initialize(Context context) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index c841b7b..281598a 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -58,6 +58,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.launcher3.AbstractFloatingView;
@@ -216,6 +217,8 @@
private StatsLogManager mStatsLogManager;
+ @Nullable private FolderWindowInsetsAnimationCallback mFolderWindowInsetsAnimationCallback;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -262,10 +265,10 @@
mFooterHeight = mFooter.getMeasuredHeight();
if (Utilities.ATLEAST_R) {
- WindowInsetsAnimation.Callback cb = new FolderWindowInsetsAnimationCallback(
- DISPATCH_MODE_STOP, this);
+ mFolderWindowInsetsAnimationCallback =
+ new FolderWindowInsetsAnimationCallback(DISPATCH_MODE_STOP, this);
- setWindowInsetsAnimationCallback(cb);
+ setWindowInsetsAnimationCallback(mFolderWindowInsetsAnimationCallback);
}
}
@@ -755,11 +758,17 @@
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ if (Utilities.ATLEAST_R) {
+ setWindowInsetsAnimationCallback(null);
+ }
mIsAnimatingClosed = true;
}
@Override
public void onAnimationEnd(Animator animation) {
+ if (Utilities.ATLEAST_R && mFolderWindowInsetsAnimationCallback != null) {
+ setWindowInsetsAnimationCallback(mFolderWindowInsetsAnimationCallback);
+ }
closeComplete(true);
announceAccessibilityChanges();
mIsAnimatingClosed = false;
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 753a6dd..fa25114 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,6 +17,7 @@
package com.android.launcher3.pm;
import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -31,6 +32,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.SessionCommitReceiver;
@@ -39,10 +41,10 @@
import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Preconditions;
import java.util.ArrayList;
import java.util.HashMap;
@@ -65,27 +67,27 @@
private final LauncherApps mLauncherApps;
private final Context mAppContext;
- private final IntSet mPromiseIconIds;
private final PackageInstaller mInstaller;
private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
+ private IntSet mPromiseIconIds;
+
public InstallSessionHelper(Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
+ }
+ @WorkerThread
+ private IntSet getPromiseIconIds() {
+ Preconditions.assertWorkerThread();
+ if (mPromiseIconIds != null) {
+ return mPromiseIconIds;
+ }
mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
- getPrefs(context).getString(PROMISE_ICON_IDS, "")));
+ getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
- cleanUpPromiseIconIds();
- }
-
- public static UserHandle getUserHandle(SessionInfo info) {
- return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
- }
-
- protected void cleanUpPromiseIconIds() {
IntArray existingIds = new IntArray();
for (SessionInfo info : getActiveSessions().values()) {
existingIds.add(info.getSessionId());
@@ -100,6 +102,7 @@
for (int i = idsToRemove.size() - 1; i >= 0; --i) {
mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
}
+ return mPromiseIconIds;
}
public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
@@ -126,7 +129,7 @@
private void updatePromiseIconPrefs() {
getPrefs(mAppContext).edit()
- .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+ .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
.apply();
}
@@ -184,13 +187,15 @@
return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
}
+ @WorkerThread
public boolean promiseIconAddedForId(int sessionId) {
- return mPromiseIconIds.contains(sessionId);
+ return getPromiseIconIds().contains(sessionId);
}
+ @WorkerThread
public void removePromiseIconId(int sessionId) {
- if (mPromiseIconIds.contains(sessionId)) {
- mPromiseIconIds.getArray().removeValue(sessionId);
+ if (promiseIconAddedForId(sessionId)) {
+ getPromiseIconIds().getArray().removeValue(sessionId);
updatePromiseIconPrefs();
}
}
@@ -203,6 +208,7 @@
* - The app is not already installed
* - A promise icon for the session has not already been created
*/
+ @WorkerThread
void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
&& SessionCommitReceiver.isEnabled(mAppContext)
@@ -210,25 +216,24 @@
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())
+ && !promiseIconAddedForId(sessionInfo.getSessionId())
&& new PackageManagerHelper(mAppContext).getApplicationInfo(
sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
ItemInstallQueue.INSTANCE.get(mAppContext)
.queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
- mPromiseIconIds.add(sessionInfo.getSessionId());
+ getPromiseIconIds().add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
}
}
- public InstallSessionTracker registerInstallTracker(
- InstallSessionTracker.Callback callback, LooperExecutor executor) {
+ public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- mInstaller.registerSessionCallback(tracker, executor.getHandler());
+ mInstaller.registerSessionCallback(tracker, MODEL_EXECUTOR.getHandler());
} else {
- mLauncherApps.registerPackageInstallerSessionCallback(executor, tracker);
+ mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, tracker);
}
return tracker;
}
@@ -240,4 +245,8 @@
mLauncherApps.unregisterPackageInstallerSessionCallback(tracker);
}
}
+
+ public static UserHandle getUserHandle(SessionInfo info) {
+ return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
+ }
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index eb3ca73..b0b907a 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -24,8 +24,11 @@
import android.os.UserHandle;
import android.util.SparseArray;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.util.PackageUserKey;
+@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
// Lazily initialized
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 2d7d6b0..5ade22b 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -60,6 +60,9 @@
private void onUsersChanged(Intent intent) {
enableAndResetCache();
mUserChangeListeners.forEach(Runnable::run);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
+ }
}
/**
@@ -104,9 +107,6 @@
mUsers = null;
mUserToSerialMap = null;
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work profile removed", new Exception());
- }
}
}
diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java
index a8e1c6b..94b9ca1 100644
--- a/src/com/android/launcher3/views/HeroSearchResultView.java
+++ b/src/com/android/launcher3/views/HeroSearchResultView.java
@@ -16,12 +16,16 @@
package com.android.launcher3.views;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Point;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
@@ -31,9 +35,10 @@
import com.android.launcher3.DragSource;
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;
+import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -53,9 +58,10 @@
* A view representing a high confidence app search result that includes shortcuts
*/
public class HeroSearchResultView extends LinearLayout implements DragSource,
- AllAppsSearchBarController.PayloadResultHandler<List<ItemInfoWithIcon>> {
+ PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
public static final int MAX_SHORTCUTS_COUNT = 2;
+ private final Object[] mTargetInfo = createTargetInfo();
BubbleTextView mBubbleTextView;
View mIconView;
BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
@@ -102,7 +108,7 @@
grid.allAppsIconSizePx));
bubbleTextView.setOnClickListener(view -> {
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
- SearchTargetEvent event = new SearchTargetEvent(
+ SearchTargetEvent event = getSearchTargetEvent(
SearchTarget.ItemType.APP_HERO,
SearchTargetEvent.CHILD_SELECT);
event.bundle = getAppBundle(itemInfo);
@@ -119,15 +125,25 @@
* Apply {@link ItemInfo} for appIcon and shortcut Icons
*/
@Override
- public void applyAdapterInfo(AdapterItemWithPayload<List<ItemInfoWithIcon>> adapterItem) {
+ public void applyAdapterInfo(
+ AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
mIconView.setBackground(mBubbleTextView.getIcon());
mIconView.setTag(adapterItem.appInfo);
- List<ItemInfoWithIcon> shorcutInfos = adapterItem.getPayload();
+ List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
+ LauncherAppState appState = LauncherAppState.getInstance(getContext());
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
- mDeepShortcutTextViews[i].setVisibility(shorcutInfos.size() > i ? VISIBLE : GONE);
- if (i < shorcutInfos.size()) {
- mDeepShortcutTextViews[i].applyFromItemInfoWithIcon(shorcutInfos.get(i));
+ BubbleTextView shortcutView = mDeepShortcutTextViews[i];
+ mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
+ if (i < shortcutDetails.size()) {
+ Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
+ //apply ItemInfo and prepare view
+ shortcutView.applyFromItemInfoWithIcon(p.second);
+ MODEL_EXECUTOR.execute(() -> {
+ // load unbadged shortcut in background and update view when icon ready
+ appState.getIconCache().getUnbadgedShortcutIcon(p.second, p.first);
+ MAIN_EXECUTOR.post(() -> shortcutView.reapplyItemInfo(p.second));
+ });
}
}
mPlugin = adapterItem.getPlugin();
@@ -135,6 +151,11 @@
}
@Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
+ @Override
public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
mBubbleTextView.setVisibility(VISIBLE);
mBubbleTextView.setIconVisible(true);
@@ -169,7 +190,7 @@
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
- SearchTargetEvent event = new SearchTargetEvent(
+ SearchTargetEvent event = mContainer.getSearchTargetEvent(
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
event.bundle = getAppBundle(itemInfo);
if (mContainer.mPlugin != null) {
@@ -186,7 +207,7 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
- SearchTargetEvent event = new SearchTargetEvent(
+ SearchTargetEvent event = getSearchTargetEvent(
SearchTarget.ItemType.APP_HERO, eventType);
event.bundle = getAppBundle(itemInfo);
if (mPlugin != null) {
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 3ec20d5..a8caa9e 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.views;
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
+import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_LAUNCH_SOURCE;
import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
@@ -211,7 +212,8 @@
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(EXTRA_WALLPAPER_OFFSET,
- launcher.getWorkspace().getWallpaperOffsetForCenterPage());
+ launcher.getWorkspace().getWallpaperOffsetForCenterPage())
+ .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
if (!Utilities.existsStyleWallpapers(launcher)) {
intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
} else {
diff --git a/src/com/android/launcher3/views/SearchResultPeopleView.java b/src/com/android/launcher3/views/SearchResultPeopleView.java
index 6e45e88..eacc095 100644
--- a/src/com/android/launcher3/views/SearchResultPeopleView.java
+++ b/src/com/android/launcher3/views/SearchResultPeopleView.java
@@ -67,7 +67,7 @@
private ImageButton[] mProviderButtons = new ImageButton[3];
private AllAppsSearchPlugin mPlugin;
private Uri mContactUri;
-
+ private final Object[] mTargetInfo = createTargetInfo();
public SearchResultPeopleView(Context context) {
this(context, null, 0);
@@ -129,14 +129,14 @@
Bundle provider = providers.get(i);
Intent intent = Intent.parseUri(provider.getString("intent_uri_str"),
URI_ANDROID_APP_SCHEME | URI_ALLOW_UNSAFE);
- setupProviderButton(button, provider, 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);
- MAIN_EXECUTOR.post(()-> button.setImageDrawable(appIcon));
+ MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
} catch (PackageManager.NameNotFoundException ignored) {
}
@@ -151,11 +151,17 @@
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
}
- private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
+ private void setupProviderButton(ImageButton button, Bundle provider, Intent intent,
+ AllAppsGridAdapter.AdapterItem adapterItem) {
Launcher launcher = Launcher.getLauncher(getContext());
button.setOnClickListener(b -> {
launcher.startActivitySafely(b, intent, null);
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
SearchTarget.ItemType.PEOPLE,
SearchTargetEvent.CHILD_SELECT);
searchTargetEvent.bundle = new Bundle();
@@ -173,8 +179,8 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, new Intent(Intent.ACTION_VIEW, mContactUri).setFlags(
Intent.FLAG_ACTIVITY_NEW_TASK), null);
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
- SearchTarget.ItemType.PEOPLE, eventType);
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
+ eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("contact_uri", mContactUri);
if (mPlugin != null) {
diff --git a/src/com/android/launcher3/views/SearchResultPlayItem.java b/src/com/android/launcher3/views/SearchResultPlayItem.java
index 8624609..ff3ecc8 100644
--- a/src/com/android/launcher3/views/SearchResultPlayItem.java
+++ b/src/com/android/launcher3/views/SearchResultPlayItem.java
@@ -58,6 +58,8 @@
private String mPackageName;
private boolean mIsInstantGame;
private AllAppsSearchPlugin mPlugin;
+ private final Object[] mTargetInfo = createTargetInfo();
+
public SearchResultPlayItem(Context context) {
this(context, null, 0);
@@ -125,6 +127,11 @@
});
}
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
private void showIfNecessary(TextView textView, @Nullable String string) {
if (string == null || string.isEmpty()) {
textView.setVisibility(GONE);
@@ -160,7 +167,7 @@
}
private void logSearchEvent(int eventType) {
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
SearchTarget.ItemType.PLAY_RESULTS, eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putString("package_name", mPackageName);
diff --git a/src/com/android/launcher3/views/SearchResultShortcut.java b/src/com/android/launcher3/views/SearchResultShortcut.java
index 307cf34..a409f08 100644
--- a/src/com/android/launcher3/views/SearchResultShortcut.java
+++ b/src/com/android/launcher3/views/SearchResultShortcut.java
@@ -50,6 +50,8 @@
private View mIconView;
private ShortcutInfo mShortcutInfo;
private AllAppsSearchPlugin mPlugin;
+ private final Object[] mTargetInfo = createTargetInfo();
+
public SearchResultShortcut(@NonNull Context context) {
super(context);
@@ -96,12 +98,17 @@
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
}
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
private void handleSelection(int eventType) {
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) mBubbleTextView.getTag();
ItemClickHandler.onClickAppShortcut(this, itemInfo, Launcher.getLauncher(getContext()));
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
- SearchTarget.ItemType.SHORTCUT, eventType);
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
+ eventType);
searchTargetEvent.shortcut = mShortcutInfo;
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
diff --git a/src/com/android/launcher3/views/SearchSectionHeaderView.java b/src/com/android/launcher3/views/SearchSectionHeaderView.java
index d439ee3..0fe0a43 100644
--- a/src/com/android/launcher3/views/SearchSectionHeaderView.java
+++ b/src/com/android/launcher3/views/SearchSectionHeaderView.java
@@ -52,4 +52,9 @@
setVisibility(INVISIBLE);
}
}
+
+ @Override
+ public Object[] getTargetInfo() {
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java
index 93bcee2..a1a0172 100644
--- a/src/com/android/launcher3/views/SearchSettingsRowView.java
+++ b/src/com/android/launcher3/views/SearchSettingsRowView.java
@@ -48,6 +48,8 @@
private TextView mBreadcrumbsView;
private Intent mIntent;
private AllAppsSearchPlugin mPlugin;
+ private final Object[] mTargetInfo = createTargetInfo();
+
public SearchSettingsRowView(@NonNull Context context) {
super(context);
@@ -87,6 +89,11 @@
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
}
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+
private void showIfAvailable(TextView view, @Nullable String string) {
if (TextUtils.isEmpty(string)) {
view.setVisibility(GONE);
@@ -108,7 +115,7 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivityForResult(mIntent, 0);
- SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
+ SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
SearchTarget.ItemType.SETTINGS_ROW, eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("intent", mIntent);
diff --git a/src/com/android/launcher3/views/ThumbnailSearchResultView.java b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
new file mode 100644
index 0000000..1d58a06
--- /dev/null
+++ b/src/com/android/launcher3/views/ThumbnailSearchResultView.java
@@ -0,0 +1,89 @@
+/*
+ * 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.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.AttributeSet;
+
+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.util.Themes;
+import com.android.systemui.plugins.AllAppsSearchPlugin;
+import com.android.systemui.plugins.shared.SearchTarget;
+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<Bundle> {
+
+ private final Object[] mTargetInfo = createTargetInfo();
+ Intent mIntent;
+ AllAppsSearchPlugin mPlugin;
+ int mPosition;
+
+ public ThumbnailSearchResultView(Context context) {
+ super(context);
+ }
+
+ public ThumbnailSearchResultView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ThumbnailSearchResultView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ private void handleSelection(int eventType) {
+ Launcher launcher = Launcher.getLauncher(getContext());
+ launcher.startActivitySafely(this, mIntent, null);
+
+ SearchTargetEvent event = getSearchTargetEvent(
+ SearchTarget.ItemType.SCREENSHOT, eventType);
+ if (mPlugin != null) {
+ mPlugin.notifySearchTargetEvent(event);
+ }
+ }
+
+ @Override
+ public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItem) {
+ mPosition = adapterItem.position;
+ RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(null,
+ (Bitmap) adapterItem.getPayload().getParcelable("bitmap"));
+ drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
+ setImageDrawable(drawable);
+ mIntent = new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse(adapterItem.getPayload().getString("uri")))
+ .setType("image/*")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mPlugin = adapterItem.getPlugin();
+ adapterItem.setSelectionHandler(this::handleSelection);
+ }
+
+ @Override
+ public Object[] getTargetInfo() {
+ return mTargetInfo;
+ }
+}
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
index 437cf3c..aa3ab8f 100644
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
@@ -16,6 +16,9 @@
package com.android.systemui.plugins;
+import android.app.Activity;
+import android.view.View;
+
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
@@ -29,22 +32,26 @@
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
public interface AllAppsSearchPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
- int VERSION = 5;
+ int VERSION = 7;
+ void setup(Activity activity, View view);
/**
- * Send signal when user enters all apps.
+ * Send launcher state related signals.
*/
- void startAllAppsSession();
+ void onStateTransitionStart(int fromState, int toState);
+ void onStateTransitionComplete(int state);
/**
- * Send signal when user starts typing.
+ * Send launcher window focus and visibility changed signals.
+ */
+ void onWindowFocusChanged(boolean hasFocus);
+ void onWindowVisibilityChanged(int visibility);
+
+ /**
+ * Send signal when user starts typing, perform search, when search ends
*/
void startedSearchSession();
-
- /**
- * Send over the query and get the search results.
- */
void performSearch(String query, Consumer<List<SearchTarget>> results);
/**
@@ -53,7 +60,8 @@
void notifySearchTargetEvent(SearchTargetEvent event);
/**
- * Send signal when user exits all apps.
+ * Launcher activity lifecycle callbacks
*/
- void endAllAppsSession();
+ void onResume(int state);
+ void onStop(int state);
}
\ No newline at end of file
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
index c6b8300..fb8dd72 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTarget.java
@@ -26,14 +26,52 @@
public class SearchTarget implements Comparable<SearchTarget> {
public enum ViewType {
+
+ /**
+ * Consists of N number of icons. (N: launcher column count)
+ */
TOP_HIT(0),
+
+ /**
+ * Consists of 1 icon and two subsidiary icons.
+ */
HERO(1),
+
+ /**
+ * Main/sub/breadcrumb texts are rendered.
+ */
DETAIL(2),
+
+ /**
+ * Consists of an icon, three detail strings.
+ */
ROW(3),
+
+ /**
+ * Consists of an icon, three detail strings and a button.
+ */
ROW_WITH_BUTTON(4),
+
+ /**
+ * Consists of a single slice view
+ */
SLICE(5),
+
+ /**
+ * Similar to hero section.
+ */
SHORTCUT(6),
- PEOPLE(7);
+
+ /**
+ * Person icon and handling app icons are rendered.
+ */
+ PEOPLE(7),
+
+ /**
+ * N number of 1x1 ratio thumbnail is rendered.
+ * (current N = 3)
+ */
+ THUMBNAIL(8);
private final int mId;
ViewType(int id) {
@@ -52,9 +90,12 @@
APP(3, "", ViewType.TOP_HIT),
APP_HERO(4, "", ViewType.HERO),
SHORTCUT(5, "Shortcuts", ViewType.SHORTCUT),
- PEOPLE(6, "People", ViewType.PEOPLE);
+ PEOPLE(6, "People", ViewType.PEOPLE),
+ SCREENSHOT(7, "Screenshots", ViewType.THUMBNAIL);
private final int mId;
+
+ /** Used to render section title. */
private final String mTitle;
private final ViewType mViewType;
@@ -81,6 +122,7 @@
public List<ShortcutInfo> shortcuts;
public Bundle bundle;
public float score;
+ public String mSessionId;
/**
* Constructor to create the search target. Bundle is currently temporary to hold
@@ -89,11 +131,12 @@
*
*/
public SearchTarget(ItemType itemType, List<ShortcutInfo> shortcuts,
- Bundle bundle, float score) {
+ Bundle bundle, float score, String sessionId) {
this.type = itemType;
this.shortcuts = shortcuts;
this.bundle = bundle;
this.score = score;
+ this.mSessionId = sessionId;
}
@Override
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
index ac4bc33..00a78de 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEvent.java
@@ -31,10 +31,14 @@
public ShortcutInfo shortcut;
public int eventType;
public Bundle bundle;
- public float score;
+ public int index;
+ public String sessionIdentifier;
- public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType) {
+ public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType, int index,
+ String sessionId) {
this.type = itemType;
this.eventType = eventType;
+ this.index = index;
+ this.sessionIdentifier = sessionId;
}
}
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index f5f93c4..488e763 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -136,7 +136,8 @@
});
executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
- "Work profile status: " + launcher.getAppsView().isPersonalTabVisible()));
+ "work profile status (" + mProfileUserId + ") :"
+ + launcher.getAppsView().isWorkTabVisible()));
// verify work edu is seen next
waitForLauncherCondition("Launcher did not show the next edu screen", l ->