Merge "Revert "Make keyguard exit animation to remote animation.""
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 9e76ce3..4fd2e40 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -141,7 +141,7 @@
TODO: Add proper permissions
-->
<provider
- android:name="com.android.launcher3.graphics.GridOptionsProvider"
+ android:name="com.android.launcher3.graphics.GridCustomizationsProvider"
android:authorities="${packageName}.grid_control"
android:exported="true" />
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index d95cc01..9c14b85 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -31,6 +31,15 @@
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS"/>
+ <uses-permission android:name="android.permission.REMOVE_TASKS"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
+ <uses-permission android:name="android.permission.STATUS_BAR"/>
+ <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
+ <uses-permission android:name="android.permission.SET_ORIENTATION"/>
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+ <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
+
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
diff --git a/quickstep/res/layout/search_result_icon_row.xml b/quickstep/res/layout/search_result_icon_row.xml
index 2119fc3..81190cf 100644
--- a/quickstep/res/layout/search_result_icon_row.xml
+++ b/quickstep/res/layout/search_result_icon_row.xml
@@ -57,9 +57,10 @@
style="@style/BaseIcon"
android:layout_width="@dimen/deep_shortcut_icon_size"
android:layout_height="match_parent"
- android:gravity="start|center_vertical"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
launcher:iconDisplay="shortcut_popup"
- android:textSize="@dimen/search_hero_subtitle_size"
+ android:textSize="@dimen/search_hero_inline_button_size"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
launcher:layoutHorizontal="false" />
@@ -68,6 +69,20 @@
style="@style/BaseIcon"
android:layout_width="@dimen/deep_shortcut_icon_size"
android:layout_height="match_parent"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
+ launcher:iconDisplay="shortcut_popup"
+ android:textSize="@dimen/search_hero_inline_button_size"
+ launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
+ launcher:layoutHorizontal="false" />
+
+ <com.android.launcher3.search.SearchResultIcon
+ android:id="@+id/shortcut_2"
+ style="@style/BaseIcon"
+ android:layout_width="@dimen/deep_shortcut_icon_size"
+ android:layout_height="match_parent"
+ android:layout_marginStart="4dp"
+ android:gravity="center"
launcher:iconDisplay="shortcut_popup"
android:textSize="@dimen/search_hero_inline_button_size"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
diff --git a/quickstep/res/layout/search_result_people_item.xml b/quickstep/res/layout/search_result_people_item.xml
deleted file mode 100644
index 964300d..0000000
--- a/quickstep/res/layout/search_result_people_item.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.search.SearchResultPeopleView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:gravity="center_vertical"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:orientation="horizontal">
-
- <View
- android:id="@+id/icon"
- android:layout_marginRight="8dp"
- android:layout_width="@dimen/deep_shortcut_icon_size"
- android:layout_height="@dimen/deep_shortcut_icon_size" />
-
- <TextView
- android:layout_width="0dp"
- android:textColor="?android:attr/textColorPrimary"
- android:id="@+id/title"
- android:textSize="@dimen/search_hero_title_size"
- android:layout_height="wrap_content"
- android:layout_weight="1" />
-
- <ImageButton
- android:id="@+id/provider_0"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:layout_margin="5dp"
- android:background="?android:attr/selectableItemBackground"
- android:layout_width="@dimen/deep_shortcut_icon_size"
- android:layout_height="@dimen/deep_shortcut_icon_size" />
-
- <ImageButton
- android:id="@+id/provider_1"
- android:layout_margin="5dp"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:background="?android:attr/selectableItemBackground"
- android:layout_width="@dimen/deep_shortcut_icon_size"
- android:layout_height="@dimen/deep_shortcut_icon_size" />
-
- <ImageButton
- android:id="@+id/provider_2"
- android:layout_margin="5dp"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:background="?android:attr/selectableItemBackground"
- android:layout_width="@dimen/deep_shortcut_icon_size"
- android:layout_height="@dimen/deep_shortcut_icon_size" />
-
-</com.android.launcher3.search.SearchResultPeopleView>
\ No newline at end of file
diff --git a/quickstep/res/layout/search_result_suggest.xml b/quickstep/res/layout/search_result_suggest.xml
deleted file mode 100644
index eb5313c..0000000
--- a/quickstep/res/layout/search_result_suggest.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.search.SearchResultSuggestion xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- style="@style/BaseIcon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="start|center_vertical"
- android:textAlignment="viewStart"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="18sp"
- android:padding="@dimen/dynamic_grid_edge_margin"
- launcher:iconDisplay="hero_app"
- android:drawableTint="?android:attr/textColorPrimary"
- launcher:customIcon="@drawable/ic_allapps_search"
- launcher:iconSizeOverride="24dp"
- launcher:matchTextInsetWithQuery="true"
- launcher:layoutHorizontal="true"
- android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding">
-
-</com.android.launcher3.search.SearchResultSuggestion>
\ No newline at end of file
diff --git a/quickstep/res/layout/search_result_thumbnail.xml b/quickstep/res/layout/search_result_thumbnail.xml
index 0f25336..5062b76 100644
--- a/quickstep/res/layout/search_result_thumbnail.xml
+++ b/quickstep/res/layout/search_result_thumbnail.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.search.ThumbnailSearchResultView
+<com.android.launcher3.search.SearchResultThumbnailView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="125dp"
android:layout_height="125dp"/>
\ No newline at end of file
diff --git a/quickstep/res/layout/search_result_widget_live.xml b/quickstep/res/layout/search_result_widget_live.xml
index ffbad55..f2ac6cd 100644
--- a/quickstep/res/layout/search_result_widget_live.xml
+++ b/quickstep/res/layout/search_result_widget_live.xml
@@ -1,5 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
-<com.android.launcher3.search.SearchResultWidget android:layout_height="wrap_content"
+<com.android.launcher3.search.SearchResultWidget
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingVertical="@dimen/widget_section_vertical_padding"
+ android:layout_marginBottom="@dimen/widget_section_vertical_padding"
android:gravity="center"
- android:layout_width="match_parent" />
\ No newline at end of file
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:layout_height="wrap_content">
+
+ <com.android.launcher3.BubbleTextView
+ android:id="@+id/widget_provider"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/widget_section_height"
+ android:drawablePadding="@dimen/widget_section_horizontal_padding"
+ android:focusable="true"
+ android:gravity="start|center_vertical"
+ android:paddingHorizontal="@dimen/widget_section_horizontal_padding"
+ android:paddingVertical="@dimen/widget_section_horizontal_padding"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:textAlignment="viewStart"
+ launcher:iconDisplay="widget_section"
+ launcher:layoutHorizontal="true"
+ launcher:iconSizeOverride="@dimen/widget_section_icon_size" />
+
+ <TextView
+ android:id="@+id/widget_label"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/widget_section_height"
+ android:textSize="16sp" />
+
+ </LinearLayout>
+
+</com.android.launcher3.search.SearchResultWidget>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 5f1046d..b124b33 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -24,6 +24,8 @@
android:id="@+id/taskbar_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@color/taskbar_background"/>
+ android:background="@color/taskbar_background"
+ android:gravity="center"
+ android:animateLayoutChanges="true"/>
</com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_app_icon.xml b/quickstep/res/layout/taskbar_app_icon.xml
new file mode 100644
index 0000000..6fefdb6
--- /dev/null
+++ b/quickstep/res/layout/taskbar_app_icon.xml
@@ -0,0 +1,17 @@
+<?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.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/quickstep/res/layout/taskbar_predicted_app_icon.xml b/quickstep/res/layout/taskbar_predicted_app_icon.xml
new file mode 100644
index 0000000..211ebc8
--- /dev/null
+++ b/quickstep/res/layout/taskbar_predicted_app_icon.xml
@@ -0,0 +1,17 @@
+<?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.uioverrides.PredictedAppIcon style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 4272f50..39cc0b8 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -122,4 +122,9 @@
<!-- Taskbar -->
<dimen name="taskbar_size">48dp</dimen>
+ <dimen name="taskbar_icon_size">32dp</dimen>
+ <dimen name="taskbar_icon_touch_size">48dp</dimen>
+ <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>
</resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 8d054b4..5a353f0 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -85,4 +85,10 @@
<item name="android:drawablePadding">8dp</item>
<item name="android:textAllCaps">false</item>
</style>
+
+ <!-- Icon displayed on the taskbar -->
+ <style name="BaseIcon.Workspace.Taskbar" >
+ <item name="iconDisplay">taskbar</item>
+ <item name="iconSizeOverride">@dimen/taskbar_icon_size</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2518f42..cbe0eb0 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -45,6 +45,7 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.taskbar.TaskbarContainerView;
import com.android.launcher3.taskbar.TaskbarController;
+import com.android.launcher3.taskbar.TaskbarStateHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.UiThreadHelper;
@@ -82,6 +83,7 @@
private OverviewActionsView mActionsView;
private @Nullable TaskbarController mTaskbarController;
+ private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -245,13 +247,28 @@
getWorkspace(),
getDepthController(),
new RecentsViewStateController(this),
- new BackButtonAlphaHandler(this)};
+ new BackButtonAlphaHandler(this),
+ getTaskbarStateHandler(),
+ };
}
public DepthController getDepthController() {
return mDepthController;
}
+ public @Nullable TaskbarController getTaskbarController() {
+ return mTaskbarController;
+ }
+
+ public TaskbarStateHandler getTaskbarStateHandler() {
+ return mTaskbarStateHandler;
+ }
+
+ @Override
+ public boolean isViewInTaskbar(View v) {
+ return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
+ }
+
@Override
public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
QuickstepAppTransitionManagerImpl appTransitionManager =
@@ -296,6 +313,12 @@
mDepthController.setActivityStarted(isStarted());
}
+ if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+ if (mTaskbarController != null) {
+ mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
+ }
+ }
+
super.onActivityFlagsChanged(changeBits);
}
@@ -332,6 +355,10 @@
// 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/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 470a442..876cabc 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -139,7 +139,7 @@
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
- protected static final int CONTENT_ALPHA_DURATION = 217;
+ public static final int CONTENT_ALPHA_DURATION = 217;
protected static final int CONTENT_TRANSLATION_DURATION = 350;
// Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
@@ -202,9 +202,10 @@
}
@Override
- public boolean supportsAdaptiveIconAnimation() {
+ public boolean supportsAdaptiveIconAnimation(View clickedView) {
return hasControlRemoteAppTransitionPermission()
- && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
+ && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
+ && !mLauncher.isViewInTaskbar(clickedView);
}
/**
@@ -972,9 +973,12 @@
launcherIsATargetWithMode(appTargets, MODE_CLOSING);
final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
+ final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
if (launchingFromRecents) {
composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
launcherClosing);
+ } else if (launchingFromTaskbar) {
+ // TODO
} else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
launcherClosing);
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index b2de4c9..aa6601b 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -37,7 +37,6 @@
import com.android.launcher3.DropTarget;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -75,7 +74,7 @@
private int mHotSeatItemsCount;
- private Launcher mLauncher;
+ private QuickstepLauncher mLauncher;
private final Hotseat mHotseat;
private List<ItemInfo> mPredictedItems = Collections.emptyList();
@@ -108,7 +107,7 @@
return true;
};
- public HotseatPredictionController(Launcher launcher) {
+ public HotseatPredictionController(QuickstepLauncher launcher) {
mLauncher = launcher;
mHotseat = launcher.getHotseat();
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
@@ -229,6 +228,10 @@
} else {
if (callback != null) callback.run();
}
+
+ if (mLauncher.getTaskbarController() != null) {
+ mLauncher.getTaskbarController().onHotseatUpdated();
+ }
}
/**
@@ -242,6 +245,10 @@
* 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;
if (!paused) {
fillGapsWithPrediction();
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index 834a624..acf6c15 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ICON;
import android.app.search.SearchTarget;
+import android.util.Log;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,7 +31,6 @@
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,14 +41,13 @@
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_PEOPLE = 1 << 11;
public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12;
- public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
public static final int VIEW_TYPE_SEARCH_WIDGET_LIVE = 1 << 15;
public static final int VIEW_TYPE_SEARCH_WIDGET_PREVIEW = 1 << 16;
- private final AllAppsContainerView mAppsView;
+ private static final String TAG = "SearchServiceAdapterProvider";
+ private final AllAppsContainerView mAppsView;
private final SparseIntArray mViewTypeToLayoutMap = new SparseIntArray();
public DeviceSearchAdapterProvider(Launcher launcher, AllAppsContainerView appsView) {
@@ -59,9 +58,7 @@
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_SLICE, R.layout.search_result_slice);
- mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_PEOPLE, R.layout.search_result_people_item);
mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_THUMBNAIL, R.layout.search_result_thumbnail);
- mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SUGGEST, R.layout.search_result_suggest);
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);
@@ -74,11 +71,7 @@
SearchTargetHandler
payloadResultView =
(SearchTargetHandler) holder.itemView;
- if (!FeatureFlags.USE_SEARCH_API.get()) {
- payloadResultView.applySearchTarget(item.getSearchTargetLegacy());
- } else {
- payloadResultView.applySearchTarget(item.getSearchTarget(), item.getInlineItems());
- }
+ payloadResultView.apply(item.getSearchTarget(), item.getInlineItems());
}
@Override
@@ -113,7 +106,8 @@
/**
* Determines what view type should be used to present search target.
- * Returns -1 if viewType is not found
+ * Returns -1 if viewType is not found or if required field is not present
+ * to render the viewType.
*/
public int getViewTypeForSearchTarget(SearchTarget t) {
switch (t.getLayoutType()) {
@@ -122,14 +116,27 @@
case LayoutType.ICON_SINGLE_VERTICAL_TEXT:
return VIEW_TYPE_SEARCH_ICON;
case LayoutType.ICON_SLICE:
- return VIEW_TYPE_SEARCH_SLICE;
- case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT_BUTTON:
+ if (t.getSliceUri() != null) {
+ return VIEW_TYPE_SEARCH_SLICE;
+ }
+ Log.w(TAG, "Dropping as 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:
return VIEW_TYPE_SEARCH_ICON_ROW;
- default:
- return -1;
-
+ case LayoutType.THUMBNAIL:
+ if (t.getSearchAction() != null) {
+ return VIEW_TYPE_SEARCH_THUMBNAIL;
+ }
+ Log.w(TAG, "Dropping as 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;
}
+
+ return -1;
}
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
index 01b1999..b402f61 100644
--- a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
@@ -18,9 +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_PEOPLE;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SLICE;
-import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SUGGEST;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_THUMBNAIL;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_WIDGET_LIVE;
import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_WIDGET_PREVIEW;
@@ -43,17 +41,16 @@
private static final int AVAILABLE_FOR_ACCESSIBILITY =
- VIEW_TYPE_SEARCH_SLICE | VIEW_TYPE_SEARCH_PEOPLE | VIEW_TYPE_SEARCH_THUMBNAIL
- | VIEW_TYPE_SEARCH_ICON_ROW | VIEW_TYPE_SEARCH_ICON
- | VIEW_TYPE_SEARCH_WIDGET_PREVIEW | VIEW_TYPE_SEARCH_WIDGET_LIVE
- | VIEW_TYPE_SEARCH_SUGGEST;
+ 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;
}
-
public SearchAdapterItem(SearchTarget searchTarget, int type) {
mSearchTarget = searchTarget;
viewType = type;
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
index 0b8d3cb..c353d7a 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
@@ -15,17 +15,26 @@
*/
package com.android.launcher3.search;
+import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_BADGE_WITH_PACKAGE;
+import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_PRIMARY_ICON_FROM_TITLE;
+import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.app.search.SearchAction;
import android.app.search.SearchTarget;
+import android.app.search.SearchTargetEvent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Icon;
-import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -37,15 +46,22 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Themes;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
import java.util.List;
import java.util.function.Consumer;
@@ -56,13 +72,17 @@
SearchTargetHandler, View.OnClickListener,
View.OnLongClickListener {
- private static final String BUNDLE_EXTRA_SHOULD_START = "should_start";
- private static final String BUNDLE_EXTRA_SHOULD_START_FOR_RESULT = "should_start_for_result";
+ //Play store thumbnail process workaround
+ private final Rect mTempRect = new Rect();
+ private final Paint mIconPaint = new Paint();
+ private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
private final Launcher mLauncher;
+ private String mTargetId;
private Consumer<ItemInfoWithIcon> mOnItemInfoChanged;
+
public SearchResultIcon(Context context) {
this(context, null, 0);
}
@@ -93,73 +113,134 @@
* Applies {@link SearchTarget} to view. registers a consumer after a corresponding
* {@link ItemInfoWithIcon} is created
*/
- public void applySearchTarget(SearchTarget searchTarget, List<SearchTarget> inlineItems,
+ public void apply(SearchTarget searchTarget, List<SearchTarget> inlineItems,
Consumer<ItemInfoWithIcon> cb) {
mOnItemInfoChanged = cb;
- applySearchTarget(searchTarget, inlineItems);
+ apply(searchTarget, inlineItems);
}
@Override
- public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
- switch (parentTarget.getResultType()) {
- case ResultType.APPLICATION:
- prepareUsingApp(new ComponentName(parentTarget.getPackageName(),
- parentTarget.getExtras().getString("class")), parentTarget.getUserHandle());
- mLongPressSupported = true;
- break;
- case ResultType.SHORTCUT:
- prepareUsingShortcutInfo(parentTarget.getShortcutInfo());
- mLongPressSupported = true;
- break;
- default:
- prepareUsingSearchAction(parentTarget);
- mLongPressSupported = false;
- break;
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ mTargetId = parentTarget.getId();
+ if (parentTarget.getShortcutInfo() != null) {
+ prepareUsingShortcutInfo(parentTarget.getShortcutInfo());
+ mLongPressSupported = true;
+ } else if (parentTarget.getSearchAction() != null) {
+ prepareUsingSearchAction(parentTarget);
+ mLongPressSupported = false;
+ } else {
+ String className = parentTarget.getExtras().getString(SearchTargetUtil.EXTRA_CLASS);
+ prepareUsingApp(new ComponentName(parentTarget.getPackageName(), className),
+ parentTarget.getUserHandle());
+ mLongPressSupported = true;
}
}
private void prepareUsingSearchAction(SearchTarget searchTarget) {
SearchAction searchAction = searchTarget.getSearchAction();
Bundle extras = searchAction.getExtras();
+
SearchActionItemInfo itemInfo = new SearchActionItemInfo(searchAction.getIcon(),
searchTarget.getPackageName(), searchTarget.getUserHandle(),
- searchAction.getTitle());
+ searchAction.getTitle()
+ );
itemInfo.setIntent(searchAction.getIntent());
itemInfo.setPendingIntent(searchAction.getPendingIntent());
//TODO: remove this after flags are introduced in SearchAction. Settings results require
// startActivityForResult
boolean isSettingsResult = searchTarget.getResultType() == ResultType.SETTING;
- if ((extras != null && extras.getBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT))
+ if ((extras != null && extras.getBoolean(
+ SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START_FOR_RESULT))
|| isSettingsResult) {
itemInfo.setFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT);
- } else if (extras != null && extras.getBoolean(BUNDLE_EXTRA_SHOULD_START)) {
+ } else if (extras != null && extras.getBoolean(
+ SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START)) {
itemInfo.setFlags(SearchActionItemInfo.FLAG_SHOULD_START);
}
-
+ if (extras != null && extras.getBoolean(
+ SearchTargetUtil.BUNDLE_EXTRA_BADGE_WITH_PACKAGE)) {
+ itemInfo.setFlags(FLAG_BADGE_WITH_PACKAGE);
+ }
+ if (extras != null && extras.getBoolean(BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE)) {
+ itemInfo.setFlags(FLAG_PRIMARY_ICON_FROM_TITLE);
+ }
notifyItemInfoChanged(itemInfo);
LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
MODEL_EXECUTOR.post(() -> {
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
Icon icon = searchTarget.getSearchAction().getIcon();
- Drawable d;
- if (icon == null) {
- PackageItemInfo pkgInfo = new PackageItemInfo(searchTarget.getPackageName());
- pkgInfo.user = searchTarget.getUserHandle();
- appState.getIconCache().getTitleAndIconForApp(pkgInfo, false);
- itemInfo.bitmap = pkgInfo.bitmap;
+ BitmapInfo pkgBitmap = getPackageBitmap(appState, searchTarget);
+ if (itemInfo.hasFlags(FLAG_PRIMARY_ICON_FROM_TITLE)) {
+ // create a bitmap with first char if FLAG_PRIMARY_ICON_FROM_TITLE is set
+ itemInfo.bitmap = li.createIconBitmap(String.valueOf(itemInfo.title.charAt(0)),
+ pkgBitmap.color);
+ } else if (icon == null) {
+ // Use default icon from package name
+ itemInfo.bitmap = pkgBitmap;
} else {
- d = itemInfo.getIcon().loadDrawable(getContext());
- itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
- Build.VERSION.SDK_INT);
+ boolean isPlayResult = searchTarget.getResultType() == ResultType.PLAY;
+ if (isPlayResult) {
+ Bitmap b = getPlayResultBitmap(searchAction.getIcon());
+ itemInfo.bitmap = b == null
+ ? BitmapInfo.LOW_RES_INFO : BitmapInfo.fromBitmap(b);
+ } else {
+ itemInfo.bitmap = li.createBadgedIconBitmap(icon.loadDrawable(getContext()),
+ itemInfo.user, false);
+ }
}
+ // badge with package name
+ if (itemInfo.hasFlags(FLAG_BADGE_WITH_PACKAGE) && itemInfo.bitmap != pkgBitmap) {
+ itemInfo.bitmap = li.badgeBitmap(itemInfo.bitmap.icon, pkgBitmap);
+ }
}
MAIN_EXECUTOR.post(() -> applyFromSearchActionItemInfo(itemInfo));
});
}
+ private static BitmapInfo getPackageBitmap(LauncherAppState appState, SearchTarget target) {
+ PackageItemInfo pkgInfo = new PackageItemInfo(target.getPackageName());
+ pkgInfo.user = target.getUserHandle();
+ appState.getIconCache().getTitleAndIconForApp(pkgInfo, false);
+ return pkgInfo.bitmap;
+ }
+
+ private Bitmap getPlayResultBitmap(Icon icon) {
+ try {
+ int iconSize = getIconSize();
+ URL url = new URL(icon.getUri().toString());
+ URLConnection con = url.openConnection();
+ con.addRequestProperty("Cache-Control", "max-age: 0");
+ con.setUseCaches(true);
+ Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
+ return getRoundedBitmap(Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, false));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private Bitmap getRoundedBitmap(Bitmap bitmap) {
+ final int iconSize = bitmap.getWidth();
+ final float radius = Themes.getDialogCornerRadius(getContext());
+
+ return BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
+ mTempRect.set(0, 0, iconSize, iconSize);
+ final RectF rectF = new RectF(mTempRect);
+
+ mIconPaint.setAntiAlias(true);
+ mIconPaint.reset();
+ canvas.drawARGB(0, 0, 0, 0);
+ mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
+ canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
+
+ mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
+ });
+ }
+
private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
AppInfo appInfo = appsStore.getApp(new ComponentKey(componentName, userHandle));
@@ -185,26 +266,27 @@
@Override
public boolean quickSelect() {
- //TODO: event reporting
this.performClick();
+ notifyEvent(mLauncher, mTargetId, SearchTargetEvent.ACTION_LAUNCH_KEYBOARD_FOCUS);
return true;
}
@Override
public void onClick(View view) {
- //TODO: event reporting
- mLauncher.getItemOnClickListener().onClick(this);
+ ItemClickHandler.INSTANCE.onClick(view);
+ notifyEvent(mLauncher, mTargetId, SearchTargetEvent.ACTION_LAUNCH_TOUCH);
}
@Override
public boolean onLongClick(View view) {
- //TODO: event reporting
if (!mLongPressSupported) {
return false;
}
+ notifyEvent(mLauncher, mTargetId, SearchTargetEvent.ACTION_LONGPRESS);
return ItemLongClickListener.INSTANCE_ALL_APPS.onLongClick(this);
}
+
private void notifyItemInfoChanged(ItemInfoWithIcon itemInfoWithIcon) {
if (mOnItemInfoChanged != null) {
mOnItemInfoChanged.accept(itemInfoWithIcon);
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
index 4fb668e..7c3ed69 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
@@ -43,15 +43,15 @@
*/
public class SearchResultIconRow extends LinearLayout implements SearchTargetHandler {
- public static final int MAX_INLINE_ITEMS = 2;
+ public static final int MAX_INLINE_ITEMS = 3;
protected final Launcher mLauncher;
private final LauncherAppState mLauncherAppState;
- private SearchResultIcon mResultIcon;
+ protected SearchResultIcon mResultIcon;
private TextView mTitleView;
private TextView mSubTitleView;
- private final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS];
+ protected final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS];
private PackageItemInfo mProviderInfo;
@@ -90,6 +90,7 @@
mInlineIcons[0] = findViewById(R.id.shortcut_0);
mInlineIcons[1] = findViewById(R.id.shortcut_1);
+ mInlineIcons[2] = findViewById(R.id.shortcut_2);
for (SearchResultIcon inlineIcon : mInlineIcons) {
inlineIcon.getLayoutParams().width = getIconSize();
}
@@ -99,8 +100,9 @@
}
@Override
- public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
- mResultIcon.applySearchTarget(parentTarget, children, this::onItemInfoCreated);
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ showSubtitleIfNeeded(null);
+ mResultIcon.apply(parentTarget, children, this::onItemInfoCreated);
if (parentTarget.getShortcutInfo() != null) {
updateWithShortcutInfo(parentTarget.getShortcutInfo());
} else if (parentTarget.getSearchAction() != null) {
@@ -127,7 +129,6 @@
});
}
-
protected void showSubtitleIfNeeded(CharSequence subTitle) {
if (!TextUtils.isEmpty(subTitle)) {
mSubTitleView.setText(subTitle);
@@ -137,11 +138,10 @@
}
}
-
protected void showInlineItems(List<SearchTarget> children) {
for (int i = 0; i < MAX_INLINE_ITEMS; i++) {
if (i < children.size()) {
- mInlineIcons[i].applySearchTarget(children.get(0), new ArrayList<>());
+ mInlineIcons[i].apply(children.get(i), new ArrayList<>());
mInlineIcons[i].setVisibility(VISIBLE);
} else {
mInlineIcons[i].setVisibility(GONE);
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
index e6c952f..c441e22 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
@@ -15,11 +15,12 @@
*/
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.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;
@@ -36,7 +37,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
import java.util.ArrayList;
import java.util.List;
@@ -44,17 +44,17 @@
/**
* A slice view wrapper with settings app icon at start
*/
-public class SearchResultIconSlice extends LinearLayout implements
- SearchTargetHandler, SliceView.OnSliceActionListener {
+public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler,
+ SliceView.OnSliceActionListener {
private static final String TAG = "SearchSliceController";
- private static final String URI_EXTRA_KEY = "slice_uri";
+
+ private final Launcher mLauncher;
private SliceView mSliceView;
private SearchResultIcon mIcon;
private LiveData<Slice> mSliceLiveData;
- private SearchTargetLegacy mSearchTarget;
- private final Launcher mLauncher;
+ private String mTargetId;
public SearchResultIconSlice(Context context) {
this(context, null, 0);
@@ -83,7 +83,8 @@
}
@Override
- public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ mTargetId = parentTarget.getId();
reset();
updateIcon(parentTarget, children);
try {
@@ -97,14 +98,14 @@
private void updateIcon(SearchTarget parentTarget, List<SearchTarget> children) {
if (children.size() == 1) {
- mIcon.applySearchTarget(children.get(0), new ArrayList<>());
+ 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);
- mIcon.applyFromItemInfoWithIcon(pkgItem);
+ MAIN_EXECUTOR.post(() -> mIcon.applyFromItemInfoWithIcon(pkgItem));
});
}
}
@@ -121,7 +122,6 @@
reset();
}
-
private void reset() {
mSliceView.setOnSliceActionListener(null);
if (mSliceLiveData != null) {
@@ -131,11 +131,6 @@
@Override
public void onSliceAction(@NonNull EventInfo eventInfo, @NonNull SliceItem sliceItem) {
- //TODO: event reporting
+ notifyEvent(mLauncher, mTargetId, SearchTargetEvent.ACTION_TAP);
}
-
- private Uri getSliceUri() {
- return mSearchTarget.getExtras().getParcelable(URI_EXTRA_KEY);
- }
-
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultPeopleView.java b/quickstep/src/com/android/launcher3/search/SearchResultPeopleView.java
deleted file mode 100644
index f3355da..0000000
--- a/quickstep/src/com/android/launcher3/search/SearchResultPeopleView.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.search;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
-
-import java.util.ArrayList;
-
-/**
- * A view representing a single people search result in all apps
- */
-public class SearchResultPeopleView extends LinearLayout implements
- SearchTargetHandler {
-
- public static final String TARGET_TYPE_PEOPLE = "people";
-
- private final int mIconSize;
- private final int mButtonSize;
- private final PackageManager mPackageManager;
- private View mIconView;
- private TextView mTitleView;
- private ImageButton[] mProviderButtons = new ImageButton[3];
- private Intent mIntent;
-
-
- private SearchTargetLegacy mSearchTarget;
-
- public SearchResultPeopleView(Context context) {
- this(context, null, 0);
- }
-
- public SearchResultPeopleView(Context context,
- @Nullable AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SearchResultPeopleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- DeviceProfile deviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile();
- mPackageManager = getContext().getPackageManager();
- mIconSize = deviceProfile.iconSizePx;
- mButtonSize = (int) (deviceProfile.iconSizePx / 1.5f);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mIconView = findViewById(R.id.icon);
- mIconView.getLayoutParams().height = mIconSize;
- mIconView.getLayoutParams().width = mIconSize;
- mTitleView = findViewById(R.id.title);
- mProviderButtons[0] = findViewById(R.id.provider_0);
- mProviderButtons[1] = findViewById(R.id.provider_1);
- mProviderButtons[2] = findViewById(R.id.provider_2);
- for (ImageButton button : mProviderButtons) {
- button.getLayoutParams().width = mButtonSize;
- button.getLayoutParams().height = mButtonSize;
- }
- }
-
- @Override
- public void applySearchTarget(SearchTargetLegacy searchTarget) {
- mSearchTarget = searchTarget;
- Bundle payload = searchTarget.getExtras();
- mTitleView.setText(payload.getString("title"));
- mIntent = payload.getParcelable("intent");
- Bitmap contactIcon = payload.getParcelable("icon");
- try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
- BitmapInfo badgeInfo = li.createBadgedIconBitmap(
- getAppIcon(mIntent.getPackage()), Process.myUserHandle(),
- Build.VERSION.SDK_INT);
- setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false);
- } catch (Exception e) {
- setIcon(contactIcon, true);
- }
-
- ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
- for (int i = 0; i < mProviderButtons.length; i++) {
- ImageButton button = mProviderButtons[i];
- if (providers != null && i < providers.size()) {
- Bundle provider = providers.get(i);
- Intent intent = provider.getParcelable("intent");
- setupProviderButton(button, provider, intent);
- UI_HELPER_EXECUTOR.post(() -> {
- String pkg = provider.getString("package_name");
- Drawable appIcon = getAppIcon(pkg);
- if (appIcon != null) {
- MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
- }
- });
- button.setVisibility(VISIBLE);
- } else {
- button.setVisibility(GONE);
- }
- }
- }
-
- /**
- * Normalizes the bitmap to look like rounded App Icon
- * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
- * bitmap from it.
- */
- private Bitmap roundBitmap(Bitmap icon) {
- final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
- d.setCornerRadius(R.attr.folderIconRadius);
- d.setBounds(0, 0, mIconSize, mIconSize);
- final Bitmap bitmap = Bitmap.createBitmap(d.getBounds().width(), d.getBounds().height(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- d.draw(canvas);
- return bitmap;
- }
-
- private void setIcon(Bitmap icon, Boolean round) {
- if (round) {
- RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
- d.setCornerRadius(R.attr.folderIconRadius);
- d.setBounds(0, 0, mIconSize, mIconSize);
- mIconView.setBackground(d);
- } else {
- mIconView.setBackground(new BitmapDrawable(getResources(), icon));
- }
- }
-
-
- private Drawable getAppIcon(String pkg) {
- try {
- ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
- pkg, 0);
- return applicationInfo.loadIcon(mPackageManager);
- } catch (PackageManager.NameNotFoundException ignored) {
- return null;
- }
- }
-
- private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
- Launcher launcher = Launcher.getLauncher(getContext());
- button.setOnClickListener(b -> {
- launcher.startActivitySafely(b, intent, null);
- Bundle bundle = new Bundle();
- bundle.putBundle("provider", provider);
- });
- }
-}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultSuggestion.java b/quickstep/src/com/android/launcher3/search/SearchResultSuggestion.java
deleted file mode 100644
index 6a6bd1b..0000000
--- a/quickstep/src/com/android/launcher3/search/SearchResultSuggestion.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.search;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.ViewGroup;
-
-import com.android.launcher3.R;
-import com.android.launcher3.views.BaseDragLayer;
-
-/**
- * {@link SearchResultIconRow} with custom drawable resource
- */
-public class SearchResultSuggestion extends SearchResultIcon {
-
- public static final String TARGET_TYPE_SUGGEST = "suggest";
- private final Drawable mCustomIcon;
-
- public SearchResultSuggestion(Context context) {
- this(context, null, 0);
- }
-
- public SearchResultSuggestion(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public SearchResultSuggestion(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.SearchResultSuggestion, defStyle, 0);
- mCustomIcon = a.getDrawable(R.styleable.SearchResultSuggestion_customIcon);
- a.recycle();
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- ViewGroup.LayoutParams lp = getLayoutParams();
- lp.height = BaseDragLayer.LayoutParams.WRAP_CONTENT;
- }
-
- @Override
- protected void setIcon(Drawable icon) {
- super.setIcon(mCustomIcon);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java b/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java
new file mode 100644
index 0000000..8803c98
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java
@@ -0,0 +1,102 @@
+/*
+ * 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 android.app.search.SearchTarget;
+import android.app.search.SearchTargetEvent;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.core.graphics.drawable.RoundedBitmapDrawable;
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.model.data.SearchActionItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.util.Themes;
+
+import java.util.List;
+
+/**
+ * A view representing a high confidence app search result that includes shortcuts
+ */
+public class SearchResultThumbnailView extends androidx.appcompat.widget.AppCompatImageView
+ implements SearchTargetHandler, View.OnClickListener {
+
+ private SearchTarget mSearchTarget;
+
+ public SearchResultThumbnailView(Context context) {
+ super(context);
+ }
+
+ public SearchResultThumbnailView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SearchResultThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setOnFocusChangeListener(Launcher.getLauncher(getContext()).getFocusHandler());
+ setOnClickListener(this);
+ }
+
+ @Override
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ mSearchTarget = parentTarget;
+ Bitmap bitmap;
+
+ SearchActionItemInfo itemInfo = new SearchActionItemInfo(
+ parentTarget.getSearchAction().getIcon(),
+ parentTarget.getPackageName(),
+ parentTarget.getUserHandle(),
+ parentTarget.getSearchAction().getTitle());
+ itemInfo.setIntent(parentTarget.getSearchAction().getIntent());
+ itemInfo.setPendingIntent(parentTarget.getSearchAction().getPendingIntent());
+
+ bitmap = ((BitmapDrawable) itemInfo.getIcon()
+ .loadDrawable(getContext())).getBitmap();
+ // crop
+ if (bitmap.getWidth() < bitmap.getHeight()) {
+ bitmap = Bitmap.createBitmap(bitmap, 0,
+ bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
+ bitmap.getWidth(), bitmap.getWidth());
+ } else {
+ bitmap = Bitmap.createBitmap(bitmap, bitmap.getWidth() / 2 - bitmap.getHeight() / 2,
+ 0,
+ bitmap.getHeight(), bitmap.getHeight());
+ }
+ setTag(itemInfo);
+
+ RoundedBitmapDrawable drawable = RoundedBitmapDrawableFactory.create(null, bitmap);
+ drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
+ setImageDrawable(drawable);
+ }
+
+ @Override
+ public void onClick(View view) {
+ ItemClickHandler.onClickSearchAction(Launcher.getLauncher(getContext()),
+ (SearchActionItemInfo) view.getTag());
+ notifyEvent(getContext(), mSearchTarget.getId(), SearchTargetEvent.ACTION_LAUNCH_TOUCH);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
index c6bdb68..4c64265 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.search;
+import android.app.search.SearchTarget;
+import android.app.search.SearchTargetEvent;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
@@ -23,31 +25,34 @@
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
import com.android.launcher3.allapps.search.SearchWidgetInfoContainer;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.widget.PendingAddWidgetInfo;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
+
+import java.util.List;
+
/**
* displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search
* provider
*/
-public class SearchResultWidget extends RelativeLayout implements
- SearchTargetHandler, DraggableView, View.OnLongClickListener {
+public class SearchResultWidget extends LinearLayout implements SearchTargetHandler, DraggableView,
+ View.OnLongClickListener {
- private static final String TAG = "SearchResultWidget";
-
- public static final String TARGET_TYPE_WIDGET_LIVE = "widget";
private final Rect mWidgetOffset = new Rect();
@@ -57,10 +62,9 @@
private final AppWidgetHostView mHostView;
private final float mScaleToFit;
- private SearchTargetLegacy mSearchTarget;
- private AppWidgetProviderInfo mProviderInfo;
-
private SearchWidgetInfoContainer mInfoContainer;
+ private BubbleTextView mWidgetProvider;
+ private TextView mWidgetLabel;
public SearchResultWidget(@NonNull Context context) {
this(context, null, 0);
@@ -81,9 +85,8 @@
// detect tap event on widget container for search target event reporting
mClickDetector = new GestureDetector(context,
- new ClickListener(() -> {
- }));
-
+ new ClickListener(
+ () -> reportEvent(SearchTargetEvent.ACTION_LAUNCH_TOUCH)));
mLongPressHelper = new CheckLongPressHelper(this);
mLongPressHelper.setLongPressTimeoutFactor(1);
setOnLongClickListener(this);
@@ -92,26 +95,17 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mWidgetProvider = findViewById(R.id.widget_provider);
+ mWidgetLabel = findViewById(R.id.widget_label);
addView(mHostView);
}
@Override
- public void applySearchTarget(SearchTargetLegacy searchTarget) {
- if (searchTarget.getExtras() == null
- || searchTarget.getExtras().getParcelable("provider") == null) {
- setVisibility(GONE);
- return;
- }
- AppWidgetProviderInfo providerInfo = searchTarget.getExtras().getParcelable("provider");
- if (mProviderInfo != null && providerInfo.provider.equals(mProviderInfo.provider)
- && providerInfo.getProfile().equals(mProviderInfo.getProfile())) {
- return;
- }
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ AppWidgetProviderInfo providerInfo = parentTarget.getAppWidgetProviderInfo();
removeListener();
- mSearchTarget = searchTarget;
- mProviderInfo = providerInfo;
-
+ showWidgetInfo(providerInfo);
mInfoContainer = mLauncher.getLiveSearchManager().getPlaceHolderWidget(providerInfo);
if (mInfoContainer == null) {
setVisibility(GONE);
@@ -129,6 +123,14 @@
setTag(info);
}
+ 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);
+ }
+
/**
* Stops hostView from getting updates on a widget provider
*/
@@ -138,6 +140,11 @@
}
}
+ private void reportEvent(int eventType) {
+ SearchSessionTracker.INSTANCE.get(getContext()).notifyEvent(
+ new SearchTargetEvent.Builder("search target id", eventType).build());
+ }
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
mLongPressHelper.onTouchEvent(ev);
@@ -179,6 +186,7 @@
return false;
}
+
static class ClickListener extends GestureDetector.SimpleOnGestureListener {
private final Runnable mCb;
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java b/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
index 22f0b76..5bf1908 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
@@ -18,6 +18,8 @@
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.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.Point;
@@ -39,24 +41,21 @@
import com.android.launcher3.widget.PendingItemDragHelper;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetImageView;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
+
+import java.util.List;
/**
* displays preview of a widget upon receiving {@link AppWidgetProviderInfo} from Search provider
*/
-public class SearchResultWidgetPreview extends LinearLayout implements
- SearchTargetHandler, View.OnLongClickListener,
- View.OnClickListener {
+public class SearchResultWidgetPreview extends LinearLayout implements SearchTargetHandler,
+ View.OnClickListener, View.OnLongClickListener {
- public static final String TARGET_TYPE_WIDGET_PREVIEW = "widget_preview";
private final Launcher mLauncher;
private final LauncherAppState mAppState;
private WidgetCell mWidgetCell;
private Toast mWidgetToast;
- private SearchTargetLegacy mSearchTarget;
-
-
+ private String mTargetId;
public SearchResultWidgetPreview(Context context) {
this(context, null, 0);
}
@@ -82,14 +81,9 @@
}
@Override
- public void applySearchTarget(SearchTargetLegacy searchTarget) {
- if (searchTarget.getExtras() == null
- || searchTarget.getExtras().getParcelable("provider") == null) {
- setVisibility(GONE);
- return;
- }
- mSearchTarget = searchTarget;
- AppWidgetProviderInfo providerInfo = searchTarget.getExtras().getParcelable("provider");
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+ mTargetId = parentTarget.getId();
+ AppWidgetProviderInfo providerInfo = parentTarget.getAppWidgetProviderInfo();
LauncherAppWidgetProviderInfo pInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
getContext(), providerInfo);
MODEL_EXECUTOR.post(() -> {
@@ -100,7 +94,6 @@
mWidgetCell.ensurePreview();
});
});
-
}
@Override
@@ -120,11 +113,18 @@
new PendingItemDragHelper(mWidgetCell).startDrag(
imageView.getBitmapBounds(), imageView.getBitmap().getWidth(), imageView.getWidth(),
new Point(loc[0], loc[1]), mLauncher.getAppsView(), new DragOptions());
+ reportEvent(SearchTargetEvent.ACTION_LONGPRESS);
return true;
}
@Override
public void onClick(View view) {
mWidgetToast = BaseWidgetSheet.showWidgetToast(getContext(), mWidgetToast);
+ reportEvent(SearchTargetEvent.ACTION_LAUNCH_TOUCH);
+ }
+
+ private void reportEvent(int eventType) {
+ SearchSessionTracker.INSTANCE.get(getContext()).notifyEvent(
+ new SearchTargetEvent.Builder(mTargetId, eventType).build());
}
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
index e015122..9276841 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
@@ -27,8 +27,7 @@
/**
* Header text view that shows a title for a given section in All apps search
*/
-public class SearchSectionHeaderView extends TextView implements
- SearchTargetHandler {
+public class SearchSectionHeaderView extends TextView implements SearchTargetHandler {
public SearchSectionHeaderView(Context context) {
super(context);
@@ -44,7 +43,7 @@
}
@Override
- public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
setText(parentTarget.getSearchAction().getTitle());
setVisibility(VISIBLE);
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
index 6585213..fac6ba7 100644
--- a/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
+++ b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
@@ -27,6 +27,8 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.WorkerThread;
+
import com.android.app.search.ResultType;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.AllAppsSectionDecorator;
@@ -46,10 +48,11 @@
private static final int SUPPORTED_RESULT_TYPES =
ResultType.APPLICATION | ResultType.SHORTCUT | ResultType.PLAY | ResultType.PEOPLE
| ResultType.SETTING;
+
+ private static final boolean DEBUG = true;
private static final int REQUEST_TIMEOUT = 200;
private static final String TAG = "SearchServicePipeline";
-
private final Context mContext;
private final SearchSession mSession;
private final DeviceSearchAdapterProvider mAdapterProvider;
@@ -63,16 +66,22 @@
SearchUiManager manager = context.getSystemService(SearchUiManager.class);
mSession = manager.createSearchSession(
new SearchContext(SUPPORTED_RESULT_TYPES, REQUEST_TIMEOUT, null));
+ SearchSessionTracker.getInstance(context).setSearchSession(mSession);
}
+ @WorkerThread
@Override
public void query(String input, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
CancellationSignal cancellationSignal) {
mCanceled = false;
Query query = new Query(input, System.currentTimeMillis(), null);
- mSession.query(query, UI_HELPER_EXECUTOR, items -> {
+ mSession.query(query, UI_HELPER_EXECUTOR, targets -> {
if (!mCanceled) {
- callback.accept(this.onResult(items));
+ if (DEBUG) {
+ printSearchTargets(input, targets);
+ }
+ SearchSessionTracker.getInstance(mContext).setQuery(query);
+ callback.accept(this.onResult(targets));
}
Log.w(TAG, "Ignoring results due to cancel signal");
});
@@ -111,6 +120,13 @@
return new ArrayList<>(adapterMap.values());
}
+ private void printSearchTargets(String query, List<SearchTarget> results) {
+ Log.d(TAG, " query=" + query + " size=" + results.size());
+ for (SearchTarget s : results) {
+ Log.d(TAG, "layoutType=" + s.getLayoutType() + " resultType=" + s.getResultType());
+ }
+ }
+
/**
* Adds a child SearchTarget to a collection of searchTarget children with a shared parentId.
* Returns false if no parent searchTarget with id=$parentId does not exists.
diff --git a/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java b/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java
new file mode 100644
index 0000000..3079965
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchSessionTracker.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.search;
+
+import android.app.search.Query;
+import android.app.search.SearchSession;
+import android.app.search.SearchTarget;
+import android.app.search.SearchTargetEvent;
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+/**
+ * A singleton class to track and report search events back to SearchSession
+ */
+public class SearchSessionTracker {
+
+ private static final String TAG = "SearchSessionTracker";
+
+ @Nullable
+ private SearchSession mSession;
+ private Query mQuery;
+
+ public static final MainThreadInitializedObject<SearchSessionTracker> INSTANCE =
+ new MainThreadInitializedObject<>(SearchSessionTracker::new);
+
+ private SearchSessionTracker(Context context) {
+ }
+
+ /**
+ * Returns instance of SearchSessionTracker
+ */
+ public static SearchSessionTracker getInstance(Context context) {
+ return SearchSessionTracker.INSTANCE.get(context);
+ }
+
+ @WorkerThread
+ public void setSearchSession(SearchSession session) {
+ mSession = session;
+ }
+
+ @WorkerThread
+ public void setQuery(Query query) {
+ mQuery = query;
+ }
+
+ /**
+ * Send the user event handling back to the {@link SearchSession} object.
+ */
+ public void notifyEvent(SearchTargetEvent event) {
+ if (mSession == null) {
+ Log.d(TAG, "Dropping event " + event.getTargetId());
+ }
+ Log.d(TAG, "notifyEvent:" + mQuery.getInput() + ":" + event.getTargetId());
+ UI_HELPER_EXECUTOR.post(() -> mSession.notifyEvent(mQuery, event));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
index 67502f6..acf6f8a 100644
--- a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
@@ -17,8 +17,8 @@
package com.android.launcher3.search;
import android.app.search.SearchTarget;
-
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
+import android.app.search.SearchTargetEvent;
+import android.content.Context;
import java.util.List;
@@ -28,20 +28,9 @@
public interface SearchTargetHandler {
/**
- * Update view using values from {@link SearchTargetLegacy}
- *
- * @deprecated Use {@link SearchTargetHandler#applySearchTarget(SearchTarget, List)} instead
+ * Update view using values from {@link SearchTarget}
*/
- @Deprecated
- default void applySearchTarget(SearchTargetLegacy searchTarget) {
- }
-
-
- /**
- * Update view using values from {@link SearchTargetLegacy}
- */
- default void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
- }
+ default void apply(SearchTarget parentTarget, List<SearchTarget> children) { }
/**
* Handle IME quick select event. returns whether event was handled.
@@ -50,4 +39,8 @@
return false;
}
+ default void notifyEvent(Context context, String id, int eventType) {
+ SearchTargetEvent.Builder builder = new SearchTargetEvent.Builder(id, eventType);
+ SearchSessionTracker.getInstance(context).notifyEvent(builder.build());
+ }
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
new file mode 100644
index 0000000..0abed03
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
@@ -0,0 +1,184 @@
+/*
+ * 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.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.THUMBNAIL;
+import static com.android.app.search.ResultType.ACTION;
+import static com.android.app.search.ResultType.SCREENSHOT;
+import static com.android.app.search.ResultType.SUGGEST;
+
+import android.app.PendingIntent;
+import android.app.search.SearchAction;
+import android.app.search.SearchTarget;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
+
+import com.android.app.search.ResultType;
+
+public class SearchTargetUtil {
+
+ public static final String BUNDLE_EXTRA_SHOULD_START = "should_start";
+ public static final String BUNDLE_EXTRA_SHOULD_START_FOR_RESULT = "should_start_for_result";
+ public static final String BUNDLE_EXTRA_BADGE_WITH_PACKAGE = "badge_with_package";
+ public static final String BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE = "primary_icon_from_title";
+
+ public static final String EXTRA_CLASS = "class";
+
+ private static final String TITLE = " title: weather ";
+ private static final String SUBTITLE = " subtitle: 68 degrees ";
+ private static final String PACKAGE2 = "com.google.android.gm";
+ private static final UserHandle USERHANDLE = Process.myUserHandle();
+
+
+ /**
+ * Generate SearchTargetUtil for ICON_DOUBLE_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(
+ Context context, String id, int color) {
+ SearchTarget.Builder builder =
+ new SearchTarget.Builder(ACTION, ICON_DOUBLE_HORIZONTAL_TEXT, id)
+ .setPackageName(PACKAGE2) /* required */
+ .setUserHandle(USERHANDLE); /* required */
+
+ Intent intent = new Intent("com.google.android.googlequicksearchbox.GENERIC_QUERY");
+ intent.putExtra("query", "weather");
+ intent.putExtra("full_screen", false);
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(
+ context, 1, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawColor(color);
+ Icon icon = Icon.createWithAdaptiveBitmap(bitmap);
+
+ Bundle b = new Bundle();
+ b.putBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT, true);
+ b.putBoolean(BUNDLE_EXTRA_BADGE_WITH_PACKAGE, true);
+ b.putBoolean(BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE, true);
+
+ builder.setSearchAction(new SearchAction.Builder(id, id + TITLE)
+ .setSubtitle(id + SUBTITLE)
+ .setPendingIntent(pendingIntent)
+ .setIcon(icon)
+ .setExtras(b)
+ .build());
+ return builder.build();
+ }
+
+ /**
+ * Inside SearchServicePipeline, add following samples to test the search target.
+ *
+ * targets.add(SearchTargetUtil.generateThumbnail_SearchAction("blue", Color.BLUE));
+ * 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) {
+ SearchTarget.Builder builder =
+ new SearchTarget.Builder(SCREENSHOT, THUMBNAIL, id)
+ .setPackageName(PACKAGE2) /* required */
+ .setUserHandle(USERHANDLE); /* required */
+
+
+ Intent intent = new Intent(Intent.ACTION_VIEW)
+ .setData(Uri.parse("uri blah blah"))
+ .setType("image/*")
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ Bitmap bitmap = Bitmap.createBitmap(1000, 500, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawColor(color);
+ Icon icon = Icon.createWithBitmap(bitmap);
+
+ builder.setSearchAction(new SearchAction.Builder(id, TITLE)
+ .setSubtitle(SUBTITLE)
+ .setIcon(icon)
+ .setIntent(intent)
+ .build());
+ return builder.build();
+
+ }
+
+ /**
+ * targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction(
+ * mContext, "red", Color.RED));
+ * targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction(
+ * mContext, "yellow", Color.YELLOW));
+ */
+ public static SearchTarget generateIconHorizontalText_SearchAction(
+ Context context, String id, int color) {
+ String fallbackQuery = "How to make cookie";
+ SearchTarget.Builder builder =
+ new SearchTarget.Builder(SUGGEST, ICON_SINGLE_HORIZONTAL_TEXT, id)
+ .setPackageName(PACKAGE2) /* required */
+ .setUserHandle(USERHANDLE); /* required */
+
+ Intent intent3 = new Intent("com.google.android.googlequicksearchbox.GENERIC_QUERY");
+ intent3.putExtra("query", fallbackQuery);
+ intent3.putExtra("full_screen", false);
+ PendingIntent pendingIntent3 =
+ PendingIntent.getActivity(
+ context, 1, intent3,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ Bitmap bitmap = Bitmap.createBitmap(200, 200, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ canvas.drawColor(color);
+ Icon icon = Icon.createWithAdaptiveBitmap(bitmap);
+
+ Bundle extra = new Bundle();
+ extra.putBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT, true);
+
+ SearchAction searchAction = new SearchAction.Builder(id, fallbackQuery)
+ .setPendingIntent(pendingIntent3)
+ .setIcon(icon)
+ .setExtras(extra)
+ .build();
+ return builder.setSearchAction(searchAction).build();
+ }
+
+
+ /**
+ * Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type.
+ */
+ public static SearchTarget generateIconDoubleHorizontalText_ShortcutInfo(Context context) {
+ String id = "23456";
+ SearchTarget.Builder builder =
+ new SearchTarget.Builder(ResultType.SHORTCUT, ICON_DOUBLE_HORIZONTAL_TEXT, id)
+ .setPackageName("com.google.android.gm") /* required */
+ .setUserHandle(UserHandle.CURRENT); /* required */
+
+ builder.setShortcutInfo(new ShortcutInfo.Builder(context, id).build());
+ return builder.build();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/search/ThumbnailSearchResultView.java b/quickstep/src/com/android/launcher3/search/ThumbnailSearchResultView.java
deleted file mode 100644
index 7108d63..0000000
--- a/quickstep/src/com/android/launcher3/search/ThumbnailSearchResultView.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.search;
-
-
-import android.app.search.SearchTarget;
-import android.content.Context;
-import android.util.AttributeSet;
-
-import java.util.List;
-
-/**
- * A view representing a high confidence app search result that includes shortcuts
- */
-public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
- implements SearchTargetHandler {
-
- public ThumbnailSearchResultView(Context context) {
- super(context);
- }
-
- public ThumbnailSearchResultView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ThumbnailSearchResultView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
-
- }
-}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 7be1b92..7608645 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -22,13 +22,26 @@
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
+import android.animation.Animator;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
import android.view.WindowManager;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.R;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.quickstep.AnimatedFloat;
import com.android.systemui.shared.system.WindowManagerWrapper;
/**
@@ -44,7 +57,12 @@
private final WindowManager mWindowManager;
// Layout width and height of the Taskbar in the default state.
private final Point mTaskbarSize;
+ private final TaskbarStateHandler mTaskbarStateHandler;
+ private final TaskbarVisibilityController mTaskbarVisibilityController;
+ private final TaskbarHotseatController mHotseatController;
+ private final TaskbarDragController mDragController;
+ // Initialized in init().
private WindowManager.LayoutParams mWindowLayoutParams;
public TaskbarController(BaseQuickstepLauncher launcher,
@@ -52,23 +70,85 @@
mLauncher = launcher;
mTaskbarContainerView = taskbarContainerView;
mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
+ mTaskbarView.setCallbacks(createTaskbarViewCallbacks());
mWindowManager = mLauncher.getWindowManager();
mTaskbarSize = new Point(MATCH_PARENT,
mLauncher.getResources().getDimensionPixelSize(R.dimen.taskbar_size));
+ mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
+ mTaskbarVisibilityController = new TaskbarVisibilityController(mLauncher,
+ createTaskbarVisibilityControllerCallbacks());
+ mHotseatController = new TaskbarHotseatController(mLauncher,
+ createTaskbarHotseatControllerCallbacks());
+ mDragController = new TaskbarDragController(mLauncher);
+ }
+
+ private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() {
+ return new TaskbarVisibilityControllerCallbacks() {
+ @Override
+ public void updateTaskbarBackgroundAlpha(float alpha) {
+ mTaskbarView.setBackgroundAlpha(alpha);
+ }
+
+ @Override
+ public void updateTaskbarVisibilityAlpha(float alpha) {
+ mTaskbarContainerView.setAlpha(alpha);
+ AlphaUpdateListener.updateVisibility(mTaskbarContainerView);
+ }
+ };
+ }
+
+ private TaskbarViewCallbacks createTaskbarViewCallbacks() {
+ return new TaskbarViewCallbacks() {
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return ItemClickHandler.INSTANCE;
+ }
+
+ @Override
+ public View.OnLongClickListener getItemOnLongClickListener() {
+ return mDragController::startDragOnLongClick;
+ }
+ };
+ }
+
+ private TaskbarHotseatControllerCallbacks createTaskbarHotseatControllerCallbacks() {
+ return new TaskbarHotseatControllerCallbacks() {
+ @Override
+ public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+ mTaskbarView.updateHotseatItems(hotseatItemInfos);
+ }
+ };
}
/**
* Initializes the Taskbar, including adding it to the screen.
*/
public void init() {
+ mTaskbarView.init(mHotseatController.getNumHotseatIcons());
addToWindowManager();
+ mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
+ mTaskbarVisibilityController.init();
+ mHotseatController.init();
+ }
+
+ private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
+ return new TaskbarStateHandlerCallbacks() {
+ @Override
+ public AnimatedFloat getAlphaTarget() {
+ return mTaskbarVisibilityController.getTaskbarVisibilityForLauncherState();
+ }
+ };
}
/**
* Removes the Taskbar from the screen, and removes any obsolete listeners etc.
*/
public void cleanup() {
+ mTaskbarView.cleanup();
removeFromWindowManager();
+ mTaskbarStateHandler.setTaskbarCallbacks(null);
+ mTaskbarVisibilityController.cleanup();
+ mHotseatController.cleanup();
}
private void removeFromWindowManager() {
@@ -108,4 +188,99 @@
mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
}
+
+ /**
+ * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
+ */
+ public void onLauncherResumedOrPaused(boolean isResumed) {
+ long duration = QuickstepAppTransitionManagerImpl.CONTENT_ALPHA_DURATION;
+ final Animator anim;
+ if (isResumed) {
+ anim = createAnimToLauncher(null, duration);
+ } else {
+ anim = createAnimToApp(duration);
+ }
+ anim.start();
+ }
+
+ /**
+ * Create Taskbar animation when going from an app to Launcher.
+ * @param toState If known, the state we will end up in when reaching Launcher.
+ */
+ public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
+ PendingAnimation anim = new PendingAnimation(duration);
+ anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(0, duration));
+ if (toState != null) {
+ mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
+ }
+ return anim.buildAnim();
+ }
+
+ private Animator createAnimToApp(long duration) {
+ return mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration);
+ }
+
+ /**
+ * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+ */
+ public void setIsImeVisible(boolean isImeVisible) {
+ mTaskbarVisibilityController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+ }
+
+ /**
+ * Should be called when one or more items in the Hotseat have changed.
+ */
+ public void onHotseatUpdated() {
+ mHotseatController.onHotseatUpdated();
+ }
+
+ /**
+ * @param ev MotionEvent in screen coordinates.
+ * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
+ */
+ public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
+ return mTaskbarView.isEventOverAnyItem(ev);
+ }
+
+ public boolean isDraggingItem() {
+ return mTaskbarView.isDraggingItem();
+ }
+
+ /**
+ * @return Whether the given View is in the same window as Taskbar.
+ */
+ public boolean isViewInTaskbar(View v) {
+ return mTaskbarContainerView.getWindowId().equals(v.getWindowId());
+ }
+
+ /**
+ * Contains methods that TaskbarStateHandler can call to interface with TaskbarController.
+ */
+ protected interface TaskbarStateHandlerCallbacks {
+ AnimatedFloat getAlphaTarget();
+ }
+
+ /**
+ * Contains methods that TaskbarVisibilityController can call to interface with
+ * TaskbarController.
+ */
+ protected interface TaskbarVisibilityControllerCallbacks {
+ void updateTaskbarBackgroundAlpha(float alpha);
+ void updateTaskbarVisibilityAlpha(float alpha);
+ }
+
+ /**
+ * Contains methods that TaskbarView can call to interface with TaskbarController.
+ */
+ protected interface TaskbarViewCallbacks {
+ View.OnClickListener getItemOnClickListener();
+ View.OnLongClickListener getItemOnLongClickListener();
+ }
+
+ /**
+ * Contains methods that TaskbarHotseatController can call to interface with TaskbarController.
+ */
+ protected interface TaskbarHotseatControllerCallbacks {
+ void updateHotseatItems(ItemInfo[] hotseatItemInfos);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
new file mode 100644
index 0000000..2318ff9
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -0,0 +1,133 @@
+/*
+ * 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 static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.system.ClipDescriptionCompat;
+import com.android.systemui.shared.system.LauncherAppsCompat;
+
+/**
+ * Handles long click on Taskbar items to start a system drag and drop operation.
+ */
+public class TaskbarDragController {
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final int mDragIconSize;
+
+ public TaskbarDragController(BaseQuickstepLauncher launcher) {
+ mLauncher = launcher;
+ Resources resources = mLauncher.getResources();
+ mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
+ }
+
+ /**
+ * Attempts to start a system drag and drop operation for the given View, using its tag to
+ * generate the ClipDescription and Intent.
+ * @return Whether {@link View#startDragAndDrop} started successfully.
+ */
+ protected boolean startDragOnLongClick(View view) {
+ if (!(view instanceof BubbleTextView)) {
+ return false;
+ }
+
+ BubbleTextView btv = (BubbleTextView) view;
+
+ View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
+ @Override
+ public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
+ shadowSize.set(mDragIconSize, mDragIconSize);
+ // TODO: should be based on last touch point on the icon.
+ shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
+ }
+
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ canvas.save();
+ float scale = (float) mDragIconSize / btv.getIconSize();
+ canvas.scale(scale, scale);
+ btv.getIcon().draw(canvas);
+ canvas.restore();
+ }
+ };
+
+ Object tag = view.getTag();
+ ClipDescription clipDescription = null;
+ Intent intent = null;
+ if (tag instanceof WorkspaceItemInfo) {
+ WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
+ LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class);
+ clipDescription = new ClipDescription(item.title,
+ new String[] {
+ item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT
+ : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY
+ });
+ intent = new Intent();
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
+ } else {
+ intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+ LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps,
+ item.getIntent().getComponent(), null, item.user));
+ }
+ intent.putExtra(Intent.EXTRA_USER, item.user);
+ }
+
+ if (clipDescription != null && intent != null) {
+ ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent));
+ view.setOnDragListener(getDraggedViewDragListener());
+ return view.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
+ View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE);
+ }
+ return false;
+ }
+
+ /**
+ * Hide the original Taskbar item while it is being dragged.
+ */
+ private View.OnDragListener getDraggedViewDragListener() {
+ return (view, dragEvent) -> {
+ switch (dragEvent.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ view.setVisibility(INVISIBLE);
+ return true;
+ case DragEvent.ACTION_DRAG_ENDED:
+ view.setVisibility(VISIBLE);
+ view.setOnDragListener(null);
+ return true;
+ }
+ return false;
+ };
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
new file mode 100644
index 0000000..4dc051a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -0,0 +1,90 @@
+/*
+ * 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 android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's Hotseat items.
+ */
+public class TaskbarHotseatController {
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final Hotseat mHotseat;
+ private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks;
+ private final int mNumHotseatIcons;
+
+ private final DragController.DragListener mDragListener = new DragController.DragListener() {
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ }
+
+ @Override
+ public void onDragEnd() {
+ onHotseatUpdated();
+ }
+ };
+
+ public TaskbarHotseatController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) {
+ mLauncher = launcher;
+ mHotseat = mLauncher.getHotseat();
+ mTaskbarCallbacks = taskbarCallbacks;
+ mNumHotseatIcons = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+ }
+
+ protected void init() {
+ mLauncher.getDragController().addDragListener(mDragListener);
+ }
+
+ protected void cleanup() {
+ mLauncher.getDragController().removeDragListener(mDragListener);
+ }
+
+ /**
+ * Called when any Hotseat item changes, and reports the new list of items to TaskbarController.
+ */
+ protected void onHotseatUpdated() {
+ ShortcutAndWidgetContainer shortcutsAndWidgets = mHotseat.getShortcutsAndWidgets();
+ ItemInfo[] hotseatItemInfos = new ItemInfo[mNumHotseatIcons];
+ for (int i = 0; i < shortcutsAndWidgets.getChildCount(); i++) {
+ View child = shortcutsAndWidgets.getChildAt(i);
+ Object tag = shortcutsAndWidgets.getChildAt(i).getTag();
+ if (tag instanceof ItemInfo) {
+ ItemInfo itemInfo = (ItemInfo) tag;
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ // Since the hotseat might be laid out vertically or horizontally, use whichever
+ // index is higher.
+ hotseatItemInfos[Math.max(lp.cellX, lp.cellY)] = itemInfo;
+ }
+ }
+
+ mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos);
+ }
+
+ protected int getNumHotseatIcons() {
+ return mNumHotseatIcons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
new file mode 100644
index 0000000..b4b5d8b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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 static com.android.launcher3.LauncherState.TASKBAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.AnimatedFloat;
+
+/**
+ * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
+ * isn't present (i.e. {@link #setTaskbarCallbacks} is never called).
+ */
+public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
+
+ private final BaseQuickstepLauncher mLauncher;
+
+ // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
+ private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
+
+ public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
+ mLauncher = launcher;
+ }
+
+ public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
+ mTaskbarCallbacks = callbacks;
+ }
+
+ @Override
+ public void setState(LauncherState state) {
+ if (mTaskbarCallbacks == null) {
+ return;
+ }
+
+ AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+ boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
+ alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
+ }
+
+ @Override
+ public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+ PendingAnimation animation) {
+ if (mTaskbarCallbacks == null) {
+ return;
+ }
+ if (config.hasAnimationFlag(SKIP_TASKBAR)) {
+ return;
+ }
+
+ AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+ boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
+ animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f,
+ config.getInterpolator(ANIM_TASKBAR_FADE, Interpolators.LINEAR));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 5df8d5f..c98f09c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,16 +16,51 @@
package com.android.launcher3.taskbar;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
import android.widget.LinearLayout;
+import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+
/**
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends LinearLayout {
+
+ private final ColorDrawable mBackgroundDrawable;
+ private final int mItemMarginLeftRight;
+ private final int mIconTouchSize;
+ private final int mTouchSlop;
+ private final RectF mTempDelegateBounds = new RectF();
+ private final RectF mDelegateSlopBounds = new RectF();
+ private final int[] mTempOutLocation = new int[2];
+
+ // Initialized in init().
+ private int mHotseatStartIndex;
+ private int mHotseatEndIndex;
+
+ private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+
+ // Delegate touches to the closest view if within mIconTouchSize.
+ private boolean mDelegateTargeted;
+ private View mDelegateView;
+
+ private boolean mIsDraggingItem;
+
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -42,5 +77,188 @@
public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ Resources resources = getResources();
+ mBackgroundDrawable = (ColorDrawable) getBackground();
+ mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ protected void setCallbacks(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
+ mControllerCallbacks = taskbarViewCallbacks;
+ }
+
+ protected void init(int numHotseatIcons) {
+ mHotseatStartIndex = 0;
+ mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
+ updateHotseatItems(new ItemInfo[numHotseatIcons]);
+ }
+
+ protected void cleanup() {
+ removeAllViews();
+ }
+
+ /**
+ * Sets the alpha of the background color behind all the Taskbar contents.
+ * @param alpha 0 is fully transparent, 1 is fully opaque.
+ */
+ public void setBackgroundAlpha(float alpha) {
+ mBackgroundDrawable.setAlpha((int) (alpha * 255));
+ }
+
+ /**
+ * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
+ */
+ protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+ for (int i = 0; i < hotseatItemInfos.length; i++) {
+ ItemInfo hotseatItemInfo = hotseatItemInfos[i];
+ int hotseatIndex = mHotseatStartIndex + i;
+ View hotseatView = getChildAt(hotseatIndex);
+
+ // Replace any Hotseat views with the appropriate type if it's not already that type.
+ final int expectedLayoutResId;
+ if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) {
+ expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
+ } else {
+ expectedLayoutResId = R.layout.taskbar_app_icon;
+ }
+ if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId) {
+ removeView(hotseatView);
+ BubbleTextView btv = (BubbleTextView) inflate(expectedLayoutResId);
+ LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
+ lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+ hotseatView = btv;
+ addView(hotseatView, hotseatIndex, lp);
+ }
+
+ // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
+ if (hotseatView instanceof BubbleTextView
+ && hotseatItemInfo instanceof WorkspaceItemInfo) {
+ ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
+ (WorkspaceItemInfo) hotseatItemInfo);
+ hotseatView.setVisibility(VISIBLE);
+ hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+ hotseatView.setOnLongClickListener(
+ mControllerCallbacks.getItemOnLongClickListener());
+ } else {
+ hotseatView.setVisibility(GONE);
+ hotseatView.setOnClickListener(null);
+ hotseatView.setOnLongClickListener(null);
+ }
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean handled = delegateTouchIfNecessary(event);
+ return super.onTouchEvent(event) || handled;
+ }
+
+ /**
+ * User touched the Taskbar background. Determine whether the touch is close enough to a view
+ * that we should forward the touches to it.
+ * @return Whether a delegate view was chosen and it handled the touch event.
+ */
+ private boolean delegateTouchIfNecessary(MotionEvent event) {
+ final float x = event.getX();
+ final float y = event.getY();
+ if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
+ View delegateView = findDelegateView(x, y);
+ if (delegateView != null) {
+ mDelegateTargeted = true;
+ mDelegateView = delegateView;
+ mDelegateSlopBounds.set(mTempDelegateBounds);
+ mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
+ }
+ }
+
+ boolean sendToDelegate = mDelegateTargeted;
+ boolean inBounds = true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ inBounds = mDelegateSlopBounds.contains(x, y);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mDelegateTargeted = false;
+ break;
+ }
+
+ boolean handled = false;
+ if (sendToDelegate) {
+ if (inBounds) {
+ // Offset event coordinates to be inside the target view
+ event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
+ } else {
+ // Offset event coordinates to be outside the target view (in case it does
+ // something like tracking pressed state)
+ event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
+ }
+ handled = mDelegateView.dispatchTouchEvent(event);
+ // Cleanup if this was the last event to send to the delegate.
+ if (!mDelegateTargeted) {
+ mDelegateView = null;
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Return an item whose touch bounds contain the given coordinates,
+ * or null if no such item exists.
+ *
+ * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view.
+ */
+ private @Nullable View findDelegateView(float x, float y) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (!child.isShown() || !child.isClickable()) {
+ continue;
+ }
+ int childCenterX = child.getLeft() + child.getWidth() / 2;
+ int childCenterY = child.getTop() + child.getHeight() / 2;
+ mTempDelegateBounds.set(
+ childCenterX - mIconTouchSize / 2f,
+ childCenterY - mIconTouchSize / 2f,
+ childCenterX + mIconTouchSize / 2f,
+ childCenterY + mIconTouchSize / 2f);
+ if (mTempDelegateBounds.contains(x, y)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
+ * touch bounds.
+ */
+ public boolean isEventOverAnyItem(MotionEvent ev) {
+ getLocationOnScreen(mTempOutLocation);
+ float xInOurCoordinates = ev.getX() - mTempOutLocation[0];
+ float yInOurCoorindates = ev.getY() - mTempOutLocation[1];
+ return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ mIsDraggingItem = true;
+ return true;
+ case DragEvent.ACTION_DRAG_ENDED:
+ mIsDraggingItem = false;
+ break;
+ }
+ return super.onDragEvent(event);
+ }
+
+ public boolean isDraggingItem() {
+ return mIsDraggingItem;
+ }
+
+ private View inflate(@LayoutRes int layoutResId) {
+ return LayoutInflater.from(getContext()).inflate(layoutResId, this, false);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
new file mode 100644
index 0000000..4cf55d8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
@@ -0,0 +1,95 @@
+/*
+ * 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 static com.android.launcher3.LauncherState.TASKBAR;
+
+import android.animation.Animator;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's alpha based on LauncherState, whether
+ * Launcher is in the foreground, etc.
+ */
+public class TaskbarVisibilityController {
+
+ private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final TaskbarController.TaskbarVisibilityControllerCallbacks mTaskbarCallbacks;
+
+ // Background alpha.
+ private AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
+ this::onTaskbarBackgroundAlphaChanged);
+
+ // Overall visibility.
+ private AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
+ this::updateVisibilityAlpha);
+ private AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
+ this::updateVisibilityAlpha);
+
+ public TaskbarVisibilityController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarVisibilityControllerCallbacks taskbarCallbacks) {
+ mLauncher = launcher;
+ mTaskbarCallbacks = taskbarCallbacks;
+ }
+
+ protected void init() {
+ mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f);
+ boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState()
+ .getVisibleElements(mLauncher) & TASKBAR) != 0;
+ mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f);
+ boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags()
+ & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0;
+ mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f);
+ }
+
+ protected void cleanup() {
+ }
+
+ protected AnimatedFloat getTaskbarVisibilityForLauncherState() {
+ return mTaskbarVisibilityAlphaForLauncherState;
+ }
+
+ protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
+ return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
+ .setDuration(duration);
+ }
+
+ protected void animateToVisibilityForIme(float toAlpha) {
+ mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha)
+ .setDuration(IME_VISIBILITY_ALPHA_DURATION).start();
+ }
+
+ private void onTaskbarBackgroundAlphaChanged() {
+ mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
+ updateVisibilityAlpha();
+ }
+
+ private void updateVisibilityAlpha() {
+ // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
+ // assumption being that Taskbar should always be visible regardless of the current
+ // LauncherState if Launcher is paused.
+ float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
+ mTaskbarVisibilityAlphaForLauncherState.value);
+ float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
+ mTaskbarCallbacks.updateTaskbarVisibilityAlpha(alphaDueToLauncher * alphaDueToOther);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 597c17b..22c4a7e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -184,7 +184,10 @@
}
private int getOutlineOffsetY() {
- return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+ if (mDisplay != DISPLAY_TASKBAR) {
+ return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+ }
+ return (getMeasuredHeight() / 2) - mNormalizedIconRadius;
}
private void drawEffect(Canvas canvas, boolean isBadged) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
index 7beb9db..d14e8ef 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.os.Looper;
+import com.android.launcher3.Utilities;
import com.android.systemui.shared.plugins.PluginInitializer;
public class PluginInitializerImpl implements PluginInitializer {
@@ -44,4 +45,8 @@
@Override
public void handleWtfs() {
}
+
+ public boolean isDebuggable() {
+ return Utilities.IS_DEBUG_DEVICE;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 4b4f955..2cf65af 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -69,7 +69,9 @@
@Override
public int getVisibleElements(Launcher launcher) {
return super.getVisibleElements(launcher)
- & ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
+ & ~OVERVIEW_BUTTONS
+ & ~VERTICAL_SWIPE_INDICATOR
+ | TASKBAR;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 51e72da..965f474 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -40,6 +40,6 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return NONE;
+ return TASKBAR;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index efb91c6..69b8aca 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -36,6 +36,7 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
@@ -80,6 +81,7 @@
if (toState == NORMAL && fromState == OVERVIEW) {
config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+ config.setInterpolator(ANIM_TASKBAR_FADE, ACCEL);
config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
@@ -138,6 +140,7 @@
config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_TASKBAR_FADE, OVERSHOOT_1_2);
} else if (fromState == HINT_STATE && toState == NORMAL) {
config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
if (mHintToNormalDuration == -1) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d648dd6..36b51cd 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -721,16 +721,16 @@
@UiThread
public void onGestureStarted(boolean isLikelyToStartNewTask) {
- // Temporarily disable this until we have a view that we can use
- // InteractionJankMonitorWrapper.begin(mRecentsView,
- // InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
- // InteractionJankMonitorWrapper.begin(mRecentsView,
- // InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ if (mRecentsView != null) {
+ InteractionJankMonitorWrapper.begin(mRecentsView,
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
+ InteractionJankMonitorWrapper.begin(mRecentsView,
+ InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ }
notifyGestureStartedAsync();
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
- mTaskViewSimulator.setDrawsBelowRecents(true);
}
/**
@@ -957,7 +957,6 @@
if (endTarget == HOME) {
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
- LiveTileOverlay.INSTANCE.startIconAnimation();
if (mRecentsView != null) {
int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
if (mRecentsView.getNextPage() != nearestPage) {
@@ -1058,10 +1057,11 @@
if (mGestureState.getEndTarget().isLauncher) {
ActivityManagerWrapper.getInstance().registerTaskStackListener(
mActivityRestartListener);
+
+ mActivityInterface.onAnimateToLauncher(mGestureState.getEndTarget(), duration);
}
if (mGestureState.getEndTarget() == HOME) {
- mTaskViewSimulator.setDrawsBelowRecents(false);
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index efd4530..d159fa0 100644
--- a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -33,6 +33,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
@@ -119,6 +120,11 @@
OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
}
+ TaskbarController taskbarController = mActivityInterface.getTaskbarController();
+ if (taskbarController != null) {
+ pa.add(taskbarController.createAnimToLauncher(OVERVIEW, getRecentsLaunchDuration()));
+ }
+
RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
wallpaperTargets, MODE_CLOSING);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 5bed929..ce14197 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -45,6 +45,7 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -121,6 +122,11 @@
return null;
}
+ @Nullable
+ public TaskbarController getTaskbarController() {
+ return null;
+ }
+
public final boolean isResumed() {
ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed();
@@ -147,6 +153,13 @@
return deviceState.isInDeferredGestureRegion(ev);
}
+ /**
+ * @return Whether the gesture in progress should be cancelled.
+ */
+ public boolean shouldCancelCurrentGesture() {
+ return false;
+ }
+
public abstract void onExitOverview(RotationTouchHelper deviceState,
Runnable exitRunnable);
@@ -276,6 +289,20 @@
return overviewActionsHeight;
}
+ /**
+ * Called when the gesture ends and the animation starts towards the given target. No-op by
+ * default, but subclasses can override to add an additional animation with the same duration.
+ */
+ public void onAnimateToLauncher(GestureState.GestureEndTarget endTarget, long duration) {
+ }
+
+ /**
+ * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
+ * @param systemUiStateFlags The latest SystemUiStateFlags
+ */
+ public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+ }
+
public interface AnimationFactory {
void createActivityInterface(long transitionLength);
diff --git a/quickstep/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
index b04905c..cb4d53a 100644
--- a/quickstep/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -83,8 +83,8 @@
* Share the image this api was constructed with.
*/
@UiThread
- public void startShareActivity() {
- ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, null, null, TAG);
+ public void startShareActivity(Rect crop) {
+ ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, crop, null, TAG);
}
/**
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 7630bc4..8b0d782 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -16,14 +16,17 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.Log;
+import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -38,8 +41,10 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -156,6 +161,16 @@
@Nullable
@Override
+ public TaskbarController getTaskbarController() {
+ BaseQuickstepLauncher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return null;
+ }
+ return launcher.getTaskbarController();
+ }
+
+ @Nullable
+ @Override
public RecentsView getVisibleRecentsView() {
Launcher launcher = getVisibleLauncher();
return launcher != null && launcher.getStateManager().getState().overviewUi
@@ -277,4 +292,42 @@
if (activity == null) return;
activity.getAppTransitionManager().registerRemoteTransitions();
}
+
+ @Override
+ public void onAnimateToLauncher(GestureEndTarget endTarget, long duration) {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return;
+ }
+ LauncherState toState = endTarget == GestureEndTarget.RECENTS ? OVERVIEW : NORMAL;
+ taskbarController.createAnimToLauncher(toState, duration).start();
+ }
+
+ @Override
+ public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return;
+ }
+ boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+ taskbarController.setIsImeVisible(isImeVisible);
+ }
+
+ @Override
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return super.deferStartingActivity(deviceState, ev);
+ }
+ return taskbarController.isEventOverAnyTaskbarItem(ev);
+ }
+
+ @Override
+ public boolean shouldCancelCurrentGesture() {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return super.shouldCancelCurrentGesture();
+ }
+ return taskbarController.isDraggingItem();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 80308a5..65847f1 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -26,6 +26,7 @@
import com.android.launcher3.MainProcessInitializer;
import com.android.launcher3.util.Executors;
import com.android.quickstep.logging.SettingsChangeLogger;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
@@ -35,7 +36,11 @@
private static final String TAG = "QuickstepProcessInitializer";
private static final int SETUP_DELAY_MILLIS = 5000;
- public QuickstepProcessInitializer(Context context) { }
+ public QuickstepProcessInitializer(Context context) {
+ // Fake call to create an instance of InteractionJankMonitor to avoid binder calls during
+ // its initialization during transitions.
+ InteractionJankMonitorWrapper.cancel(-1);
+ }
@Override
protected void init(Context context) {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 6f2f86e..02c2763 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -21,7 +21,10 @@
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
+import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
import android.util.Log;
import androidx.annotation.UiThread;
@@ -30,11 +33,15 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
import java.util.function.Consumer;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
+ public static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
@@ -43,7 +50,11 @@
private GestureState mLastGestureState;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
+ private Context mCtx;
+ TaskAnimationManager(Context ctx) {
+ mCtx = ctx;
+ }
/**
* Preloads the recents animation.
*/
@@ -122,8 +133,16 @@
final long eventTime = gestureState.getSwipeUpStartTimeMs();
mCallbacks.addListener(gestureState);
mCallbacks.addListener(listener);
- UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
+ mController != null ? mController.getController() : null);
+ Bundle options = ActivityOptionsCompat.makeRemoteTransition(transition).toBundle();
+ mCtx.startActivity(intent, options);
+ } else {
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
+ }
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
return mCallbacks;
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 6677724..93ebd5a 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -159,7 +159,7 @@
@Override
public void onShare() {
if (isAllowedByPolicy) {
- endLiveTileMode(mImageApi::startShareActivity);
+ endLiveTileMode(() -> mImageApi.startShareActivity(null));
} else {
showBlockedByPolicyMessage();
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 196cae7..8ebea33 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -309,7 +309,7 @@
@UiThread
public void onUserUnlocked() {
- mTaskAnimationManager = new TaskAnimationManager();
+ mTaskAnimationManager = new TaskAnimationManager(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
mOverviewComponentObserver);
@@ -370,12 +370,14 @@
@UiThread
private void onSystemUiFlagsChanged() {
if (mDeviceState.isUserUnlocked()) {
- SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
- mDeviceState.getSystemUiStateFlags());
+ int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
+ SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
+ mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
+ systemUiStateFlags);
// Update the tracing state
- if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) {
+ if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
Log.d(TAG, "Starting tracing.");
ProtoTracer.INSTANCE.get(this).start();
} else {
@@ -512,9 +514,14 @@
}
}
- boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
+ boolean cancelGesture = mGestureState.getActivityInterface() != null
+ && mGestureState.getActivityInterface().shouldCancelCurrentGesture();
+ boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
&& mConsumer != null
&& !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
+ if (cancelGesture) {
+ event.setAction(ACTION_CANCEL);
+ }
mUncheckedConsumer.onMotionEvent(event);
if (cleanUpConsumer) {
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 9ee9f00..cb81d36 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.AllAppsInsetTransitionController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.search.DeviceSearchEdu;
@@ -139,8 +140,12 @@
@Override
public void onStateTransitionStart(LauncherState toState) {
if (toState == ALL_APPS) {
- mLauncher.getAllAppsController().getInsetController().setSearchEduRunnable(
- () -> DeviceSearchEdu.show(launcher));
+ AllAppsInsetTransitionController insetTransitionController =
+ mLauncher.getAllAppsController().getInsetController();
+ insetTransitionController.setSearchEduRunnable(() -> {
+ DeviceSearchEdu.show(launcher);
+ insetTransitionController.setSearchEduRunnable(null);
+ });
stateManager.removeStateListener(this);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 4120331..5bae3c7 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -158,7 +159,8 @@
*/
private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
StateAnimationConfig config = new StateAnimationConfig();
- config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
+ config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER
+ | SKIP_TASKBAR;
config.duration = 0;
// setRecentsAttachedToAppWindow() will animate recents out.
launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
diff --git a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
index 747c3f2..8210ab0 100644
--- a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -85,6 +85,11 @@
mIcon = icon;
}
+ // TODO: consider cleaning this up and drawing icon in another way. Previously we place app
+ // below launcher during the initial swipe up and render the icon in this live tile overlay.
+ // However, this resulted in a bunch of touch input issues caused by Launcher getting the input
+ // events during transition (to overview / to another app (quick switch). So now our new
+ // solution places app on top in live tile until it fully settles in Overview.
public void startIconAnimation() {
if (mIconAnimator != null) {
mIconAnimator.cancel();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b791d29..59cf3b2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -257,6 +257,8 @@
private final float[] mIconCenterCoords = new float[2];
private final float[] mChipCenterCoords = new float[2];
+ private boolean mIsClickableAsLiveTile = true;
+
public TaskView(Context context) {
this(context, null);
}
@@ -273,6 +275,11 @@
return;
}
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
+ if (!mIsClickableAsLiveTile) {
+ return;
+ }
+
+ mIsClickableAsLiveTile = false;
RecentsView recentsView = getRecentsView();
RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
@@ -289,6 +296,7 @@
public void onAnimationEnd(Animator animator) {
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
recentsView.finishRecentsAnimation(false, null);
+ mIsClickableAsLiveTile = true;
}
});
anim.start();
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 96c30b5..e593fb4 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -57,6 +57,7 @@
<enum name="widget_section" value="3" />
<enum name="shortcut_popup" value="4" />
<enum name="hero_app" value="5" />
+ <enum name="taskbar" value="6" />
</attr>
<attr name="centerVertically" format="boolean" />
</declare-styleable>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3425f91..447c9ac 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -232,6 +232,8 @@
<string name="abandoned_promise_explanation">The app for this icon isn\'t installed.
You can remove it, or search for the app and install it manually.
</string>
+ <!-- Title for an app which is being installed. -->
+ <string name="app_installing_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> installing, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
<!-- Title for an app which is being downloaded. -->
<string name="app_downloading_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> downloading, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
<!-- Title for an app whose download has been started. -->
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 22eb15a..5007ffc 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -51,6 +51,7 @@
import android.widget.TextView;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.Launcher.OnResumeCallback;
@@ -92,6 +93,7 @@
private static final int DISPLAY_ALL_APPS = 1;
private static final int DISPLAY_FOLDER = 2;
private static final int DISPLAY_HERO_APP = 5;
+ protected static final int DISPLAY_TASKBAR = 6;
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
private static final float HIGHLIGHT_SCALE = 1.16f;
@@ -140,7 +142,7 @@
private Drawable mIcon;
private boolean mCenterVertically;
- private final int mDisplay;
+ protected final int mDisplay;
private final CheckLongPressHelper mLongPressHelper;
@@ -206,6 +208,8 @@
defaultIconSize = grid.folderChildIconSizePx;
} else if (mDisplay == DISPLAY_HERO_APP) {
defaultIconSize = grid.allAppsIconSizePx;
+ } else if (mDisplay == DISPLAY_TASKBAR) {
+ defaultIconSize = grid.iconSizePx;
} else {
// widget_selection or shortcut_popup
defaultIconSize = grid.iconSizePx;
@@ -268,6 +272,7 @@
mDotScaleAnim.start();
}
+ @UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
applyFromWorkspaceItem(info, false);
}
@@ -284,13 +289,16 @@
}
}
+ @UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
applyIconAndLabel(info);
setTag(info);
applyLoadingState(promiseStateChanged);
applyDotState(info, false /* animate */);
+ setDownloadStateContentDescription(info, info.getProgressLevel());
}
+ @UiThread
public void applyFromApplicationInfo(AppInfo info) {
applyIconAndLabel(info);
@@ -301,14 +309,16 @@
verifyHighRes();
if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
- applyProgressLevel(info.getProgressLevel());
+ applyProgressLevel();
}
applyDotState(info, false /* animate */);
+ setDownloadStateContentDescription(info, info.getProgressLevel());
}
/**
* Apply label and tag using a generic {@link ItemInfoWithIcon}
*/
+ @UiThread
public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
applyIconAndLabel(info);
// We don't need to check the info since it's not a WorkspaceItemInfo
@@ -316,16 +326,20 @@
// Verify high res immediately
verifyHighRes();
+
+ setDownloadStateContentDescription(info, info.getProgressLevel());
}
/**
* Apply label and tag using a {@link SearchActionItemInfo}
*/
+ @UiThread
public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
applyIconAndLabel(searchActionItemInfo);
setTag(searchActionItemInfo);
}
+ @UiThread
protected void applyIconAndLabel(ItemInfoWithIcon info) {
FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
@@ -334,6 +348,7 @@
applyLabel(info);
}
+ @UiThread
private void applyLabel(ItemInfoWithIcon info) {
setText(info.title);
if (info.contentDescription != null) {
@@ -483,6 +498,10 @@
* @param canvas The canvas to draw to.
*/
protected void drawDotIfNecessary(Canvas canvas) {
+ if (mDisplay == DISPLAY_TASKBAR) {
+ // TODO: support notification dots in Taskbar
+ return;
+ }
if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
getIconBounds(mDotParams.iconBounds);
Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
@@ -603,21 +622,20 @@
* with the total download progress.
*/
public void applyLoadingState(boolean promiseStateChanged) {
- if (getTag() instanceof WorkspaceItemInfo) {
+ if (getTag() instanceof ItemInfoWithIcon) {
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
- int progressLevel = info.getProgressLevel();
if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
!= 0) {
- updateProgressBarUi(progressLevel, progressLevel == 100);
+ updateProgressBarUi(info.getProgressLevel() == 100);
} else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
- updateProgressBarUi(progressLevel, promiseStateChanged);
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ updateProgressBarUi(promiseStateChanged);
}
}
}
- private void updateProgressBarUi(int progressLevel, boolean maybePerformFinishedAnimation) {
- PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+ private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {
+ PreloadIconDrawable preloadDrawable = applyProgressLevel();
if (preloadDrawable != null && maybePerformFinishedAnimation) {
preloadDrawable.maybePerformFinishedAnimation();
}
@@ -625,38 +643,57 @@
/** Applies the given progress level to the this icon's progress bar. */
@Nullable
- public PreloadIconDrawable applyProgressLevel(int progressLevel) {
- if (getTag() instanceof ItemInfoWithIcon) {
- ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
- if (progressLevel >= 100) {
- setContentDescription(info.contentDescription != null
- ? info.contentDescription : "");
- } else if (progressLevel > 0) {
- setContentDescription(getContext()
- .getString(R.string.app_downloading_title, info.title,
- NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+ public PreloadIconDrawable applyProgressLevel() {
+ if (!(getTag() instanceof ItemInfoWithIcon)) {
+ return null;
+ }
+
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ int progressLevel = info.getProgressLevel();
+ if (progressLevel >= 100) {
+ setContentDescription(info.contentDescription != null
+ ? info.contentDescription : "");
+ } else if (progressLevel > 0) {
+ setDownloadStateContentDescription(info, progressLevel);
+ } else {
+ setContentDescription(getContext()
+ .getString(R.string.app_waiting_download_title, info.title));
+ }
+ if (mIcon != null) {
+ PreloadIconDrawable preloadIconDrawable;
+ if (mIcon instanceof PreloadIconDrawable) {
+ preloadIconDrawable = (PreloadIconDrawable) mIcon;
+ preloadIconDrawable.setLevel(progressLevel);
+ preloadIconDrawable.setIsDisabled(!info.isAppStartable());
} else {
- setContentDescription(getContext()
- .getString(R.string.app_waiting_download_title, info.title));
+ preloadIconDrawable = makePreloadIcon();
+ setIcon(preloadIconDrawable);
}
- if (mIcon != null) {
- final PreloadIconDrawable preloadDrawable;
- if (mIcon instanceof PreloadIconDrawable) {
- preloadDrawable = (PreloadIconDrawable) mIcon;
- preloadDrawable.setLevel(progressLevel);
- preloadDrawable.setIsDisabled(!info.isAppStartable());
- } else {
- preloadDrawable = newPendingIcon(getContext(), info);
- preloadDrawable.setLevel(progressLevel);
- preloadDrawable.setIsDisabled(!info.isAppStartable());
- setIcon(preloadDrawable);
- }
- return preloadDrawable;
- }
+ return preloadIconDrawable;
}
return null;
}
+ /**
+ * Creates a PreloadIconDrawable with the appropriate progress level without mutating this
+ * object.
+ */
+ @Nullable
+ public PreloadIconDrawable makePreloadIcon() {
+ if (!(getTag() instanceof ItemInfoWithIcon)) {
+ return null;
+ }
+
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ int progressLevel = info.getProgressLevel();
+ final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);
+
+ preloadDrawable.setLevel(progressLevel);
+ preloadDrawable.setIsDisabled(!info.isAppStartable());
+
+ return preloadDrawable;
+ }
+
public void applyDotState(ItemInfo itemInfo, boolean animate) {
if (mIcon instanceof FastBitmapDrawable) {
boolean wasDotted = mDotInfo != null;
@@ -693,6 +730,24 @@
}
}
+ private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
+ != 0) {
+ String percentageString = NumberFormat.getPercentInstance()
+ .format(progressLevel * 0.01);
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ setContentDescription(getContext()
+ .getString(
+ R.string.app_installing_title, info.title, percentageString));
+ } else if ((info.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+ setContentDescription(getContext()
+ .getString(
+ R.string.app_downloading_title, info.title, percentageString));
+ }
+ }
+ }
+
/**
* Sets the icon for this view based on the layout direction.
*/
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 12ce9f3..f681d75 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -607,8 +607,9 @@
*/
public boolean updateIsSeascape(Context context) {
if (isVerticalBarLayout()) {
- boolean isSeascape = DisplayController.getDefaultDisplay(context).getInfo().rotation
- == Surface.ROTATION_270;
+ // Check an up-to-date info.
+ boolean isSeascape = DisplayController.getDefaultDisplay(context)
+ .createInfoForContext(context).rotation == Surface.ROTATION_270;
if (mIsSeascape != isSeascape) {
mIsSeascape = isSeascape;
return true;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 139d4a8..b1fe4a2 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -276,15 +276,15 @@
@Override
public ConstantState getConstantState() {
- return new MyConstantState(mBitmap, mIconColor, mIsDisabled);
+ return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled);
}
- protected static class MyConstantState extends ConstantState {
+ protected static class FastBitmapConstantState extends ConstantState {
protected final Bitmap mBitmap;
protected final int mIconColor;
protected final boolean mIsDisabled;
- public MyConstantState(Bitmap bitmap, int color, boolean isDisabled) {
+ public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) {
mBitmap = bitmap;
mIconColor = color;
mIsDisabled = isDisabled;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0274775..2334267 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1917,6 +1917,13 @@
@Override
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ if (isViewInTaskbar(v)) {
+ // Start the activity without the hacky workarounds below, which assume the View was
+ // clicked when Launcher was resumed and will be hidden until Launcher is re-resumed
+ // (this isn't the case for Taskbar).
+ return super.startActivitySafely(v, intent, item);
+ }
+
if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
@@ -2818,6 +2825,13 @@
.start();
}
+ /**
+ * @return Whether the View is in the same window as the Taskbar window.
+ */
+ public boolean isViewInTaskbar(View v) {
+ return false;
+ }
+
private static class NonConfigInstance {
public Configuration config;
public Bitmap snapshot;
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index ac3ad9f..0fa441a 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -50,7 +50,7 @@
return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
}
- public boolean supportsAdaptiveIconAnimation() {
+ public boolean supportsAdaptiveIconAnimation(View clickedView) {
return false;
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 79476fc..f9a1ded 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -57,6 +57,7 @@
public static final int ALL_APPS_CONTENT = 1 << 4;
public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
public static final int OVERVIEW_BUTTONS = 1 << 6;
+ public static final int TASKBAR = 1 << 7;
/** Mask of all the items that are contained in the apps view. */
public static final int APPS_VIEW_ITEM_MASK =
@@ -186,7 +187,7 @@
}
public int getVisibleElements(Launcher launcher) {
- int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
+ int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR | TASKBAR;
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
&& !launcher.getDeviceProfile().isVerticalBarLayout()) {
flags |= HOTSEAT_SEARCH_BOX;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8066aa6..51d8e66 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -67,7 +67,7 @@
import androidx.core.os.BuildCompat;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-import com.android.launcher3.graphics.GridOptionsProvider;
+import com.android.launcher3.graphics.GridCustomizationsProvider;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -520,7 +520,7 @@
public static boolean isGridOptionsEnabled(Context context) {
return isComponentEnabled(context.getPackageManager(),
context.getPackageName(),
- GridOptionsProvider.class.getName());
+ GridCustomizationsProvider.class.getName());
}
private static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 65eba20..8a45c81 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -111,9 +111,11 @@
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -3002,38 +3004,27 @@
* shortcuts are not removed.
*/
public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
- for (final CellLayout layoutParent: getWorkspaceAndHotseatCellLayouts()) {
- final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
+ for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
+ ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+ // Iterate in reverse order as we are removing items
+ for (int i = container.getChildCount() - 1; i >= 0; i--) {
+ View child = container.getChildAt(i);
+ ItemInfo info = (ItemInfo) child.getTag();
- IntSparseArrayMap<View> idToViewMap = new IntSparseArrayMap<>();
- ArrayList<ItemInfo> items = new ArrayList<>();
- for (int j = 0; j < layout.getChildCount(); j++) {
- final View view = layout.getChildAt(j);
- if (view.getTag() instanceof ItemInfo) {
- ItemInfo item = (ItemInfo) view.getTag();
- items.add(item);
- idToViewMap.put(item.id, view);
- }
- }
-
- for (ItemInfo itemToRemove : matcher.filterItemInfos(items)) {
- View child = idToViewMap.get(itemToRemove.id);
-
- if (child != null) {
- // Note: We can not remove the view directly from CellLayoutChildren as this
- // does not re-mark the spaces as unoccupied.
- layoutParent.removeViewInLayout(child);
+ if (matcher.matchesInfo(info)) {
+ layout.removeViewInLayout(child);
if (child instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) child);
}
- } else if (itemToRemove.container >= 0) {
- // The item may belong to a folder.
- View parent = idToViewMap.get(itemToRemove.container);
- if (parent instanceof FolderIcon) {
- FolderInfo folderInfo = (FolderInfo) parent.getTag();
- folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
- if (((FolderIcon) parent).getFolder().isOpen()) {
- ((FolderIcon) parent).getFolder().close(false /* animate */);
+ } else if (child instanceof FolderIcon) {
+ FolderInfo folderInfo = (FolderInfo) info;
+ List<WorkspaceItemInfo> matches = folderInfo.contents.stream()
+ .filter(matcher::matchesInfo)
+ .collect(Collectors.toList());
+ if (!matches.isEmpty()) {
+ folderInfo.removeAll(matches, false);
+ if (((FolderIcon) child).getFolder().isOpen()) {
+ ((FolderIcon) child).getFolder().close(false /* animate */);
}
}
}
@@ -3143,9 +3134,8 @@
}
public void removeAbandonedPromise(String packageName, UserHandle user) {
- HashSet<String> packages = new HashSet<>(1);
- packages.add(packageName);
- ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
+ ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(
+ Collections.singleton(packageName), user);
mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
removeItemsByMatcher(matcher);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 00bdb70..769cb5e 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -154,7 +154,7 @@
public void updateProgressBar(AppInfo app) {
updateAllIcons((child) -> {
if (child.getTag() == app) {
- child.applyProgressLevel(app.getProgressLevel());
+ child.applyProgressLevel();
}
});
}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index 511de30..a79ec43 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -16,7 +16,6 @@
package com.android.launcher3.allapps.search;
-
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 63fa391..61938d1 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1394,10 +1394,10 @@
mItemsInvalidated = true;
}
- public void onRemove(WorkspaceItemInfo item) {
+ @Override
+ public void onRemove(List<WorkspaceItemInfo> items) {
mItemsInvalidated = true;
- View v = getViewForInfo(item);
- mContent.removeItem(v);
+ items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
if (mState == STATE_ANIMATING) {
mRearrangeOnClose = true;
} else {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 3296eed..fe310f6 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -695,9 +695,9 @@
}
@Override
- public void onRemove(WorkspaceItemInfo item) {
+ public void onRemove(List<WorkspaceItemInfo> items) {
boolean wasDotted = mDotInfo.hasDot();
- mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
+ items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
boolean isDotted = mDotInfo.hasDot();
updateDotScale(wasDotted, isDotted);
setContentDescription(getAccessiblityTitle(mInfo.title));
diff --git a/src/com/android/launcher3/graphics/GridOptionsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
similarity index 97%
rename from src/com/android/launcher3/graphics/GridOptionsProvider.java
rename to src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 08d7e4c..cb42e7a 100644
--- a/src/com/android/launcher3/graphics/GridOptionsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -40,9 +40,9 @@
* /default_grid: Call update to set the current grid, with values
* name: name of the grid to apply
*/
-public class GridOptionsProvider extends ContentProvider {
+public class GridCustomizationsProvider extends ContentProvider {
- private static final String TAG = "GridOptionsProvider";
+ private static final String TAG = "GridCustomizationsProvider";
private static final String KEY_NAME = "name";
private static final String KEY_ROWS = "rows";
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 9971990..ce824df 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -24,6 +24,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -34,10 +35,12 @@
import android.util.Pair;
import android.util.Property;
import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.Themes;
import java.lang.ref.WeakReference;
@@ -77,6 +80,9 @@
private static final SparseArray<WeakReference<Pair<Path, Bitmap>>> sShadowCache =
new SparseArray<>();
+ private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
+ private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
+
private final Matrix mTmpMatrix = new Matrix();
private final PathMeasure mPathMeasure = new PathMeasure();
@@ -91,6 +97,9 @@
private Bitmap mShadowBitmap;
private final int mIndicatorColor;
+ private final int mSystemAccentColor;
+ private final int mSystemBackgroundColor;
+ private final boolean mIsDarkMode;
private int mTrackAlpha;
private float mTrackLength;
@@ -104,7 +113,23 @@
private ObjectAnimator mCurrentAnim;
+ private boolean mIsStartable;
+
public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
+ this(
+ info,
+ IconPalette.getPreloadProgressColor(context, info.bitmap.color),
+ getPreloadColors(context),
+ (context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK
+ & Configuration.UI_MODE_NIGHT_YES) != 0) /* isDarkMode */;
+ }
+
+ public PreloadIconDrawable(
+ ItemInfoWithIcon info,
+ int indicatorColor,
+ int[] preloadColors,
+ boolean isDarkMode) {
super(info.bitmap);
mItem = info;
mShapePath = getShapePath();
@@ -114,11 +139,14 @@
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
- mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
+ mIndicatorColor = indicatorColor;
- setInternalProgress(0);
+ mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
+ mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
+ mIsDarkMode = isDarkMode;
- setIsDisabled(!info.isAppStartable());
+ setInternalProgress(info.getProgressLevel());
+ setIsStartable(info.isAppStartable());
}
@Override
@@ -144,7 +172,7 @@
}
private Bitmap getShadowBitmap(int width, int height, float shadowRadius) {
- int key = (width << 16) | height;
+ int key = ((width << 16) | height) * (mIsDarkMode ? -1 : 1);
WeakReference<Pair<Path, Bitmap>> shadowRef = sShadowCache.get(key);
Pair<Path, Bitmap> cache = shadowRef != null ? shadowRef.get() : null;
Bitmap shadow = cache != null && cache.first.equals(mShapePath) ? cache.second : null;
@@ -153,8 +181,9 @@
}
shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(shadow);
- mProgressPaint.setShadowLayer(shadowRadius, 0, 0, COLOR_SHADOW);
- mProgressPaint.setColor(COLOR_TRACK);
+ mProgressPaint.setShadowLayer(shadowRadius, 0, 0, mIsStartable
+ ? COLOR_SHADOW : mSystemAccentColor);
+ mProgressPaint.setColor(mIsStartable ? COLOR_TRACK : mSystemBackgroundColor);
mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
c.drawPath(mScaledTrackPath, mProgressPaint);
mProgressPaint.clearShadowLayer();
@@ -172,7 +201,7 @@
}
// Draw track.
- mProgressPaint.setColor(mIndicatorColor);
+ mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor);
mProgressPaint.setAlpha(mTrackAlpha);
if (mShadowBitmap != null) {
canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
@@ -211,6 +240,14 @@
return !mRanFinishAnimation;
}
+ /** Sets whether this icon should display the startable app UI. */
+ public void setIsStartable(boolean isStartable) {
+ if (mIsStartable != isStartable) {
+ mIsStartable = isStartable;
+ setIsDisabled(!isStartable);
+ }
+ }
+
private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) {
if (mCurrentAnim != null) {
mCurrentAnim.cancel();
@@ -291,10 +328,73 @@
invalidateSelf();
}
+ private static int[] getPreloadColors(Context context) {
+ Context dayNightThemeContext = new ContextThemeWrapper(
+ context, android.R.style.Theme_DeviceDefault_DayNight);
+ int[] preloadColors = new int[2];
+
+ preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getColorAccent(dayNightThemeContext);
+ preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getColorBackgroundFloating(
+ dayNightThemeContext);
+
+ return preloadColors;
+ }
+
/**
* Returns a FastBitmapDrawable with the icon.
*/
public static PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
return new PreloadIconDrawable(info, context);
}
+
+ @Override
+ public ConstantState getConstantState() {
+ return new PreloadIconConstantState(
+ mBitmap,
+ mIconColor,
+ !mItem.isAppStartable(),
+ mItem,
+ mIndicatorColor,
+ new int[] {mSystemAccentColor, mSystemBackgroundColor},
+ mIsDarkMode);
+ }
+
+ protected static class PreloadIconConstantState extends FastBitmapConstantState {
+
+ protected final ItemInfoWithIcon mInfo;
+ protected final int mIndicatorColor;
+ protected final int[] mPreloadColors;
+ protected final boolean mIsDarkMode;
+ protected final int mLevel;
+
+ public PreloadIconConstantState(
+ Bitmap bitmap,
+ int iconColor,
+ boolean isDisabled,
+ ItemInfoWithIcon info,
+ int indicatorColor,
+ int[] preloadColors,
+ boolean isDarkMode) {
+ super(bitmap, iconColor, isDisabled);
+ mInfo = info;
+ mIndicatorColor = indicatorColor;
+ mPreloadColors = preloadColors;
+ mIsDarkMode = isDarkMode;
+ mLevel = info.getProgressLevel();
+ }
+
+ @Override
+ public PreloadIconDrawable newDrawable() {
+ return new PreloadIconDrawable(
+ mInfo,
+ mIndicatorColor,
+ mPreloadColors,
+ mIsDarkMode);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
}
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
index b7dd092..1bd252b 100644
--- a/src/com/android/launcher3/icons/ClockDrawableWrapper.java
+++ b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
@@ -308,7 +308,7 @@
return new ClockConstantState(mInfo, isDisabled());
}
- private static class ClockConstantState extends MyConstantState {
+ private static class ClockConstantState extends FastBitmapConstantState {
private final ClockBitmapInfo mInfo;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f74c8b5..8438622 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -588,27 +588,26 @@
if (isSafeMode && !isSystemApp(context, intent)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
}
+ LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
+ if (activityInfo != null) {
+ info.setProgressLevel(
+ PackageManagerHelper
+ .getLoadingProgress(activityInfo),
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+ }
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
tempPackageKey.update(targetPkg, c.user);
SessionInfo si = installingPkgs.get(tempPackageKey);
- LauncherActivityInfo activityInfo =
- c.getLauncherActivityInfo();
if (si == null) {
info.runtimeStatusFlags &=
- ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+ ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
} else if (activityInfo == null) {
int installProgress = (int) (si.getProgress() * 100);
info.setProgressLevel(
installProgress,
PackageInstallInfo.STATUS_INSTALLING);
- } else {
- info.setProgressLevel(
- PackageManagerHelper
- .getLoadingProgress(activityInfo),
- PackageInstallInfo
- .STATUS_INSTALLED_DOWNLOADING);
}
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 2c99df7..f7b43d6 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -51,6 +51,7 @@
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
/**
* Class for handling model updates.
@@ -259,7 +260,9 @@
* Removes all the items from the database matching {@param matcher}.
*/
public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
- deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
+ deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false)
+ .filter(matcher::matchesInfo)
+ .collect(Collectors.toList()));
}
/**
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index dde0cf4..7f70bad 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -179,7 +179,7 @@
// Sets the progress level, installation and incremental download flags.
info.setProgressLevel(
PackageManagerHelper.getLoadingProgress(lai),
- PackageInstallInfo.STATUS_INSTALLED);
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
}
@Override
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 06a2c92..cc783f7 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -40,6 +40,8 @@
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.OptionalInt;
import java.util.stream.IntStream;
@@ -137,9 +139,16 @@
* @param item
*/
public void remove(WorkspaceItemInfo item, boolean animate) {
- contents.remove(item);
+ removeAll(Collections.singletonList(item), animate);
+ }
+
+ /**
+ * Remove all matching app or shortcut. Does not change the DB.
+ */
+ public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
+ contents.removeAll(items);
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onRemove(item);
+ mListeners.get(i).onRemove(items);
}
itemsChanged(animate);
}
@@ -166,9 +175,9 @@
}
public interface FolderListener {
- public void onAdd(WorkspaceItemInfo item, int rank);
- public void onRemove(WorkspaceItemInfo item);
- public void onItemsChanged(boolean animate);
+ void onAdd(WorkspaceItemInfo item, int rank);
+ void onRemove(List<WorkspaceItemInfo> item);
+ void onItemsChanged(boolean animate);
}
public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index 1831ffd..8469569 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -21,6 +21,8 @@
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.Nullable;
+
/**
* Represents a SearchAction with in launcher
*/
@@ -28,9 +30,10 @@
public static final int FLAG_SHOULD_START = 1 << 1;
public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2;
+ public static final int FLAG_BADGE_WITH_PACKAGE = 1 << 3;
+ public static final int FLAG_PRIMARY_ICON_FROM_TITLE = 1 << 4;
private final String mFallbackPackageName;
-
private int mFlags = 0;
private final Icon mIcon;
@@ -54,12 +57,15 @@
title = info.title;
}
- public void setFlags(int flags) {
- mFlags = flags;
+ /**
+ * Returns if multiple flags are all available.
+ */
+ public boolean hasFlags(int flags) {
+ return (mFlags & flags) != 0;
}
- public int getFlags() {
- return mFlags;
+ public void setFlags(int flags) {
+ mFlags |= flags ;
}
@Override
@@ -93,20 +99,13 @@
mPendingIntent = pendingIntent;
}
-
- /**
- * Returns if a flag is set on instance
- */
- public boolean hasFlag(int flag) {
- return (mFlags & flag) == flag;
+ @Nullable
+ public Icon getIcon() {
+ return mIcon;
}
@Override
public ItemInfoWithIcon clone() {
return new SearchActionItemInfo(this);
}
-
- public Icon getIcon() {
- return mIcon;
- }
}
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 47d214d..c9fb956 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -15,7 +15,10 @@
*/
package com.android.launcher3.settings;
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
@@ -26,10 +29,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -130,7 +131,7 @@
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
EditText filterBox = view.findViewById(R.id.filter_box);
- filterBox.setVisibility(View.VISIBLE);
+ filterBox.setVisibility(VISIBLE);
filterBox.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
@@ -212,7 +213,7 @@
Set<String> pluginActions = manager.getPluginActions();
- ArrayMap<Pair<String, String>, ArrayList<Pair<String, ServiceInfo>>> plugins =
+ ArrayMap<Pair<String, String>, ArrayList<Pair<String, ResolveInfo>>> plugins =
new ArrayMap<>();
Set<String> pluginPermissionApps = pm.getPackagesHoldingPermissions(
@@ -224,7 +225,7 @@
for (String action : pluginActions) {
String name = toName(action);
List<ResolveInfo> result = pm.queryIntentServices(
- new Intent(action), MATCH_DISABLED_COMPONENTS);
+ new Intent(action), MATCH_DISABLED_COMPONENTS | GET_RESOLVED_FILTER);
for (ResolveInfo info : result) {
String packageName = info.serviceInfo.packageName;
if (!pluginPermissionApps.contains(packageName)) {
@@ -235,7 +236,7 @@
if (!plugins.containsKey(key)) {
plugins.put(key, new ArrayList<>());
}
- plugins.get(key).add(Pair.create(name, info.serviceInfo));
+ plugins.get(key).add(Pair.create(name, info));
}
}
@@ -243,11 +244,11 @@
plugins.forEach((key, si) -> {
String packageName = key.first;
List<ComponentName> componentNames = si.stream()
- .map(p -> new ComponentName(packageName, p.second.name))
+ .map(p -> new ComponentName(packageName, p.second.serviceInfo.name))
.collect(Collectors.toList());
if (!componentNames.isEmpty()) {
SwitchPreference pref = new PluginPreference(
- prefContext, si.get(0).second.applicationInfo, enabler, componentNames);
+ prefContext, si.get(0).second, enabler, componentNames);
pref.setSummary("Plugins: "
+ si.stream().map(p -> p.first).collect(Collectors.joining(", ")));
mPluginsCategory.addPreference(pref);
@@ -341,21 +342,33 @@
}
private static class PluginPreference extends SwitchPreference {
- private final boolean mHasSettings;
- private final PreferenceDataStore mPluginEnabler;
private final String mPackageName;
+ private final ResolveInfo mSettingsInfo;
+ private final PreferenceDataStore mPluginEnabler;
private final List<ComponentName> mComponentNames;
- PluginPreference(Context prefContext, ApplicationInfo info,
+ PluginPreference(Context prefContext, ResolveInfo pluginInfo,
PreferenceDataStore pluginEnabler, List<ComponentName> componentNames) {
super(prefContext);
PackageManager pm = prefContext.getPackageManager();
- mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
- .setPackage(info.packageName), 0) != null;
- mPackageName = info.packageName;
- mComponentNames = componentNames;
+ mPackageName = pluginInfo.serviceInfo.applicationInfo.packageName;
+ Intent settingsIntent = new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName);
+ // If any Settings activity in app has category filters, set plugin action as category.
+ List<ResolveInfo> settingsInfos =
+ pm.queryIntentActivities(settingsIntent, GET_RESOLVED_FILTER);
+ if (pluginInfo.filter != null) {
+ for (ResolveInfo settingsInfo : settingsInfos) {
+ if (settingsInfo.filter != null && settingsInfo.filter.countCategories() > 0) {
+ settingsIntent.addCategory(pluginInfo.filter.getAction(0));
+ break;
+ }
+ }
+ }
+
+ mSettingsInfo = pm.resolveActivity(settingsIntent, 0);
mPluginEnabler = pluginEnabler;
- setTitle(info.loadLabel(pm));
+ mComponentNames = componentNames;
+ setTitle(pluginInfo.loadLabel(pm));
setChecked(isPluginEnabled());
setWidgetLayoutResource(R.layout.switch_preference_with_settings);
}
@@ -396,17 +409,14 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
- : View.GONE);
- holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
- : View.GONE);
+ boolean hasSettings = mSettingsInfo != null;
+ holder.findViewById(R.id.settings).setVisibility(hasSettings ? VISIBLE : GONE);
+ holder.findViewById(R.id.divider).setVisibility(hasSettings ? VISIBLE : GONE);
holder.findViewById(R.id.settings).setOnClickListener(v -> {
- ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
- new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName), 0);
- if (result != null) {
+ if (hasSettings) {
v.getContext().startActivity(new Intent().setComponent(
- new ComponentName(result.activityInfo.packageName,
- result.activityInfo.name)));
+ new ComponentName(mSettingsInfo.activityInfo.packageName,
+ mSettingsInfo.activityInfo.name)));
}
});
holder.itemView.setOnLongClickListener(v -> {
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 45172b5..44bcc34 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -90,4 +90,9 @@
public float getWorkspaceScrimAlpha(Launcher launcher) {
return 0.3f;
}
+
+ @Override
+ public int getVisibleElements(Launcher launcher) {
+ return super.getVisibleElements(launcher) & ~TASKBAR;
+ }
}
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 8b72177..ec949eb 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -37,7 +37,8 @@
PLAY_ATOMIC_OVERVIEW_SCALE,
PLAY_ATOMIC_OVERVIEW_PEEK,
SKIP_OVERVIEW,
- SKIP_DEPTH_CONTROLLER
+ SKIP_DEPTH_CONTROLLER,
+ SKIP_TASKBAR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationFlags {}
@@ -46,6 +47,7 @@
public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
public static final int SKIP_OVERVIEW = 1 << 3;
public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
+ public static final int SKIP_TASKBAR = 1 << 5;
public long duration;
public boolean userControlled;
@@ -72,6 +74,7 @@
ANIM_OVERVIEW_MODAL,
ANIM_DEPTH,
ANIM_OVERVIEW_ACTIONS_FADE,
+ ANIM_TASKBAR_FADE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -91,8 +94,9 @@
public static final int ANIM_OVERVIEW_MODAL = 13;
public static final int ANIM_DEPTH = 14;
public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
+ public static final int ANIM_TASKBAR_FADE = 16;
- private static final int ANIM_TYPES_COUNT = 16;
+ private static final int ANIM_TYPES_COUNT = 17;
protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index d9483e5..2647d6f 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -250,7 +250,7 @@
*/
public static void onClickSearchAction(Launcher launcher, SearchActionItemInfo itemInfo) {
if (itemInfo.getIntent() != null) {
- if (itemInfo.hasFlag(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
+ if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
launcher.startActivityForResult(itemInfo.getIntent(), 0);
} else {
launcher.startActivity(itemInfo.getIntent());
@@ -258,9 +258,9 @@
} else if (itemInfo.getPendingIntent() != null) {
try {
PendingIntent pendingIntent = itemInfo.getPendingIntent();
- if (!itemInfo.hasFlag(SearchActionItemInfo.FLAG_SHOULD_START)) {
+ if (!itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START)) {
pendingIntent.send();
- } else if (itemInfo.hasFlag(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
+ } else if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
launcher.startIntentSenderForResult(pendingIntent.getIntentSender(), 0, null, 0,
0, 0);
} else {
@@ -302,7 +302,7 @@
intent.setPackage(null);
}
}
- if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) {
+ if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation(v)) {
// Preload the icon to reduce latency b/w swapping the floating view with the original.
FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 355c949..3b7bcc2 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -31,6 +31,8 @@
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Utilities;
+
import java.util.ArrayList;
/**
@@ -179,23 +181,35 @@
return mInfo;
}
+ /** Creates and up-to-date DisplayController.Info for the given context. */
+ public Info createInfoForContext(Context context) {
+ Display display = Utilities.ATLEAST_R
+ ? context.getDisplay()
+ : context
+ .getSystemService(DisplayManager.class)
+ .getDisplay(mId);
+ return display == null
+ ? new Info(context)
+ : new Info(context, display);
+ }
+
protected void handleOnChange() {
Info oldInfo = mInfo;
- Info info = new Info(mDisplayContext);
+ Info newInfo = createInfoForContext(mDisplayContext);
int change = 0;
- if (info.hasDifferentSize(oldInfo)) {
+ if (newInfo.hasDifferentSize(oldInfo)) {
change |= CHANGE_SIZE;
}
- if (oldInfo.rotation != info.rotation) {
+ if (newInfo.rotation != oldInfo.rotation) {
change |= CHANGE_ROTATION;
}
- if (info.singleFrameMs != oldInfo.singleFrameMs) {
+ if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
change |= CHANGE_FRAME_DELAY;
}
if (change != 0) {
- mInfo = info;
+ mInfo = newInfo;
final int flags = change;
MAIN_EXECUTOR.execute(() -> notifyChange(flags));
}
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index d26bb57..354609d 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -20,10 +20,7 @@
import android.os.UserHandle;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import java.util.HashSet;
@@ -37,34 +34,15 @@
boolean matches(ItemInfo info, ComponentName cn);
/**
- * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}.
+ * Returns true if the itemInfo matches this check
*/
- default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
- HashSet<ItemInfo> filtered = new HashSet<>();
- for (ItemInfo i : infos) {
- if (i instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo info = (WorkspaceItemInfo) i;
- ComponentName cn = info.getTargetComponent();
- if (cn != null && matches(info, cn)) {
- filtered.add(info);
- }
- } else if (i instanceof FolderInfo) {
- FolderInfo info = (FolderInfo) i;
- for (WorkspaceItemInfo s : info.contents) {
- ComponentName cn = s.getTargetComponent();
- if (cn != null && matches(s, cn)) {
- filtered.add(s);
- }
- }
- } else if (i instanceof LauncherAppWidgetInfo) {
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
- ComponentName cn = info.providerName;
- if (cn != null && matches(info, cn)) {
- filtered.add(info);
- }
- }
+ default boolean matchesInfo(ItemInfo info) {
+ if (info != null) {
+ ComponentName cn = info.getTargetComponent();
+ return cn != null && matches(info, cn);
+ } else {
+ return false;
}
- return filtered;
}
/**
@@ -96,7 +74,7 @@
return (info, cn) -> components.contains(cn) && info.user.equals(user);
}
- static ItemInfoMatcher ofPackages(HashSet<String> packageNames, UserHandle user) {
+ static ItemInfoMatcher ofPackages(Set<String> packageNames, UserHandle user) {
return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user);
}
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index b74686f..55d17fc 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -81,6 +81,11 @@
return getAttrColor(context, android.R.attr.colorAccent);
}
+ /** Returns the floating background color attribute. */
+ public static int getColorBackgroundFloating(Context context) {
+ return getAttrColor(context, android.R.attr.colorBackgroundFloating);
+ }
+
public static int getAttrColor(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int colorAccent = ta.getColor(0, 0);
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 1857c5a..23c3722 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -51,8 +51,10 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -252,12 +254,26 @@
@SuppressWarnings("WrongThread")
private static void getIconResult(Launcher l, View originalView, ItemInfo info, RectF pos,
IconLoadResult iconLoadResult) {
- Drawable drawable = null;
+ Drawable drawable;
+ Drawable btvIcon;
Drawable badge = null;
boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
&& !info.isDisabled(); // Use original icon for disabled icons.
- Drawable btvIcon = originalView instanceof BubbleTextView
- ? ((BubbleTextView) originalView).getIcon() : null;
+
+ if (originalView instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) originalView;
+
+ if (info instanceof ItemInfoWithIcon
+ && (((ItemInfoWithIcon) info).runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+ btvIcon = btv.makePreloadIcon();
+ } else {
+ btvIcon = btv.getIcon();
+ }
+ } else {
+ btvIcon = null;
+ }
+
if (info instanceof SystemShortcut) {
if (originalView instanceof ImageView) {
drawable = ((ImageView) originalView).getDrawable();
@@ -266,6 +282,9 @@
} else {
drawable = originalView.getBackground();
}
+ } else if (btvIcon instanceof PreloadIconDrawable) {
+ // Force the progress bar to display.
+ drawable = btvIcon;
} else {
int width = (int) pos.width();
int height = (int) pos.height();
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
index 7fbd6ac..0fc61f0 100644
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
+++ b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
@@ -20,7 +20,7 @@
/**
* Event used for the feedback loop to the plugin. (and future aiai)
*
- * @deprecated Use SearchTargetEvent
+ * @deprecated Use {@link android.app.search.SearchTargetEvent}
*/
@Deprecated
public class SearchTargetEventLegacy {