Merge "Make keyguard exit animation to remote animation." into sc-dev
diff --git a/Android.bp b/Android.bp
index b20b307..a720658 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,17 +36,37 @@
name: "launcher_log_protos_lite",
srcs: [
"protos/*.proto",
+ "protos_overrides/*.proto",
],
sdk_version: "current",
proto: {
type: "lite",
local_include_dirs:[
"protos",
+ "protos_overrides",
],
},
static_libs: ["libprotobuf-java-lite"],
}
+java_library_static {
+ name: "launcher_quickstep_log_protos_lite",
+ srcs: [
+ "quickstep/protos_overrides/*.proto",
+ ],
+ sdk_version: "current",
+ proto: {
+ type: "lite",
+ local_include_dirs:[
+ "quickstep/protos_overrides",
+ ],
+ },
+ static_libs: [
+ "libprotobuf-java-lite",
+ "launcher_log_protos_lite"
+ ],
+}
+
java_library {
name: "LauncherPluginLib",
diff --git a/Android.mk b/Android.mk
index ed42039..19ad328 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,7 +33,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
LauncherPluginLib \
- launcher_log_protos_lite \
+ launcher_quickstep_log_protos_lite \
search_ui
LOCAL_SRC_FILES := \
@@ -65,7 +65,7 @@
$(call all-java-files-under, src_shortcuts_overrides) \
$(call all-java-files-under, src_ui_overrides) \
$(call all-java-files-under, ext_tests/src)
-
+
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -129,8 +129,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUI-statsd \
- SystemUISharedLib \
- launcher_log_protos_lite
+ SystemUISharedLib
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -196,8 +195,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUI-statsd \
- SystemUISharedLib \
- launcher_log_protos_lite
+ SystemUISharedLib
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
diff --git a/build.gradle b/build.gradle
index 28a05d5..a7eef13 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@
java.srcDirs = ['src', 'src_plugins']
manifest.srcFile 'AndroidManifest-common.xml'
proto {
- srcDir 'protos/'
+ srcDirs = ['protos/', 'protos_overrides/']
}
}
@@ -181,4 +181,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cd229ae..b4c6138 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -18,6 +18,8 @@
option java_package = "com.android.launcher3.logger";
option java_outer_classname = "LauncherAtom";
+import "launcher_atom_extension.proto";
+
//
// ItemInfos
message ItemInfo {
@@ -55,6 +57,7 @@
SettingsContainer settings_container = 9;
PredictedHotseatContainer predicted_hotseat_container = 10;
TaskSwitcherContainer task_switcher_container = 11;
+ ExtendedContainers extended_containers = 20;
}
}
diff --git a/protos_overrides/launcher_atom_extension.proto b/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..a07daf8
--- /dev/null
+++ b/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// This proto file contains placeholder messages that can be overridden by
+// other Launcher variants.
+// Variants could have its own launcher_atom_extension.proto file(should have
+// same messages declared here but with different implementation); when building
+// variant's apk launcher_atom.proto will reference variant's extension file,
+// essentially overriding this file.
+
+
+// Wrapper message for additional containers used in variants.
+message ExtendedContainers {}
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..2766acf
--- /dev/null
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// Wrapper message for containers used at the quickstep level.
+// Message name should match with launcher_atom_extension.proto message at
+// the AOSP level.
+message ExtendedContainers {
+
+ oneof Container{
+ DeviceSearchResultContainer device_search_result_container = 1;
+ }
+}
+
+// Represents on-device search result container.
+message DeviceSearchResultContainer{
+}
diff --git a/quickstep/res/layout/search_result_icon_row.xml b/quickstep/res/layout/search_result_icon_row.xml
index 81190cf..084920a 100644
--- a/quickstep/res/layout/search_result_icon_row.xml
+++ b/quickstep/res/layout/search_result_icon_row.xml
@@ -20,10 +20,10 @@
android:padding="@dimen/dynamic_grid_edge_margin">
<com.android.launcher3.search.SearchResultIcon
- android:layout_width="wrap_content"
android:id="@+id/icon"
- launcher:iconDisplay="hero_app"
- android:layout_height="wrap_content" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ launcher:iconDisplay="hero_app" />
<LinearLayout
android:layout_width="0dp"
@@ -34,8 +34,8 @@
android:layout_gravity="center_vertical">
<TextView
- android:layout_width="wrap_content"
android:id="@id/title"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start|center_vertical"
android:maxLines="1"
diff --git a/quickstep/res/layout/search_result_small_icon_row.xml b/quickstep/res/layout/search_result_small_icon_row.xml
new file mode 100644
index 0000000..41856fe
--- /dev/null
+++ b/quickstep/res/layout/search_result_small_icon_row.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.search.SearchResultSmallIconRow
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/dynamic_grid_edge_margin">
+
+ <com.android.launcher3.search.SearchResultIcon
+ android:id="@+id/icon"
+ style="@style/BaseIcon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
+ android:drawableTint="?android:attr/textColorPrimary"
+ android:padding="@dimen/dynamic_grid_edge_margin"
+ launcher:iconDisplay="hero_app"
+ launcher:iconSizeOverride="48dp"
+ launcher:matchTextInsetWithQuery="true"
+ launcher:layoutHorizontal="true" />
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:orientation="horizontal"
+ android:padding="@dimen/dynamic_grid_edge_margin" >
+
+ <TextView
+ android:id="@id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:maxLines="1"
+ android:paddingEnd="4dp"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/search_hero_title_size" />
+ <TextView
+ android:id="@+id/delimeter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:maxLines="1"
+ android:text="\u2022"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/search_hero_subtitle_size" />
+ <TextView
+ android:id="@+id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="1"
+ android:paddingStart="4dp"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="@dimen/search_hero_subtitle_size" />
+ </LinearLayout>
+</com.android.launcher3.search.SearchResultSmallIconRow>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
new file mode 100644
index 0000000..6e1aa1e
--- /dev/null
+++ b/quickstep/res/layout/taskbar_divider.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/taskbar_divider_thickness"
+ android:layout_height="@dimen/taskbar_divider_height"
+ android:layout_marginStart="@dimen/taskbar_icon_spacing"
+ android:layout_marginEnd="@dimen/taskbar_icon_spacing"
+ android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 3bc8ddc..54730f1 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -27,4 +27,5 @@
<!-- Taskbar -->
<color name="taskbar_background">#101010</color>
+ <color name="taskbar_divider">#C0C0C0</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 39cc0b8..0f40775 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -127,4 +127,6 @@
<dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
<!-- Note that this applies to both sides of all icons, so visible space is double this. -->
<dimen name="taskbar_icon_spacing">14dp</dimen>
+ <dimen name="taskbar_divider_thickness">1dp</dimen>
+ <dimen name="taskbar_divider_height">24dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index cbe0eb0..edcd0a2 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -355,10 +355,6 @@
// populating workspace.
// TODO: Find a better place for this
WellbeingModel.INSTANCE.get(this);
-
- if (mTaskbarController != null) {
- mTaskbarController.onHotseatUpdated();
- }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index aa6601b..88cfacb 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -70,7 +71,11 @@
*/
public class HotseatPredictionController implements DragController.DragListener,
SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
- DragSource {
+ DragSource, ViewGroup.OnHierarchyChangeListener {
+
+ private static final int FLAG_UPDATE_PAUSED = 1 << 0;
+ private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
+ private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
private int mHotSeatItemsCount;
@@ -80,8 +85,7 @@
private List<ItemInfo> mPredictedItems = Collections.emptyList();
private AnimatorSet mIconRemoveAnimators;
- private boolean mUIUpdatePaused = false;
- private boolean mDragInProgress = false;
+ private int mPauseFlags = 0;
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
@@ -114,6 +118,30 @@
mLauncher.getDragController().addDragListener(this);
launcher.getDeviceProfile().inv.addOnChangeListener(this);
+ mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ onHotseatHierarchyChanged();
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ onHotseatHierarchyChanged();
+ }
+
+ private void onHotseatHierarchyChanged() {
+ if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+ // Post update after a single frame to avoid layout within layout
+ MAIN_EXECUTOR.getHandler().post(this::updateFillIfNotLoading);
+ }
+ }
+
+ private void updateFillIfNotLoading() {
+ if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+ fillGapsWithPrediction(true);
+ }
}
/**
@@ -160,11 +188,11 @@
}
private void fillGapsWithPrediction() {
- fillGapsWithPrediction(false, null);
+ fillGapsWithPrediction(false);
}
- private void fillGapsWithPrediction(boolean animate, Runnable callback) {
- if (mUIUpdatePaused || mDragInProgress) {
+ private void fillGapsWithPrediction(boolean animate) {
+ if (mPauseFlags != 0) {
return;
}
@@ -175,12 +203,14 @@
mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- fillGapsWithPrediction(animate, callback);
+ fillGapsWithPrediction(animate);
mIconRemoveAnimators.removeListener(this);
}
});
return;
}
+
+ mPauseFlags |= FLAG_FILL_IN_PROGRESS;
for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
View child = mHotseat.getChildAt(
mHotseat.getCellXFromOrder(rank),
@@ -207,10 +237,12 @@
}
preparePredictionInfo(predictedItem, rank);
}
- bindItems(newItems, animate, callback);
+ bindItems(newItems, animate);
+
+ mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
}
- private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate, Runnable callback) {
+ private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
AnimatorSet animationSet = new AnimatorSet();
for (WorkspaceItemInfo item : itemsToAdd) {
PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
@@ -221,12 +253,11 @@
}
}
if (animate) {
- if (callback != null) {
- animationSet.addListener(AnimationSuccessListener.forRunnable(callback));
- }
+ animationSet.addListener(AnimationSuccessListener
+ .forRunnable(this::removeOutlineDrawings));
animationSet.start();
} else {
- if (callback != null) callback.run();
+ removeOutlineDrawings();
}
if (mLauncher.getTaskbarController() != null) {
@@ -234,6 +265,16 @@
}
}
+ private void removeOutlineDrawings() {
+ if (mOutlineDrawings.isEmpty()) return;
+ for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+ mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+ }
+ mHotseat.invalidate();
+ mOutlineDrawings.clear();
+ }
+
+
/**
* Unregisters callbacks and frees resources
*/
@@ -245,11 +286,9 @@
* start and pauses predicted apps update on the hotseat
*/
public void setPauseUIUpdate(boolean paused) {
- if (mLauncher.getTaskbarController() != null) {
- // Taskbar is present, always allow updates since hotseat is still visible.
- return;
- }
- mUIUpdatePaused = paused;
+ mPauseFlags = paused
+ ? (mPauseFlags | FLAG_UPDATE_PAUSED)
+ : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
if (!paused) {
fillGapsWithPrediction();
}
@@ -365,14 +404,14 @@
for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
mHotseat.addDelegatedCellDrawing(outlineDrawing);
}
- mDragInProgress = true;
+ mPauseFlags |= FLAG_DRAG_IN_PROGRESS;
mHotseat.invalidate();
}
@Override
public void onDragEnd() {
- mDragInProgress = false;
- fillGapsWithPrediction(true, this::removeOutlineDrawings);
+ mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS;
+ fillGapsWithPrediction(true);
}
@Nullable
@@ -393,15 +432,6 @@
itemInfo.screenId = rank;
}
- private void removeOutlineDrawings() {
- if (mOutlineDrawings.isEmpty()) return;
- for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
- mHotseat.removeDelegatedCellDrawing(outlineDrawing);
- }
- mHotseat.invalidate();
- mOutlineDrawings.clear();
- }
-
@Override
public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
this.mHotSeatItemsCount = profile.numHotseatIcons;
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index acf6c15..4a656c1 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -16,6 +16,7 @@
package com.android.launcher3.search;
+import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ICON;
import android.app.search.SearchTarget;
@@ -31,6 +32,7 @@
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.config.FeatureFlags;
/**
* Provides views for on-device search results
@@ -41,11 +43,12 @@
public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 7;
public static final int VIEW_TYPE_SEARCH_ICON = (1 << 8) | VIEW_TYPE_ICON;
public static final int VIEW_TYPE_SEARCH_ICON_ROW = (1 << 9);
+ public static final int VIEW_TYPE_SEARCH_SMALL_ICON_ROW = (1 << 10);
public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12;
public static final int VIEW_TYPE_SEARCH_WIDGET_LIVE = 1 << 15;
public static final int VIEW_TYPE_SEARCH_WIDGET_PREVIEW = 1 << 16;
- private static final String TAG = "SearchServiceAdapterProvider";
+ private static final String TAG = "SearchServiceAdapter";
private final AllAppsContainerView mAppsView;
private final SparseIntArray mViewTypeToLayoutMap = new SparseIntArray();
@@ -57,11 +60,13 @@
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_CORPUS_TITLE, R.layout.search_section_title);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_ICON, R.layout.search_result_icon);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_ICON_ROW, R.layout.search_result_icon_row);
+ mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SMALL_ICON_ROW, R.layout.search_result_small_icon_row);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SLICE, R.layout.search_result_slice);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_THUMBNAIL, R.layout.search_result_thumbnail);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_WIDGET_LIVE, R.layout.search_result_widget_live);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_WIDGET_PREVIEW,
R.layout.search_result_widget_preview);
+ mViewTypeToLayoutMap.put(VIEW_TYPE_ALL_APPS_DIVIDER, R.layout.all_apps_divider);
}
@Override
@@ -116,25 +121,33 @@
case LayoutType.ICON_SINGLE_VERTICAL_TEXT:
return VIEW_TYPE_SEARCH_ICON;
case LayoutType.ICON_SLICE:
+ if (FeatureFlags.DISABLE_SLICE_IN_ALLAPPS.get()) {
+ return -1;
+ }
if (t.getSliceUri() != null) {
return VIEW_TYPE_SEARCH_SLICE;
}
- Log.w(TAG, "Dropping as LayoutType.ICON_SLICE target doesn't contain sliceUri.");
+ Log.w(TAG, "LayoutType.ICON_SLICE target doesn't contain sliceUri.");
break;
case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT:
case LayoutType.ICON_SINGLE_HORIZONTAL_TEXT:
case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT_BUTTON:
+ case LayoutType.ICON_HORIZONTAL_TEXT:
return VIEW_TYPE_SEARCH_ICON_ROW;
+ case LayoutType.SMALL_ICON_HORIZONTAL_TEXT:
+ return VIEW_TYPE_SEARCH_SMALL_ICON_ROW;
case LayoutType.THUMBNAIL:
if (t.getSearchAction() != null) {
return VIEW_TYPE_SEARCH_THUMBNAIL;
}
- Log.w(TAG, "Dropping as LayoutType.THUMBNAIL target doesn't contain searchAction.");
+ Log.w(TAG, "LayoutType.THUMBNAIL target doesn't contain searchAction.");
break;
case LayoutType.WIDGET_PREVIEW:
return VIEW_TYPE_SEARCH_WIDGET_PREVIEW;
case LayoutType.WIDGET_LIVE:
return VIEW_TYPE_SEARCH_WIDGET_LIVE;
+ case LayoutType.DIVIDER:
+ return VIEW_TYPE_ALL_APPS_DIVIDER;
}
return -1;
diff --git a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
index b402f61..8983c4f 100644
--- a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_ICON;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_ICON_ROW;
+import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SMALL_ICON_ROW;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SLICE;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_THUMBNAIL;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_WIDGET_LIVE;
@@ -26,7 +27,6 @@
import android.app.search.SearchTarget;
import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
import java.util.ArrayList;
import java.util.List;
@@ -35,31 +35,23 @@
* Extension of AdapterItem that contains an extra payload specific to item
*/
public class SearchAdapterItem extends AllAppsGridAdapter.AdapterItem {
- private SearchTargetLegacy mSearchTargetLegacy;
private SearchTarget mSearchTarget;
private List<SearchTarget> mInlineItems = new ArrayList<>();
- private static final int AVAILABLE_FOR_ACCESSIBILITY =
- VIEW_TYPE_SEARCH_SLICE | VIEW_TYPE_SEARCH_THUMBNAIL | VIEW_TYPE_SEARCH_ICON_ROW
- | VIEW_TYPE_SEARCH_ICON | VIEW_TYPE_SEARCH_WIDGET_PREVIEW
- | VIEW_TYPE_SEARCH_WIDGET_LIVE;
-
-
- public SearchAdapterItem(SearchTargetLegacy searchTargetLegacy, int type) {
- mSearchTargetLegacy = searchTargetLegacy;
- viewType = type;
- }
+ private static final int AVAILABLE_FOR_ACCESSIBILITY = VIEW_TYPE_SEARCH_SLICE
+ | VIEW_TYPE_SEARCH_THUMBNAIL
+ | VIEW_TYPE_SEARCH_ICON_ROW
+ | VIEW_TYPE_SEARCH_ICON
+ | VIEW_TYPE_SEARCH_SMALL_ICON_ROW
+ | VIEW_TYPE_SEARCH_WIDGET_PREVIEW
+ | VIEW_TYPE_SEARCH_WIDGET_LIVE;
public SearchAdapterItem(SearchTarget searchTarget, int type) {
mSearchTarget = searchTarget;
viewType = type;
}
- public SearchTargetLegacy getSearchTargetLegacy() {
- return mSearchTargetLegacy;
- }
-
public SearchTarget getSearchTarget() {
return mSearchTarget;
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
index c353d7a..f7d5f45 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
@@ -49,6 +49,9 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -142,8 +145,14 @@
SearchActionItemInfo itemInfo = new SearchActionItemInfo(searchAction.getIcon(),
searchTarget.getPackageName(), searchTarget.getUserHandle(),
- searchAction.getTitle()
- );
+ searchAction.getTitle()) {
+ // Workaround to log ItemInfo with DeviceSearchResultContainer without
+ // updating ItemInfo.container field.
+ @Override
+ public ContainerInfo getContainerInfo() {
+ return buildDeviceSearchResultContainer();
+ }
+ };
itemInfo.setIntent(searchAction.getIntent());
itemInfo.setPendingIntent(searchAction.getPendingIntent());
@@ -243,7 +252,15 @@
private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
- AppInfo appInfo = appsStore.getApp(new ComponentKey(componentName, userHandle));
+ AppInfo appInfo = new AppInfo(
+ appsStore.getApp(new ComponentKey(componentName, userHandle))) {
+ // Workaround to log ItemInfo with DeviceSearchResultContainer without
+ // updating ItemInfo.container field.
+ @Override
+ public ContainerInfo getContainerInfo() {
+ return buildDeviceSearchResultContainer();
+ }
+ };
if (appInfo == null) {
setVisibility(GONE);
@@ -253,9 +270,15 @@
notifyItemInfoChanged(appInfo);
}
-
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
- WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext()) {
+ // Workaround to log ItemInfo with DeviceSearchResultContainer without
+ // updating ItemInfo.container field.
+ @Override
+ public ContainerInfo getContainerInfo() {
+ return buildDeviceSearchResultContainer();
+ }
+ };
notifyItemInfoChanged(workspaceItemInfo);
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
MODEL_EXECUTOR.execute(() -> {
@@ -293,4 +316,14 @@
mOnItemInfoChanged = null;
}
}
+
+ private static ContainerInfo buildDeviceSearchResultContainer() {
+ return ContainerInfo.newBuilder().setExtendedContainers(
+ ExtendedContainers
+ .newBuilder()
+ .setDeviceSearchResultContainer(
+ DeviceSearchResultContainer
+ .newBuilder()))
+ .build();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
index 7c3ed69..12a1a1c 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
@@ -46,12 +46,12 @@
public static final int MAX_INLINE_ITEMS = 3;
protected final Launcher mLauncher;
- private final LauncherAppState mLauncherAppState;
- protected SearchResultIcon mResultIcon;
+ protected final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS];
+ private SearchResultIcon mResultIcon;
+ private final LauncherAppState mLauncherAppState;
private TextView mTitleView;
private TextView mSubTitleView;
- protected final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS];
private PackageItemInfo mProviderInfo;
@@ -77,13 +77,14 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
int iconSize = getIconSize();
mResultIcon = findViewById(R.id.icon);
+
mTitleView = findViewById(R.id.title);
mSubTitleView = findViewById(R.id.subtitle);
mSubTitleView.setVisibility(GONE);
+
mResultIcon.getLayoutParams().height = iconSize;
mResultIcon.getLayoutParams().width = iconSize;
mResultIcon.setTextVisibility(false);
@@ -94,15 +95,16 @@
for (SearchResultIcon inlineIcon : mInlineIcons) {
inlineIcon.getLayoutParams().width = getIconSize();
}
-
setOnClickListener(mResultIcon);
setOnLongClickListener(mResultIcon);
}
@Override
public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
- showSubtitleIfNeeded(null);
mResultIcon.apply(parentTarget, children, this::onItemInfoCreated);
+
+ showSubtitleIfNeeded(null);
+
if (parentTarget.getShortcutInfo() != null) {
updateWithShortcutInfo(parentTarget.getShortcutInfo());
} else if (parentTarget.getSearchAction() != null) {
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
index c441e22..4bf3432 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
@@ -15,28 +15,22 @@
*/
package com.android.launcher3.search;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
import android.app.search.SearchTarget;
import android.app.search.SearchTargetEvent;
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.lifecycle.LiveData;
-import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.widget.EventInfo;
import androidx.slice.widget.SliceView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.SafeCloseable;
import java.util.ArrayList;
import java.util.List;
@@ -47,13 +41,11 @@
public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler,
SliceView.OnSliceActionListener {
- private static final String TAG = "SearchSliceController";
-
private final Launcher mLauncher;
private SliceView mSliceView;
private SearchResultIcon mIcon;
- private LiveData<Slice> mSliceLiveData;
+ private SafeCloseable mSliceSession;
private String mTargetId;
public SearchResultIconSlice(Context context) {
@@ -87,26 +79,20 @@
mTargetId = parentTarget.getId();
reset();
updateIcon(parentTarget, children);
- try {
- mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri(
- parentTarget.getSliceUri());
- mSliceLiveData.observe(mLauncher, mSliceView);
- } catch (Exception ex) {
- Log.e(TAG, "unable to bind slice", ex);
- }
+ mSliceSession = mLauncher.getLiveSearchManager()
+ .addObserver(parentTarget.getSliceUri(), mSliceView);
}
private void updateIcon(SearchTarget parentTarget, List<SearchTarget> children) {
if (children.size() == 1) {
mIcon.apply(children.get(0), new ArrayList<>());
} else {
- LauncherAppState appState = LauncherAppState.getInstance(getContext());
- MODEL_EXECUTOR.post(() -> {
- PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
- pkgItem.user = parentTarget.getUserHandle();
- appState.getIconCache().getTitleAndIconForApp(pkgItem, false);
- MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem));
- });
+ PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
+ pkgItem.user = parentTarget.getUserHandle();
+ if (!pkgItem.equals(mIcon.getTag())) {
+ // The icon will load and apply high res icon automatically
+ mIcon.applyFromItemInfoWithIcon(pkgItem);
+ }
}
}
@@ -124,8 +110,8 @@
private void reset() {
mSliceView.setOnSliceActionListener(null);
- if (mSliceLiveData != null) {
- mSliceLiveData.removeObservers(mLauncher);
+ if (mSliceSession != null) {
+ mSliceSession.close();
}
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java
new file mode 100644
index 0000000..ca8aa81
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java
@@ -0,0 +1,142 @@
+/*
+ * 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.search;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.app.search.SearchTarget;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.List;
+
+/**
+ * A full width representation of {@link SearchResultIcon} with a secondary label and inline
+ * SearchTargets
+ */
+public class SearchResultSmallIconRow extends LinearLayout implements SearchTargetHandler {
+
+ protected final Launcher mLauncher;
+ private final LauncherAppState mLauncherAppState;
+ protected SearchResultIcon mResultIcon;
+
+ private TextView mTitleView;
+ private TextView mDelimeterView;
+ private TextView mSubTitleView;
+
+ private PackageItemInfo mProviderInfo;
+
+ public SearchResultSmallIconRow(Context context) {
+ this(context, null, 0);
+ }
+
+ public SearchResultSmallIconRow(Context context,
+ @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SearchResultSmallIconRow(Context context,
+ @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(getContext());
+ mLauncherAppState = LauncherAppState.getInstance(getContext());
+ }
+
+ protected int getIconSize() {
+ return mLauncher.getDeviceProfile().allAppsIconSizePx;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ int iconSize = getIconSize();
+
+ mResultIcon = findViewById(R.id.icon);
+
+ mTitleView = findViewById(R.id.title);
+ mDelimeterView = findViewById(R.id.delimeter);
+ mDelimeterView.setVisibility(GONE);
+ mSubTitleView = findViewById(R.id.subtitle);
+ mSubTitleView.setVisibility(GONE);
+
+ mResultIcon.getLayoutParams().height = iconSize;
+ mResultIcon.getLayoutParams().width = iconSize;
+ mResultIcon.setTextVisibility(false);
+
+ setOnClickListener(mResultIcon);
+ setOnLongClickListener(mResultIcon);
+ }
+
+ @Override
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ mResultIcon.apply(parentTarget, children, this::onItemInfoCreated);
+
+ showSubtitleIfNeeded(null);
+
+ if (parentTarget.getShortcutInfo() != null) {
+ updateWithShortcutInfo(parentTarget.getShortcutInfo());
+ } else if (parentTarget.getSearchAction() != null) {
+ showSubtitleIfNeeded(parentTarget.getSearchAction().getSubtitle());
+ }
+ }
+
+ @Override
+ public boolean quickSelect() {
+ this.performClick();
+ return true;
+ }
+
+ private void updateWithShortcutInfo(ShortcutInfo shortcutInfo) {
+ PackageItemInfo packageItemInfo = new PackageItemInfo(shortcutInfo.getPackage());
+ if (packageItemInfo.equals(mProviderInfo)) return;
+ MODEL_EXECUTOR.post(() -> {
+ mLauncherAppState.getIconCache().getTitleAndIconForApp(packageItemInfo, true);
+ MAIN_EXECUTOR.post(() -> {
+ showSubtitleIfNeeded(packageItemInfo.title);
+ mProviderInfo = packageItemInfo;
+ });
+ });
+ }
+
+ protected void showSubtitleIfNeeded(CharSequence subTitle) {
+ if (!TextUtils.isEmpty(subTitle)) {
+ mSubTitleView.setText(subTitle);
+ mSubTitleView.setVisibility(VISIBLE);
+ mDelimeterView.setVisibility(VISIBLE);
+
+ } else {
+ mSubTitleView.setVisibility(GONE);
+ }
+ }
+
+ protected void onItemInfoCreated(ItemInfoWithIcon info) {
+ setTag(info);
+ mTitleView.setText(info.title);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
index 4c64265..e22f6ab 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.search;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
import android.app.search.SearchTarget;
import android.app.search.SearchTargetEvent;
import android.appwidget.AppWidgetHostView;
@@ -36,16 +39,19 @@
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.search.SearchWidgetInfoContainer;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.List;
-
/**
* displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search
* provider
@@ -63,6 +69,7 @@
private final float mScaleToFit;
private SearchWidgetInfoContainer mInfoContainer;
+ private HandlerRunnable mLabelRequest;
private BubbleTextView mWidgetProvider;
private TextView mWidgetLabel;
@@ -124,11 +131,18 @@
}
private void showWidgetInfo(AppWidgetProviderInfo providerInfo) {
- String title = providerInfo.loadLabel(mLauncher.getPackageManager());
PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName());
pinfo.user = providerInfo.getProfile();
mWidgetProvider.applyFromItemInfoWithIcon(pinfo);
- mWidgetLabel.setText(title);
+
+ mLabelRequest = new HandlerRunnable<>(
+ MODEL_EXECUTOR.getHandler(),
+ () -> LauncherAppState.getInstance(mLauncher).getIconCache()
+ .getTitleNoCache(LauncherAppWidgetProviderInfo
+ .fromProviderInfo(mLauncher, providerInfo)),
+ MAIN_EXECUTOR,
+ mWidgetLabel::setText);
+ Utilities.postAsyncCallback(MODEL_EXECUTOR.getHandler(), mLabelRequest);
}
/**
@@ -137,7 +151,18 @@
public void removeListener() {
if (mInfoContainer != null) {
mInfoContainer.detachWidget(mHostView);
+ mInfoContainer = null;
}
+ if (mLabelRequest != null) {
+ mLabelRequest.cancel();
+ mLabelRequest = null;
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ removeListener();
}
private void reportEvent(int eventType) {
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
index 0abed03..ede3b9d 100644
--- a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.launcher3.search;
-import static com.android.app.search.LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT;
-import static com.android.app.search.LayoutType.ICON_SINGLE_HORIZONTAL_TEXT;
+import static com.android.app.search.LayoutType.DIVIDER;
+import static com.android.app.search.LayoutType.ICON_HORIZONTAL_TEXT;
+import static com.android.app.search.LayoutType.SMALL_ICON_HORIZONTAL_TEXT;
import static com.android.app.search.LayoutType.THUMBNAIL;
import static com.android.app.search.ResultType.ACTION;
import static com.android.app.search.ResultType.SCREENSHOT;
@@ -54,17 +56,17 @@
/**
- * Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type.
+ * Generate SearchTargetUtil for ICON_HORIZONTAL_TEXT layout type.
*
* targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction(
* mContext, "red", Color.RED));
* targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction(
* mContext, "yellow", Color.YELLOW));
*/
- public static SearchTarget generateIconDoubleHorizontalText_SearchAction(
+ public static SearchTarget generateIcoHorizontalText_usingSearchAction(
Context context, String id, int color) {
SearchTarget.Builder builder =
- new SearchTarget.Builder(ACTION, ICON_DOUBLE_HORIZONTAL_TEXT, id)
+ new SearchTarget.Builder(ACTION, ICON_HORIZONTAL_TEXT, id)
.setPackageName(PACKAGE2) /* required */
.setUserHandle(USERHANDLE); /* required */
@@ -102,7 +104,7 @@
* targets.add(SearchTargetUtil.generateThumbnail_SearchAction("red", Color.RED));
* targets.add(SearchTargetUtil.generateThumbnail_SearchAction("green", Color.GREEN));
*/
- public static SearchTarget generateThumbnail_SearchAction(String id, int color) {
+ public static SearchTarget generateThumbnail_usingSearchAction(String id, int color) {
SearchTarget.Builder builder =
new SearchTarget.Builder(SCREENSHOT, THUMBNAIL, id)
.setPackageName(PACKAGE2) /* required */
@@ -130,16 +132,19 @@
}
/**
+ * Generate SearchTargetUtil for SMALL_ICON_HORIZONTAL_TEXT layout type.
+ *
* targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction(
* mContext, "red", Color.RED));
* targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction(
* mContext, "yellow", Color.YELLOW));
*/
- public static SearchTarget generateIconHorizontalText_SearchAction(
+ public static SearchTarget generateSmallIconHorizontalText_usingSearchAction(
Context context, String id, int color) {
- String fallbackQuery = "How to make cookie";
+ String title = "Ask the assistant";
+ String fallbackQuery = "sourdough bread";
SearchTarget.Builder builder =
- new SearchTarget.Builder(SUGGEST, ICON_SINGLE_HORIZONTAL_TEXT, id)
+ new SearchTarget.Builder(SUGGEST, SMALL_ICON_HORIZONTAL_TEXT, id)
.setPackageName(PACKAGE2) /* required */
.setUserHandle(USERHANDLE); /* required */
@@ -159,7 +164,8 @@
Bundle extra = new Bundle();
extra.putBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT, true);
- SearchAction searchAction = new SearchAction.Builder(id, fallbackQuery)
+ SearchAction searchAction = new SearchAction.Builder(id, title)
+ .setSubtitle(fallbackQuery)
.setPendingIntent(pendingIntent3)
.setIcon(icon)
.setExtras(extra)
@@ -167,6 +173,14 @@
return builder.setSearchAction(searchAction).build();
}
+ public static SearchTarget generateDivider() {
+ SearchTarget.Builder builder =
+ new SearchTarget.Builder(SUGGEST, DIVIDER, "divider")
+ .setPackageName("") /* required but not used*/
+ .setUserHandle(USERHANDLE); /* required */
+ return builder.build();
+ }
+
/**
* Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type.
@@ -174,7 +188,7 @@
public static SearchTarget generateIconDoubleHorizontalText_ShortcutInfo(Context context) {
String id = "23456";
SearchTarget.Builder builder =
- new SearchTarget.Builder(ResultType.SHORTCUT, ICON_DOUBLE_HORIZONTAL_TEXT, id)
+ new SearchTarget.Builder(ResultType.SHORTCUT, SMALL_ICON_HORIZONTAL_TEXT, id)
.setPackageName("com.google.android.gm") /* required */
.setUserHandle(UserHandle.CURRENT); /* required */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 7608645..6a74aac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -23,6 +23,8 @@
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
import android.animation.Animator;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.view.Gravity;
@@ -42,8 +44,13 @@
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
*/
@@ -60,11 +67,17 @@
private final TaskbarStateHandler mTaskbarStateHandler;
private final TaskbarVisibilityController mTaskbarVisibilityController;
private final TaskbarHotseatController mHotseatController;
+ private final TaskbarRecentsController mRecentsController;
private final TaskbarDragController mDragController;
// Initialized in init().
private WindowManager.LayoutParams mWindowLayoutParams;
+ // Contains all loaded Tasks, not yet deduped from Hotseat items.
+ private List<Task> mLatestLoadedRecentTasks;
+ // Contains all loaded Hotseat items.
+ private ItemInfo[] mLatestLoadedHotseatItems;
+
public TaskbarController(BaseQuickstepLauncher launcher,
TaskbarContainerView taskbarContainerView) {
mLauncher = launcher;
@@ -79,6 +92,8 @@
createTaskbarVisibilityControllerCallbacks());
mHotseatController = new TaskbarHotseatController(mLauncher,
createTaskbarHotseatControllerCallbacks());
+ mRecentsController = new TaskbarRecentsController(mLauncher,
+ createTaskbarRecentsControllerCallbacks());
mDragController = new TaskbarDragController(mLauncher);
}
@@ -101,7 +116,16 @@
return new TaskbarViewCallbacks() {
@Override
public View.OnClickListener getItemOnClickListener() {
- return ItemClickHandler.INSTANCE;
+ return view -> {
+ Object tag = view.getTag();
+ if (tag instanceof Task) {
+ Task task = (Task) tag;
+ ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
+ ActivityOptions.makeBasic());
+ } else {
+ ItemClickHandler.INSTANCE.onClick(view);
+ }
+ };
}
@Override
@@ -116,6 +140,23 @@
@Override
public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
mTaskbarView.updateHotseatItems(hotseatItemInfos);
+ mLatestLoadedHotseatItems = hotseatItemInfos;
+ dedupeAndUpdateRecentItems();
+ }
+ };
+ }
+
+ private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() {
+ return new TaskbarRecentsControllerCallbacks() {
+ @Override
+ public void updateRecentItems(ArrayList<Task> recentTasks) {
+ mLatestLoadedRecentTasks = recentTasks;
+ dedupeAndUpdateRecentItems();
+ }
+
+ @Override
+ public void updateRecentTaskAtIndex(int taskIndex, Task task) {
+ mTaskbarView.updateRecentTaskAtIndex(taskIndex, task);
}
};
}
@@ -124,11 +165,13 @@
* Initializes the Taskbar, including adding it to the screen.
*/
public void init() {
- mTaskbarView.init(mHotseatController.getNumHotseatIcons());
+ mTaskbarView.init(mHotseatController.getNumHotseatIcons(),
+ mRecentsController.getNumRecentIcons());
addToWindowManager();
mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
mTaskbarVisibilityController.init();
mHotseatController.init();
+ mRecentsController.init();
}
private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
@@ -149,6 +192,7 @@
mTaskbarStateHandler.setTaskbarCallbacks(null);
mTaskbarVisibilityController.cleanup();
mHotseatController.cleanup();
+ mRecentsController.cleanup();
}
private void removeFromWindowManager() {
@@ -246,6 +290,52 @@
return mTaskbarView.isDraggingItem();
}
+ private void dedupeAndUpdateRecentItems() {
+ if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) {
+ return;
+ }
+
+ final int numRecentIcons = mRecentsController.getNumRecentIcons();
+
+ // From most recent to least recently opened.
+ List<Task> dedupedTasksInDescendingOrder = new ArrayList<>();
+ for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) {
+ Task task = mLatestLoadedRecentTasks.get(i);
+ boolean isTaskInHotseat = false;
+ for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) {
+ if (hotseatItem == null) {
+ continue;
+ }
+ ComponentName hotseatActivity = hotseatItem.getTargetComponent();
+ if (hotseatActivity != null && task.key.sourceComponent.getPackageName()
+ .equals(hotseatActivity.getPackageName())) {
+ isTaskInHotseat = true;
+ break;
+ }
+ }
+ if (!isTaskInHotseat) {
+ dedupedTasksInDescendingOrder.add(task);
+ if (dedupedTasksInDescendingOrder.size() == numRecentIcons) {
+ break;
+ }
+ }
+ }
+
+ // TaskbarView expects an array of all the recent tasks to show, in the order to show them.
+ // So we create an array of the proper size, then fill it in such that the most recent items
+ // are at the end. If there aren't enough elements to fill the array, leave them null.
+ Task[] tasksArray = new Task[numRecentIcons];
+ for (int i = 0; i < tasksArray.length; i++) {
+ Task task = i >= dedupedTasksInDescendingOrder.size()
+ ? null
+ : dedupedTasksInDescendingOrder.get(i);
+ tasksArray[tasksArray.length - 1 - i] = task;
+ }
+
+ mTaskbarView.updateRecentTasks(tasksArray);
+ mRecentsController.loadIconsForTasks(tasksArray);
+ }
+
/**
* @return Whether the given View is in the same window as Taskbar.
*/
@@ -283,4 +373,12 @@
protected interface TaskbarHotseatControllerCallbacks {
void updateHotseatItems(ItemInfo[] hotseatItemInfos);
}
+
+ /**
+ * Contains methods that TaskbarRecentsController can call to interface with TaskbarController.
+ */
+ protected interface TaskbarRecentsControllerCallbacks {
+ void updateRecentItems(ArrayList<Task> recentTasks);
+ void updateRecentTaskAtIndex(int taskIndex, Task task);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 2318ff9..baec899 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -25,6 +25,7 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Point;
+import android.os.UserHandle;
import android.view.DragEvent;
import android.view.View;
@@ -33,6 +34,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ClipDescriptionCompat;
import com.android.systemui.shared.system.LauncherAppsCompat;
@@ -102,6 +104,15 @@
item.getIntent().getComponent(), null, item.user));
}
intent.putExtra(Intent.EXTRA_USER, item.user);
+ } else if (tag instanceof Task) {
+ Task task = (Task) tag;
+ clipDescription = new ClipDescription(task.titleDescription,
+ new String[] {
+ ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK
+ });
+ intent = new Intent();
+ intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id);
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId));
}
if (clipDescription != null && intent != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
new file mode 100644
index 0000000..9d4e000
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 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.taskbar;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.util.CancellableTask;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.ArrayList;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's Recent items.
+ */
+public class TaskbarRecentsController {
+
+ private final int mNumRecentIcons = 2;
+ private final BaseQuickstepLauncher mLauncher;
+ private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks;
+ private final RecentsModel mRecentsModel;
+
+ private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ reloadRecentTasksIfNeeded();
+ }
+ };
+
+ // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?)
+
+ // Used to keep track of the last requested task list id, so that we do not request to load the
+ // tasks again if we have already requested it and the task list has not changed
+ private int mTaskListChangeId = -1;
+
+ // The current background requests to load the task icons
+ private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons];
+
+ public TaskbarRecentsController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) {
+ mLauncher = launcher;
+ mTaskbarCallbacks = taskbarCallbacks;
+ mRecentsModel = RecentsModel.INSTANCE.get(mLauncher);
+ }
+
+ protected void init() {
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener);
+ reloadRecentTasksIfNeeded();
+ }
+
+ protected void cleanup() {
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mTaskStackChangeListener);
+ cancelAllPendingIconLoadTasks();
+ }
+
+ private void reloadRecentTasksIfNeeded() {
+ if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) {
+ mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged);
+ }
+ }
+
+ private void cancelAllPendingIconLoadTasks() {
+ for (int i = 0; i < mIconLoadRequests.length; i++) {
+ if (mIconLoadRequests[i] != null) {
+ mIconLoadRequests[i].cancel();
+ }
+ mIconLoadRequests[i] = null;
+ }
+ }
+
+ private void onRecentTasksChanged(ArrayList<Task> tasks) {
+ mTaskbarCallbacks.updateRecentItems(tasks);
+ }
+
+ /**
+ * For each Task, loads its icon from the cache in the background, then calls
+ * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}.
+ */
+ protected void loadIconsForTasks(Task[] tasks) {
+ cancelAllPendingIconLoadTasks();
+ for (int i = 0; i < tasks.length; i++) {
+ Task task = tasks[i];
+ if (task == null) {
+ continue;
+ }
+ final int taskIndex = i;
+ mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground(
+ task, updatedTask -> onTaskIconLoaded(task, taskIndex));
+ }
+ }
+
+ private void onTaskIconLoaded(Task task, int taskIndex) {
+ mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task);
+ }
+
+ protected int getNumRecentIcons() {
+ return mNumRecentIcons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index c98f09c..d8f3bb5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -19,6 +19,7 @@
import android.content.res.Resources;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.DragEvent;
import android.view.LayoutInflater;
@@ -35,6 +36,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.recents.model.Task;
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
@@ -52,6 +54,9 @@
// Initialized in init().
private int mHotseatStartIndex;
private int mHotseatEndIndex;
+ private View mHotseatRecentsDivider;
+ private int mRecentsStartIndex;
+ private int mRecentsEndIndex;
private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
@@ -89,10 +94,17 @@
mControllerCallbacks = taskbarViewCallbacks;
}
- protected void init(int numHotseatIcons) {
+ protected void init(int numHotseatIcons, int numRecentIcons) {
mHotseatStartIndex = 0;
mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
updateHotseatItems(new ItemInfo[numHotseatIcons]);
+
+ int dividerIndex = mHotseatEndIndex + 1;
+ mHotseatRecentsDivider = addDivider(dividerIndex);
+
+ mRecentsStartIndex = dividerIndex + 1;
+ mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1;
+ updateRecentTasks(new Task[numRecentIcons]);
}
protected void cleanup() {
@@ -147,6 +159,93 @@
hotseatView.setOnLongClickListener(null);
}
}
+
+ updateHotseatRecentsDividerVisibility();
+ }
+
+ private View addDivider(int dividerIndex) {
+ View divider = inflate(R.layout.taskbar_divider);
+ addView(divider, dividerIndex);
+ return divider;
+ }
+
+ /**
+ * Inflates/binds the Recents items to show in the Taskbar given their Tasks.
+ */
+ protected void updateRecentTasks(Task[] tasks) {
+ for (int i = 0; i < tasks.length; i++) {
+ Task task = tasks[i];
+ int recentsIndex = mRecentsStartIndex + i;
+ View recentsView = getChildAt(recentsIndex);
+
+ // Inflate empty icon Views.
+ if (recentsView == null) {
+ BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon);
+ LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
+ lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+ recentsView = btv;
+ addView(recentsView, recentsIndex, lp);
+ }
+
+ // Apply the Task, or hide the view if there is none for a given index.
+ if (recentsView instanceof BubbleTextView && task != null) {
+ applyTaskToBubbleTextView((BubbleTextView) recentsView, task);
+ recentsView.setVisibility(VISIBLE);
+ recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+ recentsView.setOnLongClickListener(
+ mControllerCallbacks.getItemOnLongClickListener());
+ } else {
+ recentsView.setVisibility(GONE);
+ recentsView.setOnClickListener(null);
+ recentsView.setOnLongClickListener(null);
+ }
+ }
+
+ updateHotseatRecentsDividerVisibility();
+ }
+
+ private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) {
+ if (task.icon != null) {
+ Drawable icon = task.icon.getConstantState().newDrawable().mutate();
+ btv.applyIconAndLabel(icon, task.titleDescription);
+ }
+ btv.setTag(task);
+ }
+
+ protected void updateRecentTaskAtIndex(int taskIndex, Task task) {
+ View taskView = getChildAt(mRecentsStartIndex + taskIndex);
+ if (taskView instanceof BubbleTextView) {
+ applyTaskToBubbleTextView((BubbleTextView) taskView, task);
+ }
+ }
+
+ /**
+ * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in
+ * each, otherwise make it GONE.
+ */
+ private void updateHotseatRecentsDividerVisibility() {
+ if (mHotseatRecentsDivider == null) {
+ return;
+ }
+
+ boolean hasAtLeastOneHotseatItem = false;
+ for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
+ if (getChildAt(i).getVisibility() != GONE) {
+ hasAtLeastOneHotseatItem = true;
+ break;
+ }
+ }
+
+ boolean hasAtLeastOneRecentItem = false;
+ for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) {
+ if (getChildAt(i).getVisibility() != GONE) {
+ hasAtLeastOneRecentItem = true;
+ break;
+ }
+ }
+
+ mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem
+ ? VISIBLE : GONE);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index a00ce56..9a1e707 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -24,6 +25,8 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
+import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
@@ -52,6 +55,7 @@
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.search.DeviceSearchAdapterProvider;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -80,7 +84,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.stream.Stream;
public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -105,6 +111,15 @@
@Override
protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+ // If the app launch is from DeviceSearchResultContainer then add the InstanceId from
+ // LiveSearchManager to recreate the AllApps search session on the server side.
+ Optional<InstanceId> logInstanceId = this.getLiveSearchManager().getLogInstanceId();
+ if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS
+ && info.getContainerInfo().getExtendedContainers().getContainerCase()
+ == DEVICE_SEARCH_RESULT_CONTAINER && logInstanceId.isPresent()) {
+ instanceId = logInstanceId.get();
+ }
+
StatsLogger logger = getStatsLogManager()
.logger().withItemInfo(info).withInstanceId(instanceId);
@@ -152,7 +167,8 @@
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
if (mHotseatPredictionController != null) {
- mHotseatPredictionController.setPauseUIUpdate(true);
+ // Only pause is taskbar controller is not present
+ mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
}
return super.startActivitySafely(v, intent, item);
}
@@ -218,6 +234,15 @@
}
@Override
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+ super.bindWorkspaceItemsChanged(updated);
+ if (getTaskbarController() != null && updated.stream()
+ .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
+ getTaskbarController().onHotseatUpdated();
+ }
+ }
+
+ @Override
public void onDestroy() {
super.onDestroy();
getAppsView().getSearchUiManager().destroy();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ca55468..85b21e0 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,6 +17,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.app.PendingIntent;
import android.app.PictureInPictureParams;
import android.content.ComponentName;
import android.content.Context;
@@ -24,15 +25,18 @@
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
@@ -431,4 +435,84 @@
}
}
}
+
+ @Override
+ public void registerSplitScreenListener(ISplitScreenListener listener) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.registerSplitScreenListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerSplitScreenListener");
+ }
+ }
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.unregisterSplitScreenListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call unregisterSplitScreenListener");
+ }
+ }
+ }
+
+ @Override
+ public void setSideStageVisibility(boolean visible) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setSideStageVisibility(visible);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setSideStageVisibility");
+ }
+ }
+ }
+
+ @Override
+ public void exitSplitScreen() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.exitSplitScreen();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call exitSplitScreen");
+ }
+ }
+ }
+
+ @Override
+ public void startTask(int taskId, int stage, int position, Bundle options) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startTask(taskId, stage, position, options);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startTask");
+ }
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ Bundle options, UserHandle user) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startShortcut(packageName, shortcutId, stage, position, options,
+ user);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startShortcut");
+ }
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startIntent(intent, stage, position, options);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntent");
+ }
+ }
+ }
+
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index d949126..d22496d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,6 +16,7 @@
package com.android.quickstep.logging;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
@@ -72,6 +73,7 @@
private static final int DEFAULT_PAGE_INDEX = -2;
private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
+ private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
new CopyOnWriteArrayList<>();
@@ -397,6 +399,9 @@
} else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
.getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
+ } else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
+ return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
+ + EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
} else {
return info.getContainerInfo().getContainerCase().getNumber();
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2f2b566..d9d0a93 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2277,7 +2277,9 @@
}
public void redrawLiveTile() {
- mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+ if (mLiveTileParams.getTargetSet() != null) {
+ mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+ }
}
public TaskViewSimulator getLiveTileTaskViewSimulator() {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5007ffc..21297c9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -66,9 +66,9 @@
import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -171,7 +171,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
- private IconLoadRequest mIconLoadRequest;
+ private HandlerRunnable mIconLoadRequest;
private boolean mEnableIconUpdateAnimation = false;
@@ -359,6 +359,16 @@
}
/**
+ * Directly set the icon and label.
+ */
+ @UiThread
+ public void applyIconAndLabel(Drawable icon, CharSequence label) {
+ setIcon(icon);
+ setText(label);
+ setContentDescription(label);
+ }
+
+ /**
* Overrides the default long press timeout.
*/
public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index f681d75..4d5bd5d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -168,8 +168,10 @@
// Constants from resources
float swDPs = Utilities.dpiFromPx(
Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
- isTablet = swDPs >= TABLET_MIN_DPS;
- isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS;
+ boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation);
+ // Tablet UI is built with assumption that simulated landscape is disabled.
+ isTablet = allowRotation && swDPs >= TABLET_MIN_DPS;
+ isLargeTablet = isTablet && swDPs >= LARGE_TABLET_MIN_DPS;
isPhone = !isTablet && !isLargeTablet;
aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2334267..36963f1 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -98,13 +98,9 @@
import android.widget.Toast;
import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -211,8 +207,7 @@
* Default launcher application.
*/
public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
- Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin>,
- LifecycleOwner {
+ Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -275,8 +270,6 @@
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
- private LifecycleRegistry mLifecycleRegistry;
-
private LiveSearchManager mLiveSearchManager;
@Thunk
@@ -392,12 +385,12 @@
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
- mLiveSearchManager = new LiveSearchManager(this);
-
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
+ mLiveSearchManager = new LiveSearchManager(this);
+
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
mAppWidgetManager = new WidgetManagerHelper(this);
@@ -486,15 +479,6 @@
if (Utilities.ATLEAST_R) {
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
-
- mLifecycleRegistry = new LifecycleRegistry(this);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
- }
-
- @NonNull
- @Override
- public Lifecycle getLifecycle() {
- return mLifecycleRegistry;
}
public LiveSearchManager getLiveSearchManager() {
@@ -913,7 +897,6 @@
@Override
protected void onStop() {
- mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
super.onStop();
if (mDeferOverlayCallbacks) {
checkIfOverlayStillDeferred();
@@ -937,7 +920,6 @@
mAppWidgetHost.setListenIfResumed(true);
TraceHelper.INSTANCE.endSection(traceToken);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
}
@Override
@@ -1091,7 +1073,6 @@
}
TraceHelper.INSTANCE.endSection(traceToken);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
}
@Override
@@ -1099,7 +1080,6 @@
// Ensure that items added to Launcher are queued until Launcher returns
ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
- mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
super.onPause();
mDragController.cancelDrag();
mLastTouchUpTime = -1;
@@ -1598,7 +1578,6 @@
mAppTransitionManager.unregisterRemoteAnimations();
mAppTransitionManager.unregisterRemoteTransitions();
mUserChangedCallbackCloseable.close();
- mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
mLiveSearchManager.stop();
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2f805fd..a4e1f27 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -268,13 +268,10 @@
&& !FeatureFlags.DISABLE_INITIAL_IME_IN_ALLAPPS.get() && BuildCompat.isAtLeastR()) {
mInsetController.onAnimationEnd(mProgress);
if (Float.compare(mProgress, 0f) == 0) {
- mLauncher.getLiveSearchManager().start();
EditText editText = mAppsView.getSearchUiManager().getEditText();
if (editText != null && !mInsetController.showSearchEduIfNecessary()) {
editText.requestFocus();
}
- } else {
- mLauncher.getLiveSearchManager().stop();
}
// TODO: should make the controller hide synchronously
}
diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
index ec33908..71aedb8 100644
--- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java
+++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
@@ -15,8 +15,14 @@
*/
package com.android.launcher3.allapps.search;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -26,35 +32,49 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
+import android.util.Log;
-import androidx.lifecycle.LiveData;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Observer;
import androidx.slice.Slice;
-import androidx.slice.widget.SliceLiveData;
+import androidx.slice.SliceViewManager;
+import androidx.slice.SliceViewManager.SliceCallback;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Optional;
/**
* Manages Lifecycle for Live search results
*/
-public class LiveSearchManager {
+public class LiveSearchManager implements StateListener<LauncherState> {
+
+ private static final String TAG = "LiveSearchManager";
public static final int SEARCH_APPWIDGET_HOST_ID = 2048;
private final Launcher mLauncher;
- private final AppWidgetManager mAppWidgetManger;
+ private final HashMap<Uri, SliceLifeCycle> mUriSliceMap = new HashMap<>();
+
private final HashMap<ComponentKey, SearchWidgetInfoContainer> mWidgetPlaceholders =
new HashMap<>();
- private final HashMap<Uri, LiveData<Slice>> mUriSliceMap = new HashMap<>();
private SearchWidgetHost mSearchWidgetHost;
+ private InstanceId mLogInstanceId;
public LiveSearchManager(Launcher launcher) {
mLauncher = launcher;
- mAppWidgetManger = AppWidgetManager.getInstance(launcher);
+ mLauncher.getStateManager().addStateListener(this);
}
/**
@@ -63,77 +83,88 @@
*/
public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) {
if (mSearchWidgetHost == null) {
- throw new RuntimeException("AppWidgetHost has not been created yet");
+ mSearchWidgetHost = new SearchWidgetHost(mLauncher);
+ mSearchWidgetHost.startListening();
}
ComponentName provider = providerInfo.provider;
UserHandle userHandle = providerInfo.getProfile();
ComponentKey key = new ComponentKey(provider, userHandle);
- SearchWidgetInfoContainer view = mWidgetPlaceholders.getOrDefault(key, null);
if (mWidgetPlaceholders.containsKey(key)) {
return mWidgetPlaceholders.get(key);
}
+
LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
mLauncher, providerInfo);
PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo);
Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo);
int appWidgetId = mSearchWidgetHost.allocateAppWidgetId();
- boolean success = mAppWidgetManger.bindAppWidgetIdIfAllowed(appWidgetId, userHandle,
- provider, options);
+ boolean success = AppWidgetManager.getInstance(mLauncher)
+ .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options);
if (!success) {
+ mSearchWidgetHost.deleteAppWidgetId(appWidgetId);
mWidgetPlaceholders.put(key, null);
return null;
}
- view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(mLauncher, appWidgetId,
- providerInfo);
+ SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(
+ mLauncher, appWidgetId, providerInfo);
view.setTag(pendingAddWidgetInfo);
mWidgetPlaceholders.put(key, view);
return view;
}
/**
- * Creates {@link LiveData<Slice>} from Slice Uri. Caches created live data to be reused
- * within the same search session. Removes previous observers when new SliceView request a
- * live data for observation.
- */
- public LiveData<Slice> getSliceForUri(Uri sliceUri) {
- LiveData<Slice> sliceLiveData = mUriSliceMap.getOrDefault(sliceUri, null);
- if (sliceLiveData == null) {
- sliceLiveData = SliceLiveData.fromUri(mLauncher, sliceUri);
- mUriSliceMap.put(sliceUri, sliceLiveData);
- }
- return sliceLiveData;
- }
-
- /**
- * Start search session
- */
- public void start() {
- stop();
- mSearchWidgetHost = new SearchWidgetHost(mLauncher);
- mSearchWidgetHost.startListening();
- }
-
- /**
* Stop search session
*/
public void stop() {
+ clearWidgetHost();
+ }
+
+ private void clearWidgetHost() {
if (mSearchWidgetHost != null) {
mSearchWidgetHost.stopListening();
+ mSearchWidgetHost.clearViews();
mSearchWidgetHost.deleteHost();
- for (SearchWidgetInfoContainer placeholder : mWidgetPlaceholders.values()) {
- placeholder.clearListeners();
- }
mWidgetPlaceholders.clear();
mSearchWidgetHost = null;
}
- for (LiveData<Slice> liveData : mUriSliceMap.values()) {
- liveData.removeObservers(mLauncher);
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState != ALL_APPS) {
+ // Clear all search session related objects
+ mUriSliceMap.values().forEach(SliceLifeCycle::destroy);
+ mUriSliceMap.clear();
+
+ clearWidgetHost();
}
- mUriSliceMap.clear();
+ }
+
+ /**
+ * Adds a new observer for the provided uri and returns a callback to cancel this observer
+ */
+ public SafeCloseable addObserver(Uri uri, Observer<Slice> listener) {
+ SliceLifeCycle slc = mUriSliceMap.get(uri);
+ if (slc == null) {
+ slc = new SliceLifeCycle(uri, mLauncher);
+ mUriSliceMap.put(uri, slc);
+ }
+ slc.addListener(listener);
+
+ final SliceLifeCycle sliceLifeCycle = slc;
+ return () -> sliceLifeCycle.removeListener(listener);
+ }
+
+ /**
+ * Returns {@link InstanceId} that should be used for logging events within search session, if
+ * available.
+ */
+ public Optional<InstanceId> getLogInstanceId() {
+ return Optional.ofNullable(mLogInstanceId);
}
static class SearchWidgetHost extends AppWidgetHost {
@@ -146,5 +177,121 @@
AppWidgetProviderInfo appWidget) {
return new SearchWidgetInfoContainer(context);
}
+
+ @Override
+ public void clearViews() {
+ super.clearViews();
+ }
+ }
+
+ private static class SliceLifeCycle
+ implements ActivityLifecycleCallbacks, SliceCallback {
+
+ private final Uri mUri;
+ private final Launcher mLauncher;
+ private final SliceViewManager mSliceViewManager;
+ private final ArrayList<Observer<Slice>> mListeners = new ArrayList<>();
+
+ private boolean mDestroyed = false;
+ private boolean mWasListening = false;
+
+ SliceLifeCycle(Uri uri, Launcher launcher) {
+ mUri = uri;
+ mLauncher = launcher;
+ mSliceViewManager = SliceViewManager.getInstance(launcher);
+ launcher.registerActivityLifecycleCallbacks(this);
+
+ if (launcher.isDestroyed()) {
+ onActivityDestroyed(launcher);
+ } else if (launcher.isStarted()) {
+ onActivityStarted(launcher);
+ }
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ destroy();
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ updateListening();
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ updateListening();
+ }
+
+ private void updateListening() {
+ boolean isListening = mDestroyed
+ ? false
+ : (mLauncher.isStarted() && !mListeners.isEmpty());
+ UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening));
+ }
+
+ @WorkerThread
+ private void uploadListeningBg(boolean isListening) {
+ if (mWasListening != isListening) {
+ mWasListening = isListening;
+ if (isListening) {
+ mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this);
+ // Update slice one-time on the different thread so that we can display
+ // multiple slices in parallel
+ THREAD_POOL_EXECUTOR.execute(this::updateSlice);
+ } else {
+ mSliceViewManager.unregisterSliceCallback(mUri, this);
+ }
+ }
+ }
+
+ @UiThread
+ private void addListener(Observer<Slice> listener) {
+ mListeners.add(listener);
+ updateListening();
+ }
+
+ @UiThread
+ private void removeListener(Observer<Slice> listener) {
+ mListeners.remove(listener);
+ updateListening();
+ }
+
+ @WorkerThread
+ private void updateSlice() {
+ try {
+ Slice s = mSliceViewManager.bindSlice(mUri);
+ MAIN_EXECUTOR.execute(() -> onSliceUpdated(s));
+ } catch (Exception e) {
+ Log.d(TAG, "Error fetching slice", e);
+ }
+ }
+
+ @UiThread
+ @Override
+ public void onSliceUpdated(@Nullable Slice s) {
+ mListeners.forEach(l -> l.onChanged(s));
+ }
+
+ private void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+ mLauncher.unregisterActivityLifecycleCallbacks(this);
+ mListeners.clear();
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) { }
+
+ @Override
+ public void onActivityPaused(Activity activity) { }
+
+ @Override
+ public void onActivityResumed(Activity activity) { }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
}
}
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
index b5c2268..8e5f8cb 100644
--- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
+++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
@@ -70,10 +70,4 @@
mListeners.remove(hostView);
}
- /**
- * Removes all AppWidgetHost update listeners
- */
- public void clearListeners() {
- mListeners.clear();
- }
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 086d665..b61799f 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -98,12 +98,12 @@
public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
"ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
- public static final BooleanFlag USE_SEARCH_API = getDebugFlag(
- "USE_SEARCH_API", true, "Use SearchUIManager api for device search");
-
public static final BooleanFlag DISABLE_INITIAL_IME_IN_ALLAPPS = getDebugFlag(
"DISABLE_INITIAL_IME_IN_ALLAPPS", false, "Disable default IME state in all apps");
+ public static final BooleanFlag DISABLE_SLICE_IN_ALLAPPS = getDebugFlag(
+ "DISABLE_SLICE_IN_ALLAPPS", true, "Disable slice in all apps");
+
public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
"FOLDER_NAME_SUGGEST", true,
"Suggests folder names instead of blank text.");
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 8013557..61f2c2a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -134,7 +133,7 @@
* Fetches high-res icon for the provided ItemInfo and updates the caller when done.
* @return a request ID that can be used to cancel the request.
*/
- public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+ public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
final ItemInfoWithIcon info) {
Preconditions.assertUIThread();
if (mPendingIconRequestCount <= 0) {
@@ -142,20 +141,18 @@
}
mPendingIconRequestCount ++;
- IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
- @Override
- public void run() {
- if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
- getTitleAndIcon(info, false);
- } else if (info instanceof PackageItemInfo) {
- getTitleAndIconForApp((PackageItemInfo) info, false);
- }
- MAIN_EXECUTOR.execute(() -> {
- caller.reapplyItemInfo(info);
- onEnd();
- });
- }
- };
+ HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
+ () -> {
+ if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+ getTitleAndIcon(info, false);
+ } else if (info instanceof PackageItemInfo) {
+ getTitleAndIconForApp((PackageItemInfo) info, false);
+ }
+ return info;
+ },
+ MAIN_EXECUTOR,
+ caller::reapplyItemInfo,
+ this::onIconRequestEnd);
Utilities.postAsyncCallback(mWorkerHandler, request);
return request;
}
@@ -336,12 +333,6 @@
return super.getEntryFromDB(cacheKey, entry, lowRes);
}
- public static abstract class IconLoadRequest extends HandlerRunnable {
- IconLoadRequest(Handler handler, Runnable endRunnable) {
- super(handler, endRunnable);
- }
- }
-
/**
* Interface for receiving itemInfo with high-res icon.
*/
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index d4fa278..e3e4b69 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -203,11 +203,16 @@
/**
* Add the icons for the supplied apk called packageName.
*/
- public void addPackage(Context context, String packageName, UserHandle user) {
- for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class)
- .getActivityList(packageName, user)) {
+ public List<LauncherActivityInfo> addPackage(
+ Context context, String packageName, UserHandle user) {
+ List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, user);
+
+ for (LauncherActivityInfo info : activities) {
add(new AppInfo(context, info, user), info);
}
+
+ return activities;
}
/**
@@ -250,7 +255,8 @@
/**
* Add and remove icons for this package which has been updated.
*/
- public void updatePackage(Context context, String packageName, UserHandle user) {
+ public List<LauncherActivityInfo> updatePackage(
+ Context context, String packageName, UserHandle user) {
final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
.getActivityList(packageName, user);
if (matches.size() > 0) {
@@ -297,6 +303,8 @@
}
}
}
+
+ return matches;
}
/**
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3275d59..f13a109 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
@@ -51,6 +52,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -95,6 +97,7 @@
? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
: ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet<ComponentName> removedComponents = new HashSet<>();
+ final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
switch (mOp) {
case OP_ADD: {
@@ -104,7 +107,8 @@
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
appsList.removePackage(packages[i], mUser);
}
- appsList.addPackage(context, packages[i], mUser);
+ activitiesLists.put(
+ packages[i], appsList.addPackage(context, packages[i], mUser));
}
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
@@ -115,7 +119,8 @@
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
- appsList.updatePackage(context, packages[i], mUser);
+ activitiesLists.put(
+ packages[i], appsList.updatePackage(context, packages[i], mUser));
app.getWidgetCache().removePackage(packages[i], mUser);
}
}
@@ -247,7 +252,14 @@
if (isNewApkAvailable
&& si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
- si.setProgressLevel(100, PackageInstallInfo.STATUS_INSTALLED);
+ List<LauncherActivityInfo> activities = activitiesLists.get(
+ packageName);
+ si.setProgressLevel(
+ activities == null || activities.isEmpty()
+ ? 100
+ : PackageManagerHelper.getLoadingProgress(
+ activities.get(0)),
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index b11b419..3851ab0 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -225,7 +225,7 @@
protected String dumpProperties() {
return "id=" + id
+ " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
- + " container=" + LauncherSettings.Favorites.containerToString(container)
+ + " container=" + getContainerInfo()
+ " targetComponent=" + getTargetComponent()
+ " screen=" + screenId
+ " cell(" + cellX + "," + cellY + ")"
@@ -352,7 +352,10 @@
return itemBuilder;
}
- protected ContainerInfo getContainerInfo() {
+ /**
+ * Returns {@link ContainerInfo} used when logging this item.
+ */
+ public ContainerInfo getContainerInfo() {
switch (container) {
case CONTAINER_HOTSEAT:
return ContainerInfo.newBuilder()
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
deleted file mode 100644
index 0b48c07..0000000
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ /dev/null
@@ -1,105 +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.systemui.plugins;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Parcelable;
-import android.view.View;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Implement this plugin interface to fetch search result data from the plugin side.
- */
-@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 = 9;
-
- /**
- * init plugin
- */
- void setup(Activity activity, View view, boolean useLegacy);
-
- /**
- * Send launcher state related signals.
- */
- void onStateTransitionStart(int fromState, int toState);
-
- void onStateTransitionComplete(int state);
-
- /**
- * Send launcher window focus and visibility changed signals.
- */
- void onWindowFocusChanged(boolean hasFocus);
-
- void onWindowVisibilityChanged(int visibility);
-
- /**
- * Send signal when user starts typing, perform search, notify search target
- * event when search ends.
- */
- void startedSearchSession();
-
- /**
- * Main function that triggers search.
- *
- * @param input string that has been typed by a user
- * @param inputArgs extra info that may be relevant for the input query
- * @param results contains the result that will be rendered in all apps search
- * surface
- * @param cancellationSignal {@link CancellationSignal} can be used to share status of current
- */
- void queryLegacy(String input, Bundle inputArgs, Consumer<List<SearchTargetLegacy>> results,
- CancellationSignal cancellationSignal);
-
- /**
- * Main function that triggers search.
- *
- * @param input string that has been typed by a user
- * @param inputArgs extra info that may be relevant for the input query
- * @param results contains the result that will be rendered in all apps search
- * surface
- * @param cancellationSignal {@link CancellationSignal} can be used to share status of current
- */
- void query(String input, Bundle inputArgs, Consumer<List<Parcelable>> results,
- CancellationSignal cancellationSignal);
-
- /**
- * Send over search target interaction events to Plugin
- */
- void notifySearchTargetEventLegacy(SearchTargetEventLegacy event);
-
- /**
- * Send over search target interaction events to Plugin
- */
- void notifySearchTargetEvent(Parcelable event);
-
- /**
- * Launcher activity lifecycle callbacks
- */
- void onResume(int state);
-
- void onStop(int state);
-}
\ No newline at end of file
diff --git a/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..b90e43b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 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.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+ int VERSION = 1;
+
+ /** Register a listener to get Smartspace data. */
+ void registerListener(SmartspaceTargetListener listener);
+
+ /** Unregister a listener. */
+ void unregisterListener(SmartspaceTargetListener listener);
+
+ /** Provides Smartspace data to registered listeners. */
+ interface SmartspaceTargetListener {
+ /** Each Parcelable is a SmartspaceTarget that represents a card. */
+ void onSmartspaceTargetsUpdated(List<Parcelable> targets);
+ }
+}
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
deleted file mode 100644
index 0fc61f0..0000000
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
+++ /dev/null
@@ -1,95 +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.systemui.plugins.shared;
-
-import android.os.Bundle;
-
-/**
- * Event used for the feedback loop to the plugin. (and future aiai)
- *
- * @deprecated Use {@link android.app.search.SearchTargetEvent}
- */
-@Deprecated
-public class SearchTargetEventLegacy {
- public static final int POSITION_NONE = -1;
-
- public static final int SELECT = 0;
- public static final int QUICK_SELECT = 1;
- public static final int LONG_PRESS = 2;
- public static final int CHILD_SELECT = 3;
-
- private final SearchTargetLegacy mSearchTarget;
- private final int mEventType;
- private final int mShortcutPosition;
- private final Bundle mExtras;
-
- public SearchTargetEventLegacy(SearchTargetLegacy searchTarget, int eventType,
- int shortcutPosition,
- Bundle extras) {
- mSearchTarget = searchTarget;
- mEventType = eventType;
- mShortcutPosition = shortcutPosition;
- mExtras = extras;
- }
-
-
- public SearchTargetLegacy getSearchTarget() {
- return mSearchTarget;
- }
-
- public int getShortcutPosition() {
- return mShortcutPosition;
- }
-
- public int getEventType() {
- return mEventType;
- }
-
- public Bundle getExtras() {
- return mExtras;
- }
-
- /**
- * A builder for {@link SearchTargetLegacy}
- */
- public static final class Builder {
- private final SearchTargetLegacy mSearchTarget;
- private final int mEventType;
- private int mShortcutPosition = POSITION_NONE;
- private Bundle mExtras;
-
- public Builder(SearchTargetLegacy searchTarget, int eventType) {
- mSearchTarget = searchTarget;
- mEventType = eventType;
- }
-
- public Builder setShortcutPosition(int shortcutPosition) {
- mShortcutPosition = shortcutPosition;
- return this;
- }
-
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- public SearchTargetEventLegacy build() {
- return new SearchTargetEventLegacy(mSearchTarget, mEventType, mShortcutPosition,
- mExtras);
- }
- }
-
-}
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java
deleted file mode 100644
index 2a6ba88..0000000
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java
+++ /dev/null
@@ -1,175 +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.systemui.plugins.shared;
-
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import java.util.List;
-
-/**
- * Used to return all apps search targets.
- *
- * @deprecated Use SearchTarget
- */
-@Deprecated
-public class SearchTargetLegacy implements Comparable<SearchTargetLegacy> {
-
- private final String mItemId;
- private final String mItemType;
- private final float mScore;
-
- private final ComponentName mComponentName;
- private final UserHandle mUserHandle;
- private final List<ShortcutInfo> mShortcutInfos;
- //TODO: (sfufa) replace with a list of a custom type
- private final RemoteAction mRemoteAction;
- private final Bundle mExtras;
-
- private SearchTargetLegacy(String itemId, String itemType, float score,
- ComponentName componentName, UserHandle userHandle, List<ShortcutInfo> shortcutInfos,
- RemoteAction remoteAction, Bundle extras) {
- mItemId = itemId;
- mItemType = itemType;
- mScore = score;
- mComponentName = componentName;
- mUserHandle = userHandle;
- mShortcutInfos = shortcutInfos;
- mExtras = extras;
- mRemoteAction = remoteAction;
- }
-
- public String getItemId() {
- return mItemId;
- }
-
- public String getItemType() {
- return mItemType;
- }
-
- public ComponentName getComponentName() {
- return mComponentName;
- }
-
- public UserHandle getUserHandle() {
- return mUserHandle;
- }
-
- public float getScore() {
- return mScore;
- }
-
- public List<ShortcutInfo> getShortcutInfos() {
- return mShortcutInfos;
- }
-
- public Bundle getExtras() {
- return mExtras;
- }
-
- public RemoteAction getRemoteAction() {
- return mRemoteAction;
- }
-
- @Override
- public int compareTo(SearchTargetLegacy o) {
- return Float.compare(o.mScore, mScore);
- }
-
- /**
- * A builder for {@link SearchTargetLegacy}
- */
- public static final class Builder {
-
-
- private String mItemId;
-
- private final String mItemType;
- private final float mScore;
-
-
- private ComponentName mComponentName;
- private UserHandle mUserHandle;
- private List<ShortcutInfo> mShortcutInfos;
- private Bundle mExtras;
- private RemoteAction mRemoteAction;
-
- public Builder(String itemType, float score) {
- this(itemType, score, null, null);
- }
-
- public Builder(String itemType, float score, ComponentName cn,
- UserHandle user) {
- mItemType = itemType;
- mScore = score;
- mComponentName = cn;
- mUserHandle = user;
- }
-
- public String getItemId() {
- return mItemId;
- }
-
- public float getScore() {
- return mScore;
- }
-
- public Builder setItemId(String itemId) {
- mItemId = itemId;
- return this;
- }
-
- public Builder setComponentName(ComponentName componentName) {
- mComponentName = componentName;
- return this;
- }
-
- public Builder setUserHandle(UserHandle userHandle) {
- mUserHandle = userHandle;
- return this;
- }
-
- public Builder setShortcutInfos(List<ShortcutInfo> shortcutInfos) {
- mShortcutInfos = shortcutInfos;
- return this;
- }
-
- public Builder setExtras(Bundle extras) {
- mExtras = extras;
- return this;
- }
-
- public Builder setRemoteAction(RemoteAction remoteAction) {
- mRemoteAction = remoteAction;
- return this;
- }
-
- /**
- * Builds a {@link SearchTargetLegacy}
- */
- public SearchTargetLegacy build() {
- if (mItemId == null) {
- throw new IllegalStateException("Item ID is required for building SearchTarget");
- }
- return new SearchTargetLegacy(mItemId, mItemType, mScore, mComponentName, mUserHandle,
- mShortcutInfos,
- mRemoteAction, mExtras);
- }
- }
-}