Merge "Removing left over experimental overlays and frame width (2/2)" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index a817415..6568a26 100644
--- a/Android.mk
+++ b/Android.mk
@@ -251,7 +251,7 @@
LOCAL_PACKAGE_NAME := Launcher3QuickStepGo
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PRODUCT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3GoIconRecents
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
LOCAL_FULL_LIBS_MANIFEST_FILES := \
@@ -297,7 +297,8 @@
LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
LOCAL_PRIVILEGED_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
+LOCAL_PRODUCT_MODULE := true
+LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep Launcher3QuickStepGo
LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
LOCAL_FULL_LIBS_MANIFEST_FILES := \
diff --git a/go/quickstep/res/layout/clear_all_button.xml b/go/quickstep/res/layout/clear_all_button.xml
index 003ee86..be76d53 100644
--- a/go/quickstep/res/layout/clear_all_button.xml
+++ b/go/quickstep/res/layout/clear_all_button.xml
@@ -23,7 +23,9 @@
android:id="@+id/clear_all_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginVertical="16dp"
android:layout_gravity="center_horizontal"
+ android:paddingHorizontal="32dp"
android:background="@drawable/clear_all_button"
android:gravity="center"
android:text="@string/recents_clear_all"
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 8651698..4f2b422 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -43,9 +43,10 @@
public static final int MAX_TASKS_TO_DISPLAY = 6;
public static final int TASKS_START_POSITION = 1;
+ public static final int ITEM_TYPE_TASK = 0;
+ public static final int ITEM_TYPE_CLEAR_ALL = 1;
+
private static final String TAG = "TaskAdapter";
- private static final int ITEM_TYPE_TASK = 0;
- private static final int ITEM_TYPE_CLEAR_ALL = 1;
private final TaskListLoader mLoader;
private TaskActionController mTaskActionController;
private OnClickListener mClearAllListener;
@@ -145,7 +146,7 @@
});
mLoader.loadTaskThumbnail(task, () -> {
if (Objects.equals(Optional.of(task), taskHolder.getTask())) {
- taskHolder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
+ taskHolder.getTaskItemView().setThumbnail(task.thumbnail);
}
});
break;
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index 5755df4..49b6aaa 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep;
-import android.graphics.Bitmap;
-
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -58,12 +56,11 @@
*/
public void bindTask(@NonNull Task task, boolean willAnimate) {
mTask = task;
- Bitmap thumbnail = (task.thumbnail != null) ? task.thumbnail.thumbnail : null;
if (willAnimate) {
- mTaskItemView.startContentAnimation(task.icon, thumbnail, task.titleDescription);
+ mTaskItemView.startContentAnimation(task.icon, task.thumbnail, task.titleDescription);
} else {
mTaskItemView.setIcon(task.icon);
- mTaskItemView.setThumbnail(thumbnail);
+ mTaskItemView.setThumbnail(task.thumbnail);
mTaskItemView.setLabel(task.titleDescription);
}
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
index 98407d8..19951bb 100644
--- a/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
+++ b/go/quickstep/src/com/android/quickstep/TaskSwipeCallback.java
@@ -17,6 +17,9 @@
import static androidx.recyclerview.widget.ItemTouchHelper.RIGHT;
+import static com.android.quickstep.TaskAdapter.ITEM_TYPE_CLEAR_ALL;
+
+import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -45,4 +48,14 @@
mTaskActionController.removeTask((TaskHolder) viewHolder);
}
}
+
+ @Override
+ public int getSwipeDirs(@NonNull RecyclerView recyclerView,
+ @NonNull ViewHolder viewHolder) {
+ if (viewHolder.getItemViewType() == ITEM_TYPE_CLEAR_ALL) {
+ // Clear all button should not be swipable.
+ return 0;
+ }
+ return super.getSwipeDirs(recyclerView, viewHolder);
+ }
}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index b7ed5b5..b8c482d 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -125,7 +125,7 @@
Task task = optTask.get();
// Update thumbnail on the task.
task.thumbnail = thumbnailData;
- taskView.setThumbnail(thumbnailData.thumbnail);
+ taskView.setThumbnail(thumbnailData);
return task;
}
}
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 7d9916e..0b5ed56 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -18,9 +18,8 @@
import static com.android.quickstep.views.TaskLayoutUtils.getTaskHeight;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -33,6 +32,8 @@
import androidx.annotation.Nullable;
import com.android.launcher3.R;
+import com.android.quickstep.ThumbnailDrawable;
+import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* View representing an individual task item with the icon + thumbnail adjacent to the task label.
@@ -132,10 +133,10 @@
/**
* Set the task thumbnail for the task. Sets to a default thumbnail if null.
*
- * @param thumbnail task thumbnail for the task
+ * @param thumbnailData task thumbnail data for the task
*/
- public void setThumbnail(@Nullable Bitmap thumbnail) {
- mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnail));
+ public void setThumbnail(@Nullable ThumbnailData thumbnailData) {
+ mThumbnailDrawable.setCurrentDrawable(getSafeThumbnail(thumbnailData));
}
public View getThumbnailView() {
@@ -151,8 +152,8 @@
* @param endThumbnail the thumbnail to animate to
* @param endLabel the label to animate to
*/
- public void startContentAnimation(@Nullable Drawable endIcon, @Nullable Bitmap endThumbnail,
- @Nullable String endLabel) {
+ public void startContentAnimation(@Nullable Drawable endIcon,
+ @Nullable ThumbnailData endThumbnail, @Nullable String endLabel) {
mIconDrawable.startNewTransition(getSafeIcon(endIcon));
mThumbnailDrawable.startNewTransition(getSafeThumbnail(endThumbnail));
// TODO: Animation for label
@@ -171,12 +172,27 @@
return (icon != null) ? icon : mDefaultIcon;
}
- private @NonNull Drawable getSafeThumbnail(@Nullable Bitmap thumbnail) {
- return (thumbnail != null) ? new BitmapDrawable(getResources(), thumbnail)
- : mDefaultThumbnail;
+ private @NonNull Drawable getSafeThumbnail(@Nullable ThumbnailData thumbnailData) {
+ if (thumbnailData == null || thumbnailData.thumbnail == null) {
+ return mDefaultThumbnail;
+ }
+ int orientation = getResources().getConfiguration().orientation;
+ return new ThumbnailDrawable(thumbnailData, orientation /* requestedOrientation */);
}
private @NonNull String getSafeLabel(@Nullable String label) {
return (label != null) ? label : DEFAULT_LABEL;
}
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ int layerCount = mThumbnailDrawable.getNumberOfLayers();
+ for (int i = 0; i < layerCount; i++) {
+ Drawable drawable = mThumbnailDrawable.getDrawable(i);
+ if (drawable instanceof ThumbnailDrawable) {
+ ((ThumbnailDrawable) drawable).setRequestedOrientation(newConfig.orientation);
+ }
+ }
+ }
}
diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml
index 92a2b6a..0fd0eeb 100644
--- a/go/res/xml/device_profiles.xml
+++ b/go/res/xml/device_profiles.xml
@@ -30,7 +30,7 @@
launcher:name="Go Device"
launcher:minWidthDps="296"
launcher:minHeightDps="491.33"
- launcher:iconSize="60"
+ launcher:iconImageSize="60"
launcher:iconTextSize="14.0"
launcher:canBeDefault="true" />
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index ff0c907..1e44910 100644
--- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -55,7 +56,7 @@
return false;
}
- public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
+ public void onShortcutsChanged(List<ShortcutInfo> shortcuts) {
}
/**
@@ -64,7 +65,7 @@
* This method is intended to get the full details for shortcuts when they are added or updated,
* because we only get "key" fields in onShortcutsChanged().
*/
- public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
+ public List<ShortcutInfo> queryForFullDetails(String packageName,
List<String> shortcutIds, UserHandle user) {
return Collections.emptyList();
}
@@ -73,7 +74,7 @@
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
+ public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
UserHandle user) {
return Collections.emptyList();
}
@@ -96,7 +97,7 @@
Bundle startActivityOptions, UserHandle user) {
}
- public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+ public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
return null;
}
@@ -105,16 +106,16 @@
*
* If packageName is null, returns all pinned shortcuts regardless of package.
*/
- public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
+ public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
return Collections.emptyList();
}
- public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+ public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
List<String> shortcutIds, UserHandle user) {
return Collections.emptyList();
}
- public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
+ public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
return Collections.emptyList();
}
diff --git a/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml b/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml
new file mode 100644
index 0000000..52cc6fc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/arrow_toast_rounded_background.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2018 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="?android:attr/colorAccent" />
+ <corners android:radius="8dp" />
+</shape>
diff --git a/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
new file mode 100644
index 0000000..b0f2b4b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<merge
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="24dp"
+ android:paddingEnd="4dp"
+ android:background="@drawable/arrow_toast_rounded_background"
+ android:layout_gravity="center_horizontal"
+ android:elevation="2dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:textColor="@android:color/white"
+ android:textSize="16sp"/>
+ <ImageView
+ android:id="@+id/dismiss"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_gravity="center_vertical"
+ android:padding="10dp"
+ android:layout_marginStart="2dp"
+ android:layout_marginEnd="2dp"
+ android:alpha="0.7"
+ android:src="@drawable/ic_remove_no_shadow"
+ android:tint="@android:color/white"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/accessibility_close_task"/>
+ </LinearLayout>
+
+ <View
+ android:id="@+id/arrow"
+ android:elevation="2dp"
+ android:layout_width="10dp"
+ android:layout_height="8dp"
+ android:layout_marginTop="-2dp"
+ android:layout_gravity="center_horizontal"/>
+</merge>
diff --git a/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml b/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
new file mode 100644
index 0000000..b21c34b
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <com.android.launcher3.appprediction.PredictionRowView
+ android:id="@+id/prediction_row"
+ android:accessibilityPaneTitle="@string/title_app_suggestions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <com.android.launcher3.appprediction.AppsDividerView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/apps_divider_view" />
+</merge>
diff --git a/quickstep/recents_ui_overrides/res/layout/prediction_load_progress.xml b/quickstep/recents_ui_overrides/res/layout/prediction_load_progress.xml
new file mode 100644
index 0000000..20c4004
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/prediction_load_progress.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginLeft="20dp"
+ android:layout_marginRight="20dp"
+ android:indeterminate="true"
+ android:indeterminateOnly="true"
+ android:indeterminateTint="?workspaceTextColor" />
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
index 1e8d0cc..7426e30 100644
--- a/quickstep/recents_ui_overrides/res/values/colors.xml
+++ b/quickstep/recents_ui_overrides/res/values/colors.xml
@@ -1,4 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="chip_hint_foreground_color">#fff</color>
+
+ <color name="all_apps_label_text">#61000000</color>
+ <color name="all_apps_label_text_dark">#61FFFFFF</color>
+ <color name="all_apps_prediction_row_separator">#3c000000</color>
+ <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
index b654d5c..f991435 100644
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -12,4 +12,13 @@
<dimen name="chip_text_top_padding">4dp</dimen>
<dimen name="chip_text_start_padding">10dp</dimen>
<dimen name="chip_text_size">14sp</dimen>
+
+ <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
+ <dimen name="all_apps_label_top_padding">16dp</dimen>
+ <dimen name="all_apps_label_bottom_padding">8dp</dimen>
+ <dimen name="all_apps_label_text_size">14sp</dimen>
+ <dimen name="all_apps_tip_bottom_margin">8dp</dimen>
+ <!-- The size of corner radius of the arrow in the arrow toast. -->
+ <dimen name="arrow_toast_corner_radius">2dp</dimen>
+
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
index c60cf5a..1ddd3f5 100644
--- a/quickstep/recents_ui_overrides/res/values/override.xml
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -21,6 +21,8 @@
<string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
+ <string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string>
+
<string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
</resources>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
new file mode 100644
index 0000000..c5c4add
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 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;
+
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+
+import java.util.function.BiPredicate;
+
+public class LauncherInitListenerEx extends LauncherInitListener {
+
+ public LauncherInitListenerEx(BiPredicate<Launcher, Boolean> onInitListener) {
+ super(onInitListener);
+ }
+
+ @Override
+ protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
+ return super.init(launcher, alreadyOnHome);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
new file mode 100644
index 0000000..948f39e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -0,0 +1,207 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.TriangleShape;
+import com.android.systemui.shared.system.LauncherEventUtil;
+
+import androidx.core.content.ContextCompat;
+
+/**
+ * All apps tip view aligned just above prediction apps, shown to users that enter all apps for the
+ * first time.
+ */
+public class AllAppsTipView extends AbstractFloatingView {
+
+ private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
+ private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
+ private static final long SHOW_DELAY_MS = 200;
+ private static final long SHOW_DURATION_MS = 300;
+ private static final long HIDE_DURATION_MS = 100;
+
+ private final Launcher mLauncher;
+ private final Handler mHandler = new Handler();
+
+ private AllAppsTipView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ private AllAppsTipView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setOrientation(LinearLayout.VERTICAL);
+
+ mLauncher = Launcher.getLauncher(context);
+
+ init(context);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ close(true);
+ }
+ return false;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (mIsOpen) {
+ if (animate) {
+ animate().alpha(0f)
+ .withLayer()
+ .setStartDelay(0)
+ .setDuration(HIDE_DURATION_MS)
+ .setInterpolator(Interpolators.ACCEL)
+ .withEndAction(() -> mLauncher.getDragLayer().removeView(this))
+ .start();
+ } else {
+ animate().cancel();
+ mLauncher.getDragLayer().removeView(this);
+ }
+ mLauncher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
+ mIsOpen = false;
+ }
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ON_BOARD_POPUP) != 0;
+ }
+
+ private void init(Context context) {
+ inflate(context, R.layout.arrow_toast, this);
+
+ TextView textView = findViewById(R.id.text);
+ textView.setText(R.string.all_apps_prediction_tip);
+
+ View dismissButton = findViewById(R.id.dismiss);
+ dismissButton.setOnClickListener(view -> {
+ mLauncher.getUserEventDispatcher().logActionTip(
+ LauncherEventUtil.DISMISS, ALL_APPS_PREDICTION_TIPS);
+ handleClose(true);
+ });
+
+ View arrowView = findViewById(R.id.arrow);
+ ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ arrowLp.width, arrowLp.height, false));
+ Paint arrowPaint = arrowDrawable.getPaint();
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+ arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
+ // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+ arrowPaint.setPathEffect(new CornerPathEffect(
+ context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
+ arrowView.setBackground(arrowDrawable);
+
+ mIsOpen = true;
+
+ mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
+ }
+
+ private static boolean showAllAppsTipIfNecessary(Launcher launcher) {
+ FloatingHeaderView floatingHeaderView = launcher.getAppsView().getFloatingHeaderView();
+ if (!floatingHeaderView.hasVisibleContent()
+ || AbstractFloatingView.getOpenView(launcher,
+ TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE) != null
+ || !launcher.isInState(ALL_APPS)
+ || hasSeenAllAppsTip(launcher)
+ || UserManagerCompat.getInstance(launcher).isDemoUser()
+ || ActivityManager.isRunningInTestHarness()) {
+ return false;
+ }
+
+ AllAppsTipView allAppsTipView = new AllAppsTipView(launcher.getAppsView().getContext(),
+ null);
+ launcher.getDragLayer().addView(allAppsTipView);
+
+ DragLayer.LayoutParams params = (DragLayer.LayoutParams) allAppsTipView.getLayoutParams();
+ params.gravity = Gravity.CENTER_HORIZONTAL;
+
+ int top = floatingHeaderView.findFixedRowByType(PredictionRowView.class).getTop();
+ allAppsTipView.setY(top - launcher.getResources().getDimensionPixelSize(
+ R.dimen.all_apps_tip_bottom_margin));
+
+ allAppsTipView.setAlpha(0);
+ allAppsTipView.animate()
+ .alpha(1f)
+ .withLayer()
+ .setStartDelay(SHOW_DELAY_MS)
+ .setDuration(SHOW_DURATION_MS)
+ .setInterpolator(Interpolators.DEACCEL)
+ .start();
+
+ launcher.getUserEventDispatcher().logActionTip(
+ LauncherEventUtil.VISIBLE, ALL_APPS_PREDICTION_TIPS);
+ return true;
+ }
+
+ private static boolean hasSeenAllAppsTip(Launcher launcher) {
+ return launcher.getSharedPrefs().getBoolean(ALL_APPS_TIP_SEEN, false);
+ }
+
+ public static void scheduleShowIfNeeded(Launcher launcher) {
+ if (!hasSeenAllAppsTip(launcher)) {
+ launcher.getStateManager().addStateListener(
+ new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == ALL_APPS) {
+ if (showAllAppsTipIfNecessary(launcher)) {
+ launcher.getStateManager().removeStateListener(this);
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
new file mode 100644
index 0000000..311db21
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -0,0 +1,308 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.FloatingHeaderRow;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.util.Themes;
+
+import androidx.annotation.ColorInt;
+import androidx.core.content.ContextCompat;
+
+/**
+ * A view which shows a horizontal divider
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class AppsDividerView extends View implements LauncherStateManager.StateListener,
+ FloatingHeaderRow {
+
+ private static final String ALL_APPS_VISITED_COUNT = "launcher.all_apps_visited_count";
+ private static final int SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT = 20;
+
+ public enum DividerType {
+ NONE,
+ LINE,
+ ALL_APPS_LABEL
+ }
+
+ private final Launcher mLauncher;
+ private final TextPaint mPaint = new TextPaint();
+ private DividerType mDividerType = DividerType.NONE;
+
+ private final @ColorInt int mStrokeColor;
+ private final @ColorInt int mAllAppsLabelTextColor;
+
+ private Layout mAllAppsLabelLayout;
+ private boolean mShowAllAppsLabel;
+
+ private FloatingHeaderView mParent;
+ private boolean mTabsHidden;
+ private FloatingHeaderRow[] mRows = FloatingHeaderRow.NO_ROWS;
+
+ private boolean mIsScrolledOut = false;
+
+ public AppsDividerView(Context context) {
+ this(context, null);
+ }
+
+ public AppsDividerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppsDividerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+
+ boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
+ mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
+
+ mStrokeColor = ContextCompat.getColor(context, isMainColorDark
+ ? R.color.all_apps_prediction_row_separator_dark
+ : R.color.all_apps_prediction_row_separator);
+
+ mAllAppsLabelTextColor = ContextCompat.getColor(context, isMainColorDark
+ ? R.color.all_apps_label_text_dark
+ : R.color.all_apps_label_text);
+ }
+
+ public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
+ mParent = parent;
+ mTabsHidden = tabsHidden;
+ mRows = rows;
+ updateDividerType();
+ }
+
+ @Override
+ public int getExpectedHeight() {
+ return getPaddingTop() + getPaddingBottom();
+ }
+
+ @Override
+ public boolean shouldDraw() {
+ return mDividerType != DividerType.NONE;
+ }
+
+ @Override
+ public boolean hasVisibleContent() {
+ return false;
+ }
+
+ private void updateDividerType() {
+ final DividerType dividerType;
+ if (!mTabsHidden) {
+ dividerType = DividerType.NONE;
+ } else {
+ // Check how many sections above me.
+ int sectionCount = 0;
+ for (FloatingHeaderRow row : mRows) {
+ if (row == this) {
+ break;
+ } else if (row.shouldDraw()) {
+ sectionCount ++;
+ }
+ }
+
+ if (mShowAllAppsLabel && sectionCount > 0) {
+ dividerType = DividerType.ALL_APPS_LABEL;
+ } else if (sectionCount == 1) {
+ dividerType = DividerType.LINE;
+ } else {
+ dividerType = DividerType.NONE;
+ }
+ }
+
+ if (mDividerType != dividerType) {
+ mDividerType = dividerType;
+ int topPadding;
+ int bottomPadding;
+ switch (dividerType) {
+ case LINE:
+ topPadding = 0;
+ bottomPadding = getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height);
+ mPaint.setColor(mStrokeColor);
+ break;
+ case ALL_APPS_LABEL:
+ topPadding = getAllAppsLabelLayout().getHeight() + getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_label_top_padding);
+ bottomPadding = getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_label_bottom_padding);
+ mPaint.setColor(mAllAppsLabelTextColor);
+ break;
+ case NONE:
+ default:
+ topPadding = bottomPadding = 0;
+ break;
+ }
+ setPadding(getPaddingLeft(), topPadding, getPaddingRight(), bottomPadding);
+ updateViewVisibility();
+ invalidate();
+ requestLayout();
+ if (mParent != null) {
+ mParent.onHeightUpdated();
+ }
+ }
+ }
+
+ private void updateViewVisibility() {
+ setVisibility(mDividerType == DividerType.NONE
+ ? GONE
+ : (mIsScrolledOut ? INVISIBLE : VISIBLE));
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mDividerType == DividerType.LINE) {
+ int side = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+ int y = getHeight() - (getPaddingBottom() / 2);
+ int x1 = getPaddingLeft() + side;
+ int x2 = getWidth() - getPaddingRight() - side;
+ canvas.drawLine(x1, y, x2, y, mPaint);
+ } else if (mDividerType == DividerType.ALL_APPS_LABEL) {
+ Layout textLayout = getAllAppsLabelLayout();
+ int x = getWidth() / 2 - textLayout.getWidth() / 2;
+ int y = getHeight() - getPaddingBottom() - textLayout.getHeight();
+ canvas.translate(x, y);
+ textLayout.draw(canvas);
+ canvas.translate(-x, -y);
+ }
+ }
+
+ private Layout getAllAppsLabelLayout() {
+ if (mAllAppsLabelLayout == null) {
+ mPaint.setAntiAlias(true);
+ mPaint.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
+ mPaint.setTextSize(
+ getResources().getDimensionPixelSize(R.dimen.all_apps_label_text_size));
+
+ CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label);
+ mAllAppsLabelLayout = StaticLayout.Builder.obtain(
+ allAppsLabelText, 0, allAppsLabelText.length(), mPaint,
+ Math.round(mPaint.measureText(allAppsLabelText.toString())))
+ .setAlignment(Layout.Alignment.ALIGN_CENTER)
+ .setMaxLines(1)
+ .setIncludePad(true)
+ .build();
+ }
+ return mAllAppsLabelLayout;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+ getPaddingBottom() + getPaddingTop());
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ if (shouldShowAllAppsLabel()) {
+ mShowAllAppsLabel = true;
+ mLauncher.getStateManager().addStateListener(this);
+ updateDividerType();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mLauncher.getStateManager().removeStateListener(this);
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) { }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == ALL_APPS) {
+ setAllAppsVisitedCount(getAllAppsVisitedCount() + 1);
+ } else {
+ if (mShowAllAppsLabel != shouldShowAllAppsLabel()) {
+ mShowAllAppsLabel = !mShowAllAppsLabel;
+ updateDividerType();
+ }
+
+ if (!mShowAllAppsLabel) {
+ mLauncher.getStateManager().removeStateListener(this);
+ }
+ }
+ }
+
+ private void setAllAppsVisitedCount(int count) {
+ mLauncher.getSharedPrefs().edit().putInt(ALL_APPS_VISITED_COUNT, count).apply();
+ }
+
+ private int getAllAppsVisitedCount() {
+ return mLauncher.getSharedPrefs().getInt(ALL_APPS_VISITED_COUNT, 0);
+ }
+
+ private boolean shouldShowAllAppsLabel() {
+ return getAllAppsVisitedCount() < SHOW_ALL_APPS_LABEL_ON_ALL_APPS_VISITED_COUNT;
+ }
+
+ @Override
+ public void setInsets(Rect insets, DeviceProfile grid) {
+ int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+ + grid.cellLayoutPaddingLeftRightPx;
+ setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
+ }
+
+ @Override
+ public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+ PropertySetter setter, Interpolator fadeInterpolator) {
+ // Don't use setViewAlpha as we want to control the visibility ourselves.
+ setter.setFloat(this, ALPHA, hasContent ? 1 : 0, fadeInterpolator);
+ }
+
+ @Override
+ public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+ setTranslationY(scroll);
+ mIsScrolledOut = isScrolledOut;
+ updateViewVisibility();
+ }
+
+ @Override
+ public Class<AppsDividerView> getTypeClass() {
+ return AppsDividerView.class;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
new file mode 100644
index 0000000..b9f4147
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.content.Context;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+
+public class ComponentKeyMapper {
+
+ protected final ComponentKey componentKey;
+ private final Context mContext;
+ private final DynamicItemCache mCache;
+
+ public ComponentKeyMapper(Context context, ComponentKey key, DynamicItemCache cache) {
+ mContext = context;
+ componentKey = key;
+ mCache = cache;
+ }
+
+ public String getPackage() {
+ return componentKey.componentName.getPackageName();
+ }
+
+ public String getComponentClass() {
+ return componentKey.componentName.getClassName();
+ }
+
+ public ComponentKey getComponentKey() {
+ return componentKey;
+ }
+
+ @Override
+ public String toString() {
+ return componentKey.toString();
+ }
+
+ public ItemInfoWithIcon getApp(AllAppsStore store) {
+ AppInfo item = store.getApp(componentKey);
+ if (item != null) {
+ return item;
+ } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
+ return mCache.getInstantApp(componentKey.componentName.getPackageName());
+ } else if (componentKey instanceof ShortcutKey) {
+ return mCache.getShortcutInfo((ShortcutKey) componentKey);
+ }
+ return null;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
new file mode 100644
index 0000000..4ecc39c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -0,0 +1,242 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static android.content.pm.PackageManager.MATCH_INSTANT;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.InstantAppResolver;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import androidx.annotation.MainThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * Utility class which loads and caches predicted items like instant apps and shortcuts, before
+ * they can be displayed on the UI
+ */
+public class DynamicItemCache {
+
+ private static final String TAG = "DynamicItemCache";
+ private static final boolean DEBUG = false;
+ private static final String DEFAULT_URL = "default-url";
+
+ private static final int BG_MSG_LOAD_SHORTCUTS = 1;
+ private static final int BG_MSG_LOAD_INSTANT_APPS = 2;
+
+ private static final int UI_MSG_UPDATE_SHORTCUTS = 1;
+ private static final int UI_MSG_UPDATE_INSTANT_APPS = 2;
+
+ private final Context mContext;
+ private final Handler mWorker;
+ private final Handler mUiHandler;
+ private final InstantAppResolver mInstantAppResolver;
+ private final Runnable mOnUpdateCallback;
+
+ private final Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
+ private final Map<String, InstantAppItemInfo> mInstantApps;
+
+ public DynamicItemCache(Context context, Runnable onUpdateCallback) {
+ mContext = context;
+ mWorker = new Handler(LauncherModel.getWorkerLooper(), this::handleWorkerMessage);
+ mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
+ mInstantAppResolver = InstantAppResolver.newInstance(context);
+ mOnUpdateCallback = onUpdateCallback;
+
+ mShortcuts = new HashMap<>();
+ mInstantApps = new HashMap<>();
+ }
+
+ public void cacheItems(List<ShortcutKey> shortcutKeys, List<String> pkgNames) {
+ if (!shortcutKeys.isEmpty()) {
+ mWorker.removeMessages(BG_MSG_LOAD_SHORTCUTS);
+ Message.obtain(mWorker, BG_MSG_LOAD_SHORTCUTS, shortcutKeys).sendToTarget();
+ }
+ if (!pkgNames.isEmpty()) {
+ mWorker.removeMessages(BG_MSG_LOAD_INSTANT_APPS);
+ Message.obtain(mWorker, BG_MSG_LOAD_INSTANT_APPS, pkgNames).sendToTarget();
+ }
+ }
+
+ private boolean handleWorkerMessage(Message msg) {
+ switch (msg.what) {
+ case BG_MSG_LOAD_SHORTCUTS: {
+ List<ShortcutKey> shortcutKeys = msg.obj != null ?
+ (List<ShortcutKey>) msg.obj : Collections.EMPTY_LIST;
+ Map<ShortcutKey, WorkspaceItemInfo> shortcutKeyAndInfos = new ArrayMap<>();
+ for (ShortcutKey shortcutKey : shortcutKeys) {
+ WorkspaceItemInfo workspaceItemInfo = loadShortcutWorker(shortcutKey);
+ if (workspaceItemInfo != null) {
+ shortcutKeyAndInfos.put(shortcutKey, workspaceItemInfo);
+ }
+ }
+ Message.obtain(mUiHandler, UI_MSG_UPDATE_SHORTCUTS, shortcutKeyAndInfos)
+ .sendToTarget();
+ return true;
+ }
+ case BG_MSG_LOAD_INSTANT_APPS: {
+ List<String> pkgNames = msg.obj != null ?
+ (List<String>) msg.obj : Collections.EMPTY_LIST;
+ List<InstantAppItemInfo> instantAppItemInfos = new ArrayList<>();
+ for (String pkgName : pkgNames) {
+ InstantAppItemInfo instantAppItemInfo = loadInstantApp(pkgName);
+ if (instantAppItemInfo != null) {
+ instantAppItemInfos.add(instantAppItemInfo);
+ }
+ }
+ Message.obtain(mUiHandler, UI_MSG_UPDATE_INSTANT_APPS, instantAppItemInfos)
+ .sendToTarget();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean handleUiMessage(Message msg) {
+ switch (msg.what) {
+ case UI_MSG_UPDATE_SHORTCUTS: {
+ mShortcuts.clear();
+ mShortcuts.putAll((Map<ShortcutKey, WorkspaceItemInfo>) msg.obj);
+ mOnUpdateCallback.run();
+ return true;
+ }
+ case UI_MSG_UPDATE_INSTANT_APPS: {
+ List<InstantAppItemInfo> instantAppItemInfos = (List<InstantAppItemInfo>) msg.obj;
+ mInstantApps.clear();
+ for (InstantAppItemInfo instantAppItemInfo : instantAppItemInfos) {
+ mInstantApps.put(instantAppItemInfo.getTargetComponent().getPackageName(),
+ instantAppItemInfo);
+ }
+ mOnUpdateCallback.run();
+ if (DEBUG) {
+ Log.d(TAG, String.format("Cache size: %d, Cache: %s",
+ mInstantApps.size(), mInstantApps.toString()));
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @WorkerThread
+ private WorkspaceItemInfo loadShortcutWorker(ShortcutKey shortcutKey) {
+ DeepShortcutManager mgr = DeepShortcutManager.getInstance(mContext);
+ List<ShortcutInfo> details = mgr.queryForFullDetails(
+ shortcutKey.componentName.getPackageName(),
+ Collections.<String>singletonList(shortcutKey.getId()),
+ shortcutKey.user);
+ if (!details.isEmpty()) {
+ WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
+ try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+ si.applyFrom(li.createShortcutIcon(details.get(0), true /* badged */, null));
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
+ }
+ return null;
+ }
+ return si;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "No shortcut found: " + shortcutKey.toString());
+ }
+ return null;
+ }
+
+ private InstantAppItemInfo loadInstantApp(String pkgName) {
+ PackageManager pm = mContext.getPackageManager();
+
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0);
+ if (!mInstantAppResolver.isInstantApp(ai)) {
+ return null;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+
+ String url = retrieveDefaultUrl(pkgName, pm);
+ if (url == null) {
+ Log.w(TAG, "no default-url available for pkg " + pkgName);
+ return null;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse(url));
+ InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
+ IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+ iconCache.getTitleAndIcon(info, false);
+ if (info.iconBitmap == null || iconCache.isDefaultIcon(info.iconBitmap, info.user)) {
+ return null;
+ }
+ return info;
+ }
+
+ @Nullable
+ public static String retrieveDefaultUrl(String pkgName, PackageManager pm) {
+ Intent mainIntent = new Intent().setAction(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER).setPackage(pkgName);
+ List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
+ mainIntent, MATCH_INSTANT | PackageManager.GET_META_DATA);
+ String url = null;
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ if (resolveInfo.activityInfo.metaData != null
+ && resolveInfo.activityInfo.metaData.containsKey(DEFAULT_URL)) {
+ url = resolveInfo.activityInfo.metaData.getString(DEFAULT_URL);
+ }
+ }
+ return url;
+ }
+
+ @UiThread
+ public InstantAppItemInfo getInstantApp(String pkgName) {
+ return mInstantApps.get(pkgName);
+ }
+
+ @MainThread
+ public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) {
+ return mShortcuts.get(key);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
new file mode 100644
index 0000000..6e5f461
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.content.ComponentName;
+import android.content.Intent;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.WorkspaceItemInfo;
+
+public class InstantAppItemInfo extends AppInfo {
+
+ public InstantAppItemInfo(Intent intent, String packageName) {
+ this.intent = intent;
+ this.componentName = new ComponentName(packageName, COMPONENT_CLASS_MARKER);
+ }
+
+ @Override
+ public ComponentName getTargetComponent() {
+ return componentName;
+ }
+
+ @Override
+ public WorkspaceItemInfo makeWorkspaceItem() {
+ WorkspaceItemInfo workspaceItemInfo = super.makeWorkspaceItem();
+ workspaceItemInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON
+ | WorkspaceItemInfo.FLAG_RESTORE_STARTED
+ | WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
+ workspaceItemInfo.intent.setPackage(componentName.getPackageName());
+ return workspaceItemInfo;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
new file mode 100644
index 0000000..bd78573
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -0,0 +1,214 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.launcher3.appprediction.PredictionUiStateManager.KEY_APP_SUGGESTION;
+
+import android.annotation.TargetApi;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.util.UiThreadHelper;
+
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public class PredictionAppTracker extends AppLaunchTracker
+ implements OnSharedPreferenceChangeListener {
+
+ private static final String TAG = "PredictionAppTracker";
+ private static final boolean DBG = false;
+
+ private static final int MSG_INIT = 0;
+ private static final int MSG_DESTROY = 1;
+ private static final int MSG_LAUNCH = 2;
+ private static final int MSG_PREDICT = 3;
+
+ private final Context mContext;
+ private final Handler mMessageHandler;
+
+ private boolean mEnabled;
+
+ // Accessed only on worker thread
+ private AppPredictor mHomeAppPredictor;
+ private AppPredictor mRecentsOverviewPredictor;
+
+ public PredictionAppTracker(Context context) {
+ mContext = context;
+ mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage);
+
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
+ prefs.registerOnSharedPreferenceChangeListener(this);
+ InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
+ }
+
+ @UiThread
+ private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+ // Reinitialize everything
+ setEnabled(mEnabled);
+ }
+
+ @Override
+ @UiThread
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (KEY_APP_SUGGESTION.equals(key)) {
+ setEnabled(prefs.getBoolean(KEY_APP_SUGGESTION, true));
+ }
+ }
+
+ @WorkerThread
+ private void destroy() {
+ if (mHomeAppPredictor != null) {
+ mHomeAppPredictor.destroy();
+ mHomeAppPredictor = null;
+ }
+ if (mRecentsOverviewPredictor != null) {
+ mRecentsOverviewPredictor.destroy();
+ mRecentsOverviewPredictor = null;
+ }
+ }
+
+ @WorkerThread
+ private AppPredictor createPredictor(Client client, int count) {
+ AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
+
+ AppPredictor predictor = apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(mContext)
+ .setUiSurface(client.id)
+ .setPredictedTargetCount(count)
+ .build());
+ predictor.registerPredictionUpdates(mContext.getMainExecutor(),
+ PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client));
+ predictor.requestPredictionUpdate();
+ return predictor;
+ }
+
+ @WorkerThread
+ private boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_INIT: {
+ // Destroy any existing clients
+ destroy();
+
+ // Initialize the clients
+ int count = InvariantDeviceProfile.INSTANCE.get(mContext).numColumns;
+ mHomeAppPredictor = createPredictor(Client.HOME, count);
+ mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
+ return true;
+ }
+ case MSG_DESTROY: {
+ destroy();
+ return true;
+ }
+ case MSG_LAUNCH: {
+ if (mEnabled && mHomeAppPredictor != null) {
+ mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj);
+ }
+ return true;
+ }
+ case MSG_PREDICT: {
+ if (mEnabled && mHomeAppPredictor != null) {
+ String client = (String) msg.obj;
+ if (Client.HOME.id.equals(client)) {
+ mHomeAppPredictor.requestPredictionUpdate();
+ } else {
+ mRecentsOverviewPredictor.requestPredictionUpdate();
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ @UiThread
+ public void onReturnedToHome() {
+ String client = Client.HOME.id;
+ mMessageHandler.removeMessages(MSG_PREDICT, client);
+ Message.obtain(mMessageHandler, MSG_PREDICT, client).sendToTarget();
+ if (DBG) {
+ Log.d(TAG, String.format("Sent immediate message to update %s", client));
+ }
+ }
+
+ @UiThread
+ public void setEnabled(boolean isEnabled) {
+ mEnabled = isEnabled;
+ if (isEnabled) {
+ mMessageHandler.removeMessages(MSG_DESTROY);
+ mMessageHandler.sendEmptyMessage(MSG_INIT);
+ } else {
+ mMessageHandler.removeMessages(MSG_INIT);
+ mMessageHandler.sendEmptyMessage(MSG_DESTROY);
+ }
+ }
+
+ @Override
+ @UiThread
+ public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
+ String container) {
+ // TODO: Use the full shortcut info
+ AppTarget target = new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutId))
+ .setTarget(packageName, user)
+ .setClassName(shortcutId)
+ .build();
+ sendLaunch(target, container);
+ }
+
+ @Override
+ @UiThread
+ public void onStartApp(ComponentName cn, UserHandle user, String container) {
+ if (cn != null) {
+ AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn))
+ .setTarget(cn.getPackageName(), user)
+ .setClassName(cn.getClassName())
+ .build();
+ sendLaunch(target, container);
+ }
+ }
+
+ @UiThread
+ private void sendLaunch(AppTarget target, String container) {
+ AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH)
+ .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
+ .build();
+ Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
new file mode 100644
index 0000000..55f4c98
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -0,0 +1,411 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.IntProperty;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.allapps.FloatingHeaderRow;
+import com.android.launcher3.allapps.FloatingHeaderView;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.keyboard.FocusIndicatorHelper;
+import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
+import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
+import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
+import com.android.quickstep.AnimatedFloat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+@TargetApi(Build.VERSION_CODES.P)
+public class PredictionRowView extends LinearLayout implements
+ LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow {
+
+ private static final String TAG = "PredictionRowView";
+
+ private static final IntProperty<PredictionRowView> TEXT_ALPHA =
+ new IntProperty<PredictionRowView>("textAlpha") {
+ @Override
+ public void setValue(PredictionRowView view, int alpha) {
+ view.setTextAlpha(alpha);
+ }
+
+ @Override
+ public Integer get(PredictionRowView view) {
+ return view.mIconCurrentTextAlpha;
+ }
+ };
+
+ private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
+ (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
+
+ private static final OnClickListener PREDICTION_CLICK_LISTENER =
+ ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);
+
+ private final Launcher mLauncher;
+ private final PredictionUiStateManager mPredictionUiStateManager;
+ private final int mNumPredictedAppsPerRow;
+
+ // The set of predicted app component names
+ private final List<ComponentKeyMapper> mPredictedAppComponents = new ArrayList<>();
+ // The set of predicted apps resolved from the component names and the current set of apps
+ private final ArrayList<ItemInfoWithIcon> mPredictedApps = new ArrayList<>();
+ // Helper to drawing the focus indicator.
+ private final FocusIndicatorHelper mFocusHelper;
+
+ private final int mIconTextColor;
+ private final int mIconFullTextAlpha;
+ private int mIconCurrentTextAlpha;
+
+ private FloatingHeaderView mParent;
+ private boolean mScrolledOut;
+
+ private float mScrollTranslation = 0;
+ private final AnimatedFloat mContentAlphaFactor =
+ new AnimatedFloat(this::updateTranslationAndAlpha);
+ private final AnimatedFloat mOverviewScrollFactor =
+ new AnimatedFloat(this::updateTranslationAndAlpha);
+
+ private View mLoadingProgress;
+
+ private boolean mPredictionsEnabled = false;
+
+ public PredictionRowView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ setOrientation(LinearLayout.HORIZONTAL);
+
+ mFocusHelper = new SimpleFocusIndicatorHelper(this);
+
+ mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numColumns;
+ mLauncher = Launcher.getLauncher(context);
+ mLauncher.addOnDeviceProfileChangeListener(this);
+
+ mPredictionUiStateManager = PredictionUiStateManager.INSTANCE.get(context);
+
+ mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary);
+ mIconFullTextAlpha = Color.alpha(mIconTextColor);
+ mIconCurrentTextAlpha = mIconFullTextAlpha;
+
+ updateVisibility();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ mPredictionUiStateManager.setTargetAppsView(mLauncher.getAppsView());
+ getAppsStore().registerIconContainer(this);
+ AllAppsTipView.scheduleShowIfNeeded(mLauncher);
+ }
+
+ private AllAppsStore getAppsStore() {
+ return mLauncher.getAppsView().getAppsStore();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mPredictionUiStateManager.setTargetAppsView(null);
+ getAppsStore().unregisterIconContainer(this);
+ }
+
+ public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
+ mParent = parent;
+ setPredictionsEnabled(mPredictionUiStateManager.arePredictionsEnabled());
+ }
+
+ private void setPredictionsEnabled(boolean predictionsEnabled) {
+ if (predictionsEnabled != mPredictionsEnabled) {
+ mPredictionsEnabled = predictionsEnabled;
+ updateVisibility();
+ }
+ }
+
+ private void updateVisibility() {
+ setVisibility(mPredictionsEnabled ? VISIBLE : GONE);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(getExpectedHeight(),
+ MeasureSpec.EXACTLY));
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ mFocusHelper.draw(canvas);
+ super.dispatchDraw(canvas);
+ }
+
+ @Override
+ public int getExpectedHeight() {
+ return getVisibility() == GONE ? 0 :
+ Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx
+ + getPaddingTop() + getPaddingBottom();
+ }
+
+ @Override
+ public boolean shouldDraw() {
+ return getVisibility() != GONE;
+ }
+
+ @Override
+ public boolean hasVisibleContent() {
+ return mPredictionUiStateManager.arePredictionsEnabled();
+ }
+
+ /**
+ * Returns the predicted apps.
+ */
+ public List<ItemInfoWithIcon> getPredictedApps() {
+ return mPredictedApps;
+ }
+
+ /**
+ * Sets the current set of predicted apps.
+ *
+ * This can be called before we get the full set of applications, we should merge the results
+ * only in onPredictionsUpdated() which is idempotent.
+ *
+ * If the number of predicted apps is the same as the previous list of predicted apps,
+ * we can optimize by swapping them in place.
+ */
+ public void setPredictedApps(boolean predictionsEnabled, List<ComponentKeyMapper> apps) {
+ setPredictionsEnabled(predictionsEnabled);
+ mPredictedAppComponents.clear();
+ mPredictedAppComponents.addAll(apps);
+
+ mPredictedApps.clear();
+ mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
+ applyPredictionApps();
+ }
+
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ removeAllViews();
+ applyPredictionApps();
+ }
+
+ private void applyPredictionApps() {
+ if (mLoadingProgress != null) {
+ removeView(mLoadingProgress);
+ }
+ if (!mPredictionsEnabled) {
+ mParent.onHeightUpdated();
+ return;
+ }
+
+ if (getChildCount() != mNumPredictedAppsPerRow) {
+ while (getChildCount() > mNumPredictedAppsPerRow) {
+ removeViewAt(0);
+ }
+ while (getChildCount() < mNumPredictedAppsPerRow) {
+ BubbleTextView icon = (BubbleTextView) mLauncher.getLayoutInflater().inflate(
+ R.layout.all_apps_icon, this, false);
+ icon.setOnClickListener(PREDICTION_CLICK_LISTENER);
+ icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
+ icon.setLongPressTimeoutFactor(1f);
+ icon.setOnFocusChangeListener(mFocusHelper);
+
+ LayoutParams lp = (LayoutParams) icon.getLayoutParams();
+ // Ensure the all apps icon height matches the workspace icons in portrait mode.
+ lp.height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+ lp.width = 0;
+ lp.weight = 1;
+ addView(icon);
+ }
+ }
+
+ int predictionCount = mPredictedApps.size();
+ int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
+
+ for (int i = 0; i < getChildCount(); i++) {
+ BubbleTextView icon = (BubbleTextView) getChildAt(i);
+ icon.reset();
+ if (predictionCount > i) {
+ icon.setVisibility(View.VISIBLE);
+ if (mPredictedApps.get(i) instanceof AppInfo) {
+ icon.applyFromApplicationInfo((AppInfo) mPredictedApps.get(i));
+ } else if (mPredictedApps.get(i) instanceof WorkspaceItemInfo) {
+ icon.applyFromWorkspaceItem((WorkspaceItemInfo) mPredictedApps.get(i));
+ }
+ icon.setTextColor(iconColor);
+ } else {
+ icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
+ }
+ }
+
+ if (predictionCount == 0) {
+ if (mLoadingProgress == null) {
+ mLoadingProgress = LayoutInflater.from(getContext())
+ .inflate(R.layout.prediction_load_progress, this, false);
+ }
+ addView(mLoadingProgress);
+ } else {
+ mLoadingProgress = null;
+ }
+
+ mParent.onHeightUpdated();
+ }
+
+ private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
+ if (getAppsStore().getApps().isEmpty()) {
+ // Apps have not been bound yet.
+ return Collections.emptyList();
+ }
+
+ List<ItemInfoWithIcon> predictedApps = new ArrayList<>();
+ for (ComponentKeyMapper mapper : components) {
+ ItemInfoWithIcon info = mapper.getApp(getAppsStore());
+ if (info != null) {
+ predictedApps.add(info);
+ } else {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ Log.e(TAG, "Predicted app not found: " + mapper);
+ }
+ }
+ // Stop at the number of predicted apps
+ if (predictedApps.size() == mNumPredictedAppsPerRow) {
+ break;
+ }
+ }
+ return predictedApps;
+ }
+
+ @Override
+ public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+ LauncherLogProto.Target targetParent) {
+ for (int i = 0; i < mPredictedApps.size(); i++) {
+ ItemInfoWithIcon appInfo = mPredictedApps.get(i);
+ if (appInfo == info) {
+ targetParent.containerType = LauncherLogProto.ContainerType.PREDICTION;
+ target.predictedRank = i;
+ break;
+ }
+ }
+ }
+
+ public void setTextAlpha(int alpha) {
+ mIconCurrentTextAlpha = alpha;
+ int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
+
+ if (mLoadingProgress == null) {
+ for (int i = 0; i < getChildCount(); i++) {
+ ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
+ }
+ }
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+
+ @Override
+ public void setVerticalScroll(int scroll, boolean isScrolledOut) {
+ mScrolledOut = isScrolledOut;
+ updateTranslationAndAlpha();
+ if (!isScrolledOut) {
+ mScrollTranslation = scroll;
+ updateTranslationAndAlpha();
+ }
+ }
+
+ private void updateTranslationAndAlpha() {
+ if (mPredictionsEnabled) {
+ setTranslationY((1 - mOverviewScrollFactor.value) * mScrollTranslation);
+
+ float factor = ALPHA_FACTOR_INTERPOLATOR.getInterpolation(mOverviewScrollFactor.value);
+ float endAlpha = factor + (1 - factor) * (mScrolledOut ? 0 : 1);
+ setAlpha(mContentAlphaFactor.value * endAlpha);
+ AlphaUpdateListener.updateVisibility(this);
+ }
+ }
+
+ @Override
+ public void setContentVisibility(boolean hasHeaderExtra, boolean hasContent,
+ PropertySetter setter, Interpolator fadeInterpolator) {
+ boolean isDrawn = getAlpha() > 0;
+ int textAlpha = hasHeaderExtra
+ ? (hasContent ? mIconFullTextAlpha : 0) // Text follows the content visibility
+ : mIconCurrentTextAlpha; // Leave as before
+ if (!isDrawn) {
+ // If the header is not drawn, no need to animate the text alpha
+ setTextAlpha(textAlpha);
+ } else {
+ setter.setInt(this, TEXT_ALPHA, textAlpha, fadeInterpolator);
+ }
+
+ setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
+ (hasHeaderExtra && !hasContent) ? 1 : 0, LINEAR);
+ setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
+ fadeInterpolator);
+ }
+
+ @Override
+ public void setInsets(Rect insets, DeviceProfile grid) {
+ int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+ + grid.cellLayoutPaddingLeftRightPx;
+ setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
+ }
+
+ @Override
+ public Class<PredictionRowView> getTypeClass() {
+ return PredictionRowView.class;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
new file mode 100644
index 0000000..54fd845
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -0,0 +1,330 @@
+/**
+ * Copyright (C) 2019 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.appprediction;
+
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.MainThreadInitializedObject;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Handler responsible to updating the UI due to predicted apps changes. Operations:
+ * 1) Pushes the predicted apps to all-apps. If all-apps is visible, waits until it becomes
+ * invisible again before applying the changes. This ensures that the UI does not change abruptly
+ * in front of the user, even if an app launched and user pressed back button to return to the
+ * all-apps UI again.
+ * 2) Prefetch high-res icons for predicted apps. This ensures that we have the icons in memory
+ * even if all-apps is not opened as they are shown in search UI as well
+ * 3) Load instant app if it is not already in memory. As predictions are persisted on disk,
+ * instant app will not be in memory when launcher starts.
+ * 4) Maintains the current active client id (for the predictions) and all updates are performed on
+ * that client id.
+ */
+public class PredictionUiStateManager implements OnGlobalLayoutListener, ItemInfoUpdateReceiver,
+ OnSharedPreferenceChangeListener, OnIDPChangeListener, OnUpdateListener {
+
+ public static final String KEY_APP_SUGGESTION = "pref_show_predictions";
+
+ // TODO (b/129421797): Update the client constants
+ public enum Client {
+ HOME("GEL"),
+ OVERVIEW("OVERVIEW_GEL");
+
+ public final String id;
+
+ Client(String id) {
+ this.id = id;
+ }
+ }
+
+ public static final MainThreadInitializedObject<PredictionUiStateManager> INSTANCE =
+ new MainThreadInitializedObject<>(PredictionUiStateManager::new);
+
+ private final Context mContext;
+ private final SharedPreferences mMainPrefs;
+
+ private final DynamicItemCache mDynamicItemCache;
+ private final List[] mPredictionServicePredictions;
+
+ private int mMaxIconsPerRow;
+ private Client mActiveClient;
+
+ private AllAppsContainerView mAppsView;
+
+ private PredictionState mPendingState;
+ private PredictionState mCurrentState;
+
+ private PredictionUiStateManager(Context context) {
+ mContext = context;
+ mMainPrefs = Utilities.getPrefs(context);
+
+ mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
+
+ mActiveClient = Client.HOME;
+
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ mMaxIconsPerRow = idp.numColumns;
+
+ idp.addOnChangeListener(this);
+ mPredictionServicePredictions = new List[Client.values().length];
+ for (int i = 0; i < mPredictionServicePredictions.length; i++) {
+ mPredictionServicePredictions[i] = Collections.emptyList();
+ }
+ // Listens for enable/disable signal, and predictions if using AiAi is disabled.
+ mMainPrefs.registerOnSharedPreferenceChangeListener(this);
+ // Call this last
+ mCurrentState = parseLastState();
+ }
+
+ @Override
+ public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+ mMaxIconsPerRow = profile.numColumns;
+ }
+
+ public Client getClient() {
+ return mActiveClient;
+ }
+
+ public void switchClient(Client client) {
+ if (client == mActiveClient) {
+ return;
+ }
+ mActiveClient = client;
+ dispatchOnChange(true);
+ }
+
+ public void setTargetAppsView(AllAppsContainerView appsView) {
+ if (mAppsView != null) {
+ mAppsView.getAppsStore().removeUpdateListener(this);
+ }
+ mAppsView = appsView;
+ if (mAppsView != null) {
+ mAppsView.getAppsStore().addUpdateListener(this);
+ }
+ if (mPendingState != null) {
+ applyState(mPendingState);
+ mPendingState = null;
+ } else {
+ applyState(mCurrentState);
+ }
+ updateDependencies(mCurrentState);
+ }
+
+ @Override
+ public void reapplyItemInfo(ItemInfoWithIcon info) { }
+
+ @Override
+ public void onGlobalLayout() {
+ if (mAppsView == null) {
+ return;
+ }
+ if (mPendingState != null && canApplyPredictions(mPendingState)) {
+ applyState(mPendingState);
+ mPendingState = null;
+ }
+ if (mPendingState == null) {
+ mAppsView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ }
+ }
+
+ private void scheduleApplyPredictedApps(PredictionState state) {
+ boolean registerListener = mPendingState == null;
+ mPendingState = state;
+ if (registerListener) {
+ // OnGlobalLayoutListener is called whenever a view in the view tree changes
+ // visibility. Add a listener and wait until appsView is invisible again.
+ mAppsView.getViewTreeObserver().addOnGlobalLayoutListener(this);
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (KEY_APP_SUGGESTION.equals(key)) {
+ dispatchOnChange(true);
+ }
+ }
+
+ private void applyState(PredictionState state) {
+ boolean wasEnabled = mCurrentState.isEnabled;
+ mCurrentState = state;
+ if (mAppsView != null) {
+ mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
+ .setPredictedApps(mCurrentState.isEnabled, mCurrentState.apps);
+
+ if (wasEnabled != mCurrentState.isEnabled) {
+ // Reapply state as the State UI might have changed.
+ Launcher.getLauncher(mAppsView.getContext()).getStateManager().reapplyState(true);
+ }
+ }
+ }
+
+ public AppPredictor.Callback appPredictorCallback(Client client) {
+ return targets -> {
+ mPredictionServicePredictions[client.ordinal()] = targets;
+ dispatchOnChange(true);
+ };
+ }
+
+ private void dispatchOnChange(boolean changed) {
+ PredictionState newState = changed ? parseLastState() :
+ (mPendingState == null ? mCurrentState : mPendingState);
+ if (changed && mAppsView != null && !canApplyPredictions(newState)) {
+ scheduleApplyPredictedApps(newState);
+ } else {
+ applyState(newState);
+ }
+ }
+
+ private PredictionState parseLastState() {
+ PredictionState state = new PredictionState();
+ state.isEnabled = mMainPrefs.getBoolean(KEY_APP_SUGGESTION, true);
+ if (!state.isEnabled) {
+ state.apps = Collections.EMPTY_LIST;
+ return state;
+ }
+
+ state.apps = new ArrayList<>();
+
+ List<AppTarget> appTargets = mPredictionServicePredictions[mActiveClient.ordinal()];
+ if (!appTargets.isEmpty()) {
+ for (AppTarget appTarget : appTargets) {
+ ComponentKey key;
+ if (appTarget.getShortcutInfo() != null) {
+ key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
+ } else {
+ key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
+ appTarget.getClassName()), appTarget.getUser());
+ }
+ state.apps.add(new ComponentKeyMapper(mContext, key, mDynamicItemCache));
+ }
+ }
+ updateDependencies(state);
+ return state;
+ }
+
+ private void updateDependencies(PredictionState state) {
+ if (!state.isEnabled || mAppsView == null) {
+ return;
+ }
+
+ IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
+ List<String> instantAppsToLoad = new ArrayList<>();
+ List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
+ int total = state.apps.size();
+ for (int i = 0, count = 0; i < total && count < mMaxIconsPerRow; i++) {
+ ComponentKeyMapper mapper = state.apps.get(i);
+ // Update instant apps
+ if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
+ instantAppsToLoad.add(mapper.getPackage());
+ count++;
+ } else if (mapper.getComponentKey() instanceof ShortcutKey) {
+ shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
+ count++;
+ } else {
+ // Reload high res icon
+ AppInfo info = (AppInfo) mapper.getApp(mAppsView.getAppsStore());
+ if (info != null) {
+ if (info.usingLowResIcon()) {
+ // TODO: Update icon cache to support null callbacks.
+ iconCache.updateIconInBackground(this, info);
+ }
+ count++;
+ }
+ }
+ }
+ mDynamicItemCache.cacheItems(shortcutsToLoad, instantAppsToLoad);
+ }
+
+ @Override
+ public void onAppsUpdated() {
+ dispatchOnChange(false);
+ }
+
+ public boolean arePredictionsEnabled() {
+ return mCurrentState.isEnabled;
+ }
+
+ private boolean canApplyPredictions(PredictionState newState) {
+ if (mAppsView == null) {
+ // If there is no apps view, no need to schedule.
+ return true;
+ }
+ Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
+ PredictionRowView predictionRow = mAppsView.getFloatingHeaderView().
+ findFixedRowByType(PredictionRowView.class);
+ if (!predictionRow.isShown() || predictionRow.getAlpha() == 0 ||
+ launcher.isForceInvisible()) {
+ return true;
+ }
+
+ if (mCurrentState.isEnabled != newState.isEnabled
+ || mCurrentState.apps.isEmpty() != newState.apps.isEmpty()) {
+ // If the visibility of the prediction row is changing, apply immediately.
+ return true;
+ }
+
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ // If we are here & mAppsView.isShown() = true, we are probably in all-apps or mid way
+ return false;
+ }
+ if (!launcher.isInState(OVERVIEW) && !launcher.isInState(BACKGROUND_APP)) {
+ // Just a fallback as we dont need to apply instantly, if we are not in the swipe-up UI
+ return false;
+ }
+
+ // Instead of checking against 1, we should check against (1 + delta), where delta accounts
+ // for the nav-bar height (as app icon can still be visible under the nav-bar). Checking
+ // against 1, keeps the logic simple :)
+ return launcher.getAllAppsController().getProgress() > 1;
+ }
+
+ public PredictionState getCurrentState() {
+ return mCurrentState;
+ }
+
+ public static class PredictionState {
+
+ public boolean isEnabled;
+ public List<ComponentKeyMapper> apps;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index e865137..abd7c03 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
import android.animation.ValueAnimator;
+import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
@@ -35,10 +36,12 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
+import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
+
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.quickstep.util.MotionPauseDetector;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.launcher3.R;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -61,6 +64,9 @@
private static final int STATE_ASSISTANT_ACTIVE = 1;
private static final int STATE_DELEGATE_ACTIVE = 2;
+ private static final String INVOCATION_TYPE_KEY = "invocation_type";
+ private static final int INVOCATION_TYPE_GESTURE = 1;
+
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private final PointF mStartDragPos = new PointF();
@@ -74,12 +80,12 @@
private float mLastProgress;
private int mState;
private int mDirection;
+ private ActivityControlHelper mActivityControlHelper;
private final float mDistThreshold;
private final long mTimeThreshold;
private final int mAngleThreshold;
private final float mSlop;
- private final MotionPauseDetector mMotionPauseDetector;
private final ISystemUiProxy mSysUiProxy;
private final InputConsumer mConsumerDelegate;
private final Context mContext;
@@ -88,17 +94,18 @@
public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
- InputConsumer delegate, InputMonitorCompat inputMonitorCompat) {
+ InputConsumer delegate, InputMonitorCompat inputMonitorCompat,
+ ActivityControlHelper activityControlHelper) {
final Resources res = context.getResources();
mContext = context;
mSysUiProxy = systemUiProxy;
mConsumerDelegate = delegate;
- mMotionPauseDetector = new MotionPauseDetector(context);
mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
- mSlop = NavigationBarCompat.getQuickScrubTouchSlopPx();
+ mSlop = NavigationBarCompat.getQuickStepDragSlopPx();
mInputMonitorCompat = inputMonitorCompat;
+ mActivityControlHelper = activityControlHelper;
mState = STATE_INACTIVE;
}
@@ -108,8 +115,18 @@
}
@Override
- public boolean isActive() {
- return mState != STATE_INACTIVE;
+ public boolean useSharedSwipeState() {
+ if (mConsumerDelegate != null) {
+ return mConsumerDelegate.useSharedSwipeState();
+ }
+ return false;
+ }
+
+ @Override
+ public void onConsumerAboutToBeSwitched() {
+ if (mConsumerDelegate != null) {
+ mConsumerDelegate.onConsumerAboutToBeSwitched();
+ }
}
@Override
@@ -122,14 +139,6 @@
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
mTimeFraction = 0;
-
- // Detect when the gesture decelerates to start the assistant
- mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
- if (isPaused && mState == STATE_ASSISTANT_ACTIVE) {
- mTimeFraction = 1;
- updateAssistantProgress();
- }
- });
break;
}
case ACTION_POINTER_UP: {
@@ -172,7 +181,7 @@
mDirection = angle > 90 ? UPLEFT : UPRIGHT;
angle = angle > 90 ? 180 - angle : angle;
- if (angle > mAngleThreshold && angle < 90 - mAngleThreshold) {
+ if (angle > mAngleThreshold && angle < 90) {
mState = STATE_ASSISTANT_ACTIVE;
if (mConsumerDelegate != null) {
@@ -190,7 +199,6 @@
// Movement
mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
mLastPos.y - mStartDragPos.y);
- mMotionPauseDetector.addPosition(mDistance, 0, ev.getEventTime());
if (mDistance >= 0) {
final long diff = SystemClock.uptimeMillis() - mDragTime;
mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1);
@@ -219,8 +227,8 @@
animator.setInterpolator(Interpolators.DEACCEL_2);
animator.start();
}
+ mPassedSlop = false;
mState = STATE_INACTIVE;
- mMotionPauseDetector.clear();
break;
}
@@ -238,7 +246,17 @@
if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
UserEventDispatcher.newInstance(mContext).logActionOnContainer(
SWIPE, mDirection, NAVBAR);
- mSysUiProxy.startAssistant(new Bundle());
+ Bundle args = new Bundle();
+ args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+
+ BaseDraggingActivity launcherActivity = mActivityControlHelper.getCreatedActivity();
+ if (launcherActivity != null) {
+ launcherActivity.getRootView().
+ performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+
+ mSysUiProxy.startAssistant(args);
mLaunchedAssistant = true;
}
} catch (RemoteException e) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
index ad9fe78..e3f9e02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -34,7 +34,7 @@
int getType();
- default boolean isActive() {
+ default boolean useSharedSwipeState() {
return false;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 7c71846..35783b5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -45,7 +45,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherInitListenerEx;
import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -97,7 +97,7 @@
public void onAssistantVisibilityChanged(float visibility) {
Launcher launcher = getCreatedActivity();
if (launcher != null) {
- launcher.setQuickSearchBarAlpha(1f - visibility);
+ launcher.onAssistantVisibilityChanged(visibility);
}
}
@@ -304,7 +304,7 @@
@Override
public ActivityInitListener createActivityInitListener(
BiPredicate<Launcher, Boolean> onInitListener) {
- return new LauncherInitListener(onInitListener);
+ return new LauncherInitListenerEx(onInitListener);
}
@Nullable
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 9c8e80e..03af92d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -413,7 +413,7 @@
}
@Override
- public boolean isActive() {
+ public boolean useSharedSwipeState() {
return mInteractionHandler != null;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index d979c99..84af109 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -36,6 +36,8 @@
*/
public class TaskOverlayFactory implements ResourceBasedOverride {
+ public static final String AIAI_PACKAGE = "com.google.android.as";
+
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[]{
new TaskSystemShortcut.AppInfo(),
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 42647da..fc3f332 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -422,7 +422,7 @@
if (event.getAction() == ACTION_DOWN) {
if (isInValidSystemUiState()
&& mSwipeTouchRegion.contains(event.getX(), event.getY())) {
- boolean useSharedState = mConsumer.isActive();
+ boolean useSharedState = mConsumer.useSharedSwipeState();
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(useSharedState, event);
TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType());
@@ -455,13 +455,18 @@
final ActivityControlHelper activityControl =
mOverviewComponentObserver.getActivityControlHelper();
+
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
return InputConsumer.NO_OP;
} else if (mAssistantAvailable
&& SysUINavigationMode.INSTANCE.get(this).getMode() == Mode.NO_BUTTON
&& AssistantTouchConsumer.withinTouchRegion(this, event)) {
- return new AssistantTouchConsumer(this, mISystemUiProxy, !activityControl.isResumed()
- ? createOtherActivityInputConsumer(event, runningTaskInfo) : null, mInputMonitorCompat);
+
+ boolean addDelegate = !activityControl.isResumed();
+ return new AssistantTouchConsumer(this, mISystemUiProxy, addDelegate ?
+ createOtherActivityInputConsumer(event, runningTaskInfo) : null,
+ mInputMonitorCompat, activityControl);
+
} else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
return OverviewInputConsumer.newInstance(activityControl, false);
} else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index efe16c5..c31e829 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import android.animation.AnimatorSet;
@@ -39,7 +38,8 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.ClipAnimationHelper;
@@ -195,4 +195,13 @@
}
}
}
+
+ @Override
+ public void reset() {
+ super.reset();
+
+ // We are moving to home or some other UI with no recents. Switch back to the home client,
+ // the home predictions should have been updated when the activity was resumed.
+ PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index d8aeb35..72d60da 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -82,6 +82,8 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -751,8 +753,6 @@
unloadVisibleTaskData();
setCurrentPage(0);
-
- OverviewCallbacks.get(getContext()).onResetOverview();
}
/**
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index e29d3df..95aea43 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -29,5 +29,5 @@
<!-- Assistant Gesture -->
<integer name="assistant_gesture_min_time_threshold">200</integer>
- <integer name="assistant_gesture_corner_deg_threshold">10</integer>
+ <integer name="assistant_gesture_corner_deg_threshold">20</integer>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 75959d1..13a0435 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -66,7 +66,7 @@
<!-- Assistant Gestures -->
<dimen name="gestures_assistant_size">48dp</dimen>
- <dimen name="gestures_assistant_drag_threshold">70dp</dimen>
+ <dimen name="gestures_assistant_drag_threshold">55dp</dimen>
<!-- Distance to move elements when swiping up to go home from launcher -->
<dimen name="home_pullback_distance">28dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 81565a5..4319b5d 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -58,4 +58,13 @@
<!-- Annotation shown on an app card in Recents, telling that the app has a usage limit set by
the user, and a given time is left for it today [CHAR LIMIT=22] -->
<string name="time_left_for_app"><xliff:g id="time" example="7 minutes">%1$s</xliff:g> left today</string>
+
+ <!-- Accessibility title for the row of all-apps containing app predictions. [CHAR LIMIT=50] -->
+ <string name="title_app_suggestions">App suggestions</string>
+ <!-- Label for the header text of the All Apps section in All Apps view, used to separate Predicted Apps and Actions section from All Apps section. [CHAR_LIMIT=50] -->
+ <string name="all_apps_label">All apps</string>
+ <!-- Text of the tip when user lands in all apps view for the first time, indicating where the tip toast points to is the predicted apps section. [CHAR_LIMIT=50] -->
+ <string name="all_apps_prediction_tip">Your predicted apps</string>
+
+
</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index b263a4c..4f50cdb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -87,7 +87,7 @@
OverviewInteractionState.INSTANCE.get(launcher)
.setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
if (launcher != null && launcher.getDragLayer() != null) {
- launcher.getDragLayer().setDisallowBackGesture(shouldBackButtonBeHidden);
+ launcher.getRootView().setDisallowBackGesture(shouldBackButtonBeHidden);
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCallbacks.java b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
index ef9c5c0..f5573ba 100644
--- a/quickstep/src/com/android/quickstep/OverviewCallbacks.java
+++ b/quickstep/src/com/android/quickstep/OverviewCallbacks.java
@@ -39,7 +39,5 @@
public void onInitOverviewTransition() { }
- public void onResetOverview() { }
-
public void closeAllWindows() { }
}
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index 6dff187..ca7711f 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -36,6 +36,8 @@
@SuppressWarnings("unused")
public class UserEventDispatcherExtension extends UserEventDispatcher {
+ public static final int ALL_APPS_PREDICTION_TIPS = 2;
+
private static final String TAG = "UserEventDispatcher";
public UserEventDispatcherExtension(Context context) { }
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
new file mode 100644
index 0000000..43f6039
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -0,0 +1,168 @@
+/**
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.quickstep;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.pm.LauncherActivityInfo;
+import android.os.Process;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.appprediction.PredictionRowView;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
+import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.model.AppLaunchTracker;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class AppPredictionsUITests extends AbstractQuickStepTest {
+ private static final int DEFAULT_APP_LAUNCH_TIMES = 3;
+ private static final String TAG = "AppPredictionsUITests";
+
+ private LauncherActivityInfo mSampleApp1;
+ private LauncherActivityInfo mSampleApp2;
+ private LauncherActivityInfo mSampleApp3;
+
+ private AppPredictor.Callback mCallback;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(mTargetContext)
+ .getActivityList(null, Process.myUserHandle());
+ mSampleApp1 = activities.get(0);
+ mSampleApp2 = activities.get(1);
+ mSampleApp3 = activities.get(2);
+
+ // Disable app tracker
+ AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
+
+ mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback(
+ Client.HOME);
+
+ mDevice.setOrientationNatural();
+ }
+
+ @After
+ public void tearDown() throws Throwable {
+ mDevice.unfreezeRotation();
+ }
+
+ /**
+ * Test that prediction UI is updated as soon as we get predictions from the system
+ */
+ @Test
+ public void testPredictionExistsInAllApps() {
+ mActivityMonitor.startLauncher();
+ mLauncher.pressHome().switchToAllApps();
+
+ // There has not been any update, verify that progress bar is showing
+ waitForLauncherCondition("Prediction is not in loading state", launcher -> {
+ ProgressBar p = findLoadingBar(launcher);
+ return p != null && p.isShown();
+ });
+
+ // Dispatch an update
+ sendPredictionUpdate(mSampleApp1, mSampleApp2);
+ waitForLauncherCondition("Predictions were not updated in loading state",
+ launcher -> getPredictedApp(launcher).size() == 2);
+ }
+
+ /**
+ * Test tat prediction update is deferred if it is already visible
+ */
+ @Test
+ public void testPredictionsDeferredUntilHome() {
+ mActivityMonitor.startLauncher();
+ sendPredictionUpdate(mSampleApp1, mSampleApp2);
+ mLauncher.pressHome().switchToAllApps();
+ waitForLauncherCondition("Predictions were not updated in loading state",
+ launcher -> getPredictedApp(launcher).size() == 2);
+
+ // Update predictions while all-apps is visible
+ sendPredictionUpdate(mSampleApp1, mSampleApp2, mSampleApp3);
+ assertEquals(2, getFromLauncher(this::getPredictedApp).size());
+
+ // Go home and go back to all-apps
+ mLauncher.pressHome().switchToAllApps();
+ assertEquals(3, getFromLauncher(this::getPredictedApp).size());
+ }
+
+ public ArrayList<BubbleTextView> getPredictedApp(Launcher launcher) {
+ PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(PredictionRowView.class);
+
+ ArrayList<BubbleTextView> predictedAppViews = new ArrayList<>();
+ for (int i = 0; i < container.getChildCount(); i++) {
+ View view = container.getChildAt(i);
+ if (view instanceof BubbleTextView && view.getVisibility() == View.VISIBLE) {
+ predictedAppViews.add((BubbleTextView) view);
+ }
+ }
+ return predictedAppViews;
+ }
+
+ private ProgressBar findLoadingBar(Launcher launcher) {
+ PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
+ .findFixedRowByType(PredictionRowView.class);
+
+ for (int i = 0; i < container.getChildCount(); i++) {
+ View view = container.getChildAt(i);
+ if (view instanceof ProgressBar) {
+ return (ProgressBar) view;
+ }
+ }
+ return null;
+ }
+
+
+ private void sendPredictionUpdate(LauncherActivityInfo... activities) {
+ getOnUiThread(() -> {
+ List<AppTarget> targets = new ArrayList<>(activities.length);
+ for (LauncherActivityInfo info : activities) {
+ ComponentName cn = info.getComponentName();
+ AppTarget target = new AppTarget.Builder(new AppTargetId("app:" + cn))
+ .setTarget(cn.getPackageName(), info.getUser())
+ .setClassName(cn.getClassName())
+ .build();
+ targets.add(target);
+ }
+ mCallback.onTargetsAvailable(targets);
+ return null;
+ });
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index dc83e87..a47c8e7 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -61,7 +61,7 @@
}
@Test
- @Ignore // Ignoring until gestural navigation event sequence settles
+ @Ignore // Ignoring until race condition repro framework is changes for multi-process case.
@NavigationModeSwitch(mode = Mode.TWO_BUTTON)
public void testPressHome() {
runTest(enterEvt(Launcher.ON_CREATE_EVT),
@@ -76,14 +76,6 @@
}
@Test
- @Ignore // Ignoring until gestural navigation event sequence settles
- @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
- public void testSwipeToOverview() {
- closeLauncherActivity();
- mLauncher.getBackground().switchToOverview();
- }
-
- @Test
@NavigationModeSwitch
public void testStressPressHome() {
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 1709516..b0ce5f5 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -60,7 +62,7 @@
}
private void startTestApps() throws Exception {
- startAppFast(resolveSystemApp(Intent.CATEGORY_APP_BROWSER));
+ startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CONTACTS));
@@ -72,7 +74,7 @@
@Test
@PortraitLandscape
- @Ignore
+ @Ignore // Enable after b/131115533
public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
mDevice.pressRecentApps();
waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 465504c..43194d5 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -123,7 +123,7 @@
<attr name="minWidthDps" format="float" />
<attr name="minHeightDps" format="float" />
- <attr name="iconSize" format="float" />
+ <attr name="iconImageSize" format="float" />
<!-- landscapeIconSize defaults to iconSize, if not specified -->
<attr name="landscapeIconSize" format="float" />
<attr name="iconTextSize" format="float" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 4b68b50..83aea8b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -76,7 +76,7 @@
<string name="wallpaper_picker_package" translatable="false"></string>
<!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
- <string name="shortcutinfocompat_badgepkg_whitelist" translatable="false"></string>
+ <string name="shortcutinfo_badgepkg_whitelist" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 88b1bc9..82547d5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -30,7 +30,7 @@
launcher:name="Super Short Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="300"
- launcher:iconSize="48"
+ launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -38,7 +38,7 @@
launcher:name="Shorter Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="400"
- launcher:iconSize="48"
+ launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -57,7 +57,7 @@
launcher:name="Short Stubby"
launcher:minWidthDps="275"
launcher:minHeightDps="420"
- launcher:iconSize="48"
+ launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -65,7 +65,7 @@
launcher:name="Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="450"
- launcher:iconSize="48"
+ launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -73,7 +73,7 @@
launcher:name="Nexus S"
launcher:minWidthDps="296"
launcher:minHeightDps="491.33"
- launcher:iconSize="48"
+ launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -81,7 +81,7 @@
launcher:name="Nexus 4"
launcher:minWidthDps="359"
launcher:minHeightDps="567"
- launcher:iconSize="54"
+ launcher:iconImageSize="54"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -89,7 +89,7 @@
launcher:name="Nexus 5"
launcher:minWidthDps="335"
launcher:minHeightDps="567"
- launcher:iconSize="54"
+ launcher:iconImageSize="54"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
@@ -108,7 +108,7 @@
launcher:name="Large Phone"
launcher:minWidthDps="406"
launcher:minHeightDps="694"
- launcher:iconSize="56"
+ launcher:iconImageSize="56"
launcher:iconTextSize="14.4"
launcher:canBeDefault="true" />
@@ -116,7 +116,7 @@
launcher:name="Shorter Stubby"
launcher:minWidthDps="255"
launcher:minHeightDps="400"
- launcher:iconSize="48"
+ launcher:iconImageSize="48"
launcher:iconTextSize="13.0"
launcher:canBeDefault="true" />
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
index ae81ff2..1351348 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
@@ -22,7 +22,6 @@
public final FlagOverrideRule flags = new FlagOverrideRule();
@FlagOverride(key = "EXAMPLE_FLAG", value = true)
- @FlagOverride(key = "QUICK_SWITCH", value = false)
@Test
public void withFlagOn() {
assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index e046cad..42848f4 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -41,7 +41,7 @@
}
@Test
- @Ignore("This test fails with resource errors")
+ @Ignore("This test fails with resource errors") // b/131115553
public void testCacheUpdate_update_apps() throws Exception {
// Clear all icons from apps list so that its easy to check what was updated
for (AppInfo info : allAppsList.data) {
@@ -66,7 +66,7 @@
}
@Test
- @Ignore("This test fails with resource errors")
+ @Ignore("This test fails with resource errors") // b/131115553
public void testSessionUpdate_ignores_normal_apps() throws Exception {
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
@@ -75,7 +75,7 @@
}
@Test
- @Ignore("This test fails with resource errors")
+ @Ignore("This test fails with resource errors") // b/131115553
public void testSessionUpdate_updates_pending_apps() throws Exception {
executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index ce07a27..53f6a06 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -10,8 +10,8 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.FlagOverrideRule;
-import com.android.launcher3.config.FlagOverrideRule.FlagOverride;
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
import com.android.launcher3.util.IntArray;
@@ -210,9 +210,11 @@
}});
}
- @FlagOverride(key = "QSB_ON_FIRST_SCREEN", value = true)
@Test
public void testWorkspace_first_row_blocked() throws Exception {
+ if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ return;
+ }
// The first screen has one item on the 4th column which needs moving, as the first row
// will be kept empty.
int[][][] ids = createGrid(new int[][][]{{
@@ -236,9 +238,11 @@
}});
}
- @FlagOverride(key = "QSB_ON_FIRST_SCREEN", value = true)
@Test
public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
+ if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ return;
+ }
// Items will get moved to the next screen to keep the first screen empty.
int[][][] ids = createGrid(new int[][][]{{
{ -1, -1, -1, -1},
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
similarity index 75%
rename from tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
rename to robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index d224c89..83bf7da 100644
--- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -22,16 +22,15 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import android.content.pm.ShortcutInfo;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Collections;
@@ -40,8 +39,7 @@
/**
* Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
*/
-@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(RobolectricTestRunner.class)
public class PopupPopulatorTest {
@Test
@@ -81,20 +79,20 @@
}
private void filterShortcutsAndAssertNumStaticAndDynamic(
- List<ShortcutInfoCompat> shortcuts, int expectedStatic, int expectedDynamic) {
+ List<ShortcutInfo> shortcuts, int expectedStatic, int expectedDynamic) {
filterShortcutsAndAssertNumStaticAndDynamic(shortcuts, expectedStatic, expectedDynamic, null);
}
- private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfoCompat> shortcuts,
+ private void filterShortcutsAndAssertNumStaticAndDynamic(List<ShortcutInfo> shortcuts,
int expectedStatic, int expectedDynamic, String shortcutIdToRemove) {
Collections.shuffle(shortcuts);
- List<ShortcutInfoCompat> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
+ List<ShortcutInfo> filteredShortcuts = PopupPopulator.sortAndFilterShortcuts(
shortcuts, shortcutIdToRemove);
assertIsSorted(filteredShortcuts);
int numStatic = 0;
int numDynamic = 0;
- for (ShortcutInfoCompat shortcut : filteredShortcuts) {
+ for (ShortcutInfo shortcut : filteredShortcuts) {
if (shortcut.isDeclaredInManifest()) {
numStatic++;
}
@@ -106,11 +104,11 @@
assertEquals(expectedDynamic, numDynamic);
}
- private void assertIsSorted(List<ShortcutInfoCompat> shortcuts) {
+ private void assertIsSorted(List<ShortcutInfo> shortcuts) {
int lastStaticRank = -1;
int lastDynamicRank = -1;
boolean hasSeenDynamic = false;
- for (ShortcutInfoCompat shortcut : shortcuts) {
+ for (ShortcutInfo shortcut : shortcuts) {
int rank = shortcut.getRank();
if (shortcut.isDeclaredInManifest()) {
assertFalse("Static shortcuts should come before all dynamic shortcuts.",
@@ -126,51 +124,24 @@
}
}
- private List<ShortcutInfoCompat> createShortcutsList(int numStatic, int numDynamic) {
- List<ShortcutInfoCompat> shortcuts = new ArrayList<>();
+ private List<ShortcutInfo> createShortcutsList(int numStatic, int numDynamic) {
+ List<ShortcutInfo> shortcuts = new ArrayList<>();
for (int i = 0; i < numStatic; i++) {
- shortcuts.add(new Shortcut(true, i));
+ shortcuts.add(createInfo(true, i));
}
for (int i = 0; i < numDynamic; i++) {
- shortcuts.add(new Shortcut(false, i));
+ shortcuts.add(createInfo(false, i));
}
return shortcuts;
}
- private class Shortcut extends ShortcutInfoCompat {
- private boolean mIsStatic;
- private int mRank;
- private String mId;
-
- public Shortcut(ShortcutInfo shortcutInfo) {
- super(shortcutInfo);
- }
-
- public Shortcut(boolean isStatic, int rank) {
- this(null);
- mIsStatic = isStatic;
- mRank = rank;
- mId = generateId(isStatic, rank);
- }
-
- @Override
- public boolean isDeclaredInManifest() {
- return mIsStatic;
- }
-
- @Override
- public boolean isDynamic() {
- return !mIsStatic;
- }
-
- @Override
- public int getRank() {
- return mRank;
- }
-
- @Override
- public String getId() {
- return mId;
- }
+ private ShortcutInfo createInfo(boolean isStatic, int rank) {
+ ShortcutInfo info = spy(new ShortcutInfo.Builder(
+ RuntimeEnvironment.application, generateId(isStatic, rank))
+ .setRank(rank)
+ .build());
+ doReturn(isStatic).when(info).isDeclaredInManifest();
+ doReturn(!isStatic).when(info).isDynamic();
+ return info;
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 418f1e4..e9b932a 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -25,6 +25,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
@@ -44,7 +45,6 @@
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions;
@@ -253,7 +253,7 @@
return (WorkspaceItemInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
}
- public static void queueShortcut(ShortcutInfoCompat info, Context context) {
+ public static void queueShortcut(ShortcutInfo info, Context context) {
queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
}
@@ -327,7 +327,7 @@
private static class PendingInstallShortcutInfo {
final LauncherActivityInfo activityInfo;
- final ShortcutInfoCompat shortcutInfo;
+ final ShortcutInfo shortcutInfo;
final AppWidgetProviderInfo providerInfo;
final Intent data;
@@ -372,7 +372,7 @@
/**
* Initializes a PendingInstallShortcutInfo to represent a launcher target.
*/
- public PendingInstallShortcutInfo(ShortcutInfoCompat info, Context context) {
+ public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
activityInfo = null;
shortcutInfo = info;
providerInfo = null;
@@ -381,7 +381,7 @@
mContext = context;
user = info.getUserHandle();
- launchIntent = info.makeIntent();
+ launchIntent = ShortcutKey.makeIntent(info);
label = info.getShortLabel().toString();
}
@@ -537,10 +537,10 @@
return info == null ? null : new PendingInstallShortcutInfo(info, context);
} else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
DeepShortcutManager sm = DeepShortcutManager.getInstance(context);
- List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+ List<ShortcutInfo> si = sm.queryForFullDetails(
decoder.launcherIntent.getPackage(),
Arrays.asList(decoder.launcherIntent.getStringExtra(
- ShortcutInfoCompat.EXTRA_SHORTCUT_ID)),
+ ShortcutKey.EXTRA_SHORTCUT_ID)),
decoder.user);
if (si.isEmpty()) {
return null;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 296afe7..3e7f67b 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -543,7 +543,7 @@
canBeDefault = a.getBoolean(
R.styleable.ProfileDisplayOption_canBeDefault, false);
- iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconSize, 0);
+ iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
iconSize);
iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0c35566..d790c04 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -24,6 +24,8 @@
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newTarget;
@@ -128,6 +130,7 @@
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.views.ScrimView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -147,6 +150,7 @@
import java.util.List;
import java.util.function.Predicate;
+import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
/**
@@ -202,6 +206,9 @@
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+ private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
+ private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
+
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
@@ -223,6 +230,9 @@
@Thunk AllAppsContainerView mAppsView;
AllAppsTransitionController mAllAppsController;
+ // Scrim view for the all apps and overview state.
+ @Thunk ScrimView mScrimView;
+
// UI and state for the overview panel
private View mOverviewPanel;
@@ -261,6 +271,8 @@
final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
+ private float mCurrentAssistantVisibility = 0f;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
RaceConditionTracker.onEvent(ON_CREATE_EVT, ENTER);
@@ -361,6 +373,24 @@
TraceHelper.endSection("Launcher-onCreate");
RaceConditionTracker.onEvent(ON_CREATE_EVT, EXIT);
+ mStateManager.addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {}
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ float alpha = 1f - mCurrentAssistantVisibility;
+ if (finalState == NORMAL) {
+ mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) {
+ mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ } else {
+ mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
+ mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
+ }
+ }
+ });
}
@Override
@@ -407,13 +437,6 @@
onIdpChanged(idp);
}
- public void setQuickSearchBarAlpha(float alpha) {
- View qsbAllApps = findViewById(R.id.search_container_all_apps);
- if (qsbAllApps != null) {
- qsbAllApps.setAlpha(alpha);
- }
- }
-
private void onIdpChanged(InvariantDeviceProfile idp) {
mUserEventDispatcher = null;
@@ -426,6 +449,18 @@
rebindModel();
}
+ public void onAssistantVisibilityChanged(float visibility) {
+ mCurrentAssistantVisibility = visibility;
+ float alpha = 1f - visibility;
+ LauncherState state = mStateManager.getState();
+ if (state == NORMAL) {
+ mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ } else if (state == OVERVIEW || state == OVERVIEW_PEEK) {
+ mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ }
+ }
+
private void initDeviceProfile(InvariantDeviceProfile idp) {
// Load configuration-specific DeviceProfile
mDeviceProfile = idp.getDeviceProfile(this);
@@ -968,6 +1003,9 @@
// Setup Apps
mAppsView = findViewById(R.id.apps_view);
+ // Setup Scrim
+ mScrimView = findViewById(R.id.scrim_view);
+
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
mDropTargetBar.setup(mDragController);
@@ -1639,7 +1677,7 @@
public int getCurrentState() {
if(mStateManager.getState() == LauncherState.ALL_APPS) {
return StatsLogUtils.LAUNCHER_STATE_ALLAPPS;
- } else if (mStateManager.getState() == LauncherState.OVERVIEW) {
+ } else if (mStateManager.getState() == OVERVIEW) {
return StatsLogUtils.LAUNCHER_STATE_OVERVIEW;
}
return StatsLogUtils.LAUNCHER_STATE_HOME;
@@ -1657,7 +1695,7 @@
LauncherState state = mStateManager.getState();
if (state == LauncherState.ALL_APPS) {
event.srcTarget[2].containerType = ContainerType.ALLAPPS;
- } else if (state == LauncherState.OVERVIEW) {
+ } else if (state == OVERVIEW) {
event.srcTarget[2].containerType = ContainerType.TASKSWITCHER;
}
}
@@ -2185,10 +2223,6 @@
*/
public void bindAllApplications(ArrayList<AppInfo> apps) {
mAppsView.getAppsStore().setApps(apps);
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.bindAllApplications(apps);
- }
}
/**
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 34bdb3c..edac516 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -57,16 +57,10 @@
* Extension points for providing custom behavior on certain user interactions.
*/
void onLauncherProviderChange();
- void bindAllApplications(ArrayList<AppInfo> apps);
/**
* Starts a search with {@param initialQuery}. Return false if search was not started.
*/
boolean startSearch(
String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
-
- /*
- * Extensions points for adding / replacing some other aspects of the Launcher experience.
- */
- boolean hasSettings();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index dfbe0fe..e788ceb 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -22,6 +22,7 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -48,7 +49,6 @@
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -213,6 +213,7 @@
synchronized (mLock) {
Preconditions.assertUIThread();
mCallbacks = new WeakReference<>(callbacks);
+ android.util.Log.d("b/131170582", "mCallbacks = " + mCallbacks);
}
}
@@ -267,12 +268,12 @@
}
@Override
- public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+ public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
UserHandle user) {
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
}
- public void updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts,
+ public void updatePinnedShortcuts(String packageName, List<ShortcutInfo> shortcuts,
UserHandle user) {
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
}
@@ -330,6 +331,7 @@
// Stop any existing loaders first, so they don't set mModelLoaded to true later
stopLoader();
mModelLoaded = false;
+ android.util.Log.d("b/131170582", "1 mModelLoaded = " + mModelLoaded);
}
// Start the loader if launcher is already running, otherwise the loader will run,
@@ -390,6 +392,7 @@
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
mLoaderTask = null;
+ android.util.Log.d("b/131170582", "1 mLoaderTask = " + mLoaderTask);
if (oldTask != null) {
oldTask.stopLocked();
}
@@ -400,6 +403,7 @@
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
+ android.util.Log.d("b/131170582", "2 mLoaderTask = " + mLoaderTask);
runOnWorkerThread(mLoaderTask);
}
}
@@ -444,6 +448,7 @@
mTask = task;
mIsLoaderTaskRunning = true;
mModelLoaded = false;
+ android.util.Log.d("b/131170582", "2 mModelLoaded = " + mModelLoaded);
}
}
@@ -451,6 +456,7 @@
synchronized (mLock) {
// Everything loaded bind the data.
mModelLoaded = true;
+ android.util.Log.d("b/131170582", "3 mModelLoaded = " + mModelLoaded);
}
}
@@ -460,6 +466,7 @@
// If we are still the last one to be scheduled, remove ourselves.
if (mLoaderTask == mTask) {
mLoaderTask = null;
+ android.util.Log.d("b/131170582", "3 mLoaderTask = " + mLoaderTask);
}
mIsLoaderTaskRunning = false;
}
@@ -530,7 +537,7 @@
}
- public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfoCompat info) {
+ public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index e738eb7..e6c2d0c 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -12,12 +12,16 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
+import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.WindowInsets;
+import java.util.Collections;
+import java.util.List;
+
public class LauncherRootView extends InsettableFrameLayout {
private final Launcher mLauncher;
@@ -30,8 +34,14 @@
@ViewDebug.ExportedProperty(category = "launcher")
private final RectF mTouchExcludeRegion = new RectF();
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
+ Collections.singletonList(new Rect());
+
private View mAlignedView;
private WindowStateListener mWindowStateListener;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private boolean mDisallowBackGesture;
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -177,10 +187,38 @@
return super.dispatchTouchEvent(ev);
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
+ setDisallowBackGesture(mDisallowBackGesture);
+ }
+
+ @TargetApi(Build.VERSION_CODES.Q)
+ public void setDisallowBackGesture(boolean disallowBackGesture) {
+ if (!Utilities.ATLEAST_Q) {
+ return;
+ }
+ mDisallowBackGesture = disallowBackGesture;
+ setSystemGestureExclusionRects(mDisallowBackGesture
+ ? SYSTEM_GESTURE_EXCLUSION_RECT
+ : Collections.emptyList());
+ }
+
public interface WindowStateListener {
void onWindowFocusChanged(boolean hasFocus);
void onWindowVisibilityChanged(int visibility);
}
+
+ @Override
+ public void requestLayout() {
+ super.requestLayout();
+ if (com.android.launcher3.TestProtocol.sDebugTracing) {
+ android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+ "requestLayout @ " + android.util.Log.getStackTraceString(
+ new Throwable()));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7598450..382eedd 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -67,7 +68,6 @@
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -625,7 +625,7 @@
/**
* Returns the full drawable for {@param info}.
* @param outObj this is set to the internal data associated with {@param info},
- * eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
+ * eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
*/
public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
boolean flattenDrawable, Object[] outObj) {
@@ -645,7 +645,7 @@
}
ShortcutKey key = ShortcutKey.fromItemInfo(info);
DeepShortcutManager sm = DeepShortcutManager.getInstance(launcher);
- List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+ List<ShortcutInfo> si = sm.queryForFullDetails(
key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
if (si.isEmpty()) {
return null;
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index d0e8873..5a2373b 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -19,11 +19,12 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.text.TextUtils;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ContentWriter;
/**
@@ -107,9 +108,9 @@
}
/**
- * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfoCompat}.
+ * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfo}.
*/
- public WorkspaceItemInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+ public WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context) {
user = shortcutInfo.getUserHandle();
itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
updateFromDeepShortcutInfo(shortcutInfo, context);
@@ -158,9 +159,9 @@
status |= FLAG_INSTALL_SESSION_ACTIVE;
}
- public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
- // {@link ShortcutInfoCompat#getActivity} can change during an update. Recreate the intent
- intent = shortcutInfo.makeIntent();
+ public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
+ // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
+ intent = ShortcutKey.makeIntent(shortcutInfo);
title = shortcutInfo.getShortLabel();
CharSequence label = shortcutInfo.getLongLabel();
@@ -179,7 +180,7 @@
/** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
public String getDeepShortcutId() {
return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
- getIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
+ getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
}
@Override
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 86b96b4..e2a5160 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -49,6 +49,8 @@
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BottomUserEducationView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -70,6 +72,7 @@
private static final float FLING_VELOCITY_MULTIPLIER = 135f;
// Starts the springs after at least 55% of the animation has passed.
private static final float FLING_ANIMATION_THRESHOLD = 0.55f;
+ private static final int ALPHA_CHANNEL_COUNT = 2;
private final Launcher mLauncher;
private final AdapterHolder[] mAH;
@@ -93,6 +96,8 @@
private RecyclerViewFastScroller mTouchHandler;
private final Point mFastScrollerOffset = new Point();
+ private final MultiValueAlpha mMultiValueAlpha;
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -122,12 +127,18 @@
addSpringView(R.id.all_apps_header);
addSpringView(R.id.apps_list_view);
addSpringView(R.id.all_apps_tabs_view_pager);
+
+ mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
}
public AllAppsStore getAppsStore() {
return mAllAppsStore;
}
+ public AlphaProperty getAlphaProperty(int index) {
+ return mMultiValueAlpha.getProperty(index);
+ }
+
@Override
protected void setDampedScrollShift(float shift) {
// Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 4a1d432..a7f89d9 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -29,6 +29,7 @@
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
@@ -64,6 +65,8 @@
}
};
+ private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
+
private AllAppsContainerView mAppsView;
private ScrimView mScrimView;
@@ -102,7 +105,7 @@
setScrollRangeDelta(mScrollRangeDelta);
if (mIsVerticalLayout) {
- mAppsView.setAlpha(1);
+ mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1);
mLauncher.getHotseat().setTranslationY(0);
mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index e7d4679..4275f31 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -21,12 +21,12 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import com.android.launcher3.Utilities;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.PackageUserKey;
import java.util.List;
@@ -43,7 +43,7 @@
void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
void onPackagesSuspended(String[] packageNames, UserHandle user);
void onPackagesUnsuspended(String[] packageNames, UserHandle user);
- void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+ void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
UserHandle user);
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index b641391..fc48ba7 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -32,7 +32,6 @@
import android.util.ArrayMap;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
@@ -179,12 +178,7 @@
public void onShortcutsChanged(@NonNull String packageName,
@NonNull List<ShortcutInfo> shortcuts,
@NonNull UserHandle user) {
- List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
- for (ShortcutInfo shortcutInfo : shortcuts) {
- shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
- }
-
- mCallback.onShortcutsChanged(packageName, shortcutInfoCompats, user);
+ mCallback.onShortcutsChanged(packageName, shortcuts, user);
}
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index b64fff4..6e7a1bd 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -24,6 +24,7 @@
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
import android.os.Build;
import android.os.Parcelable;
import android.os.Process;
@@ -34,7 +35,6 @@
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey;
@@ -135,14 +135,14 @@
});
}
- ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
- WorkspaceItemInfo info = new WorkspaceItemInfo(compat, context);
+ ShortcutInfo si = request.getShortcutInfo();
+ WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
// Apply the unbadged icon and fetch the actual icon asynchronously.
LauncherIcons li = LauncherIcons.obtain(context);
- info.applyFrom(li.createShortcutIcon(compat, false /* badged */));
+ info.applyFrom(li.createShortcutIcon(si, false /* badged */));
li.recycle();
LauncherAppState.getInstance(context).getModel()
- .updateAndBindWorkspaceItem(info, compat);
+ .updateAndBindWorkspaceItem(info, si);
return info;
} else {
return null;
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 5ac9867..a72089d 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -52,7 +52,6 @@
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.InstantAppResolver;
@@ -250,8 +249,7 @@
*/
public void onPlaceAutomaticallyClick(View v) {
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
- InstallShortcutReceiver.queueShortcut(
- new ShortcutInfoCompat(mRequest.getShortcutInfo()), this);
+ InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this);
logCommand(Action.Command.CONFIRM);
mRequest.accept();
finish();
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6cc49de..9f902ed 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -24,12 +24,10 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.os.Build;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -45,7 +43,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
@@ -57,8 +54,6 @@
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/**
* A ViewGroup that coordinates dragging across its descendants
@@ -73,9 +68,6 @@
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
- private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
- Collections.singletonList(new Rect());
-
@Thunk DragController mDragController;
// Variables relating to animation of views after drop
@@ -94,8 +86,6 @@
private final ViewGroupFocusHelper mFocusIndicatorHelper;
private final WorkspaceAndHotseatScrim mScrim;
- private boolean mDisallowBackGesture;
-
/**
* Used to create a new DragLayer from XML.
*
@@ -562,24 +552,6 @@
mScrim.onInsetsChanged(insets);
}
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
- setDisallowBackGesture(mDisallowBackGesture);
- }
-
- @TargetApi(Build.VERSION_CODES.Q)
- public void setDisallowBackGesture(boolean disallowBackGesture) {
- if (!Utilities.ATLEAST_Q) {
- return;
- }
- mDisallowBackGesture = disallowBackGesture;
- setSystemGestureExclusionRects(mDisallowBackGesture
- ? SYSTEM_GESTURE_EXCLUSION_RECT
- : Collections.emptyList());
- }
-
public WorkspaceAndHotseatScrim getScrim() {
return mScrim;
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index bdbea29..3ab97b0 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -24,6 +24,7 @@
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -54,7 +55,6 @@
import com.android.launcher3.FirstFrameAnimatorHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -321,11 +321,11 @@
boolean iconBadged = (info instanceof ItemInfoWithIcon)
&& (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
if ((info.id == ItemInfo.NO_ID && !iconBadged)
- || !(obj instanceof ShortcutInfoCompat)) {
+ || !(obj instanceof ShortcutInfo)) {
// The item is not yet added on home screen.
return new FixedSizeEmptyDrawable(iconSize);
}
- ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
+ ShortcutInfo si = (ShortcutInfo) obj;
LauncherIcons li = LauncherIcons.obtain(appState.getContext());
Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
li.recycle();
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 75f76d9..5c4af1f 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -19,6 +19,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Process;
@@ -28,9 +29,9 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.Themes;
import java.util.function.Supplier;
@@ -43,6 +44,8 @@
*/
public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
+ private static final String EXTRA_BADGEPKG = "badge_package";
+
private static final Object sPoolSync = new Object();
private static LauncherIcons sPool;
private static int sPoolId = 0;
@@ -106,15 +109,15 @@
// below methods should also migrate to BaseIconFactory
- public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo) {
+ public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
return createShortcutIcon(shortcutInfo, true /* badged */);
}
- public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, boolean badged) {
+ public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
return createShortcutIcon(shortcutInfo, badged, null);
}
- public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo,
+ public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo,
boolean badged, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
@@ -155,9 +158,9 @@
return result;
}
- public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
+ public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
- String badgePkg = shortcutInfo.getBadgePackage(mContext);
+ String badgePkg = getBadgePackage(shortcutInfo);
boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
@@ -175,4 +178,13 @@
return pkgInfo;
}
}
+
+ private String getBadgePackage(ShortcutInfo si) {
+ String whitelistedPkg = mContext.getString(R.string.shortcutinfo_badgepkg_whitelist);
+ if (whitelistedPkg.equals(si.getPackage())
+ && si.getExtras().containsKey(EXTRA_BADGEPKG)) {
+ return si.getExtras().getString(EXTRA_BADGEPKG);
+ }
+ return si.getPackage();
+ }
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index b0cc87b..8f0cd08 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -34,7 +35,6 @@
import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
@@ -366,7 +366,7 @@
* Clear all the deep shortcut counts for the given package, and re-add the new shortcut counts.
*/
public synchronized void updateDeepShortcutCounts(
- String packageName, UserHandle user, List<ShortcutInfoCompat> shortcuts) {
+ String packageName, UserHandle user, List<ShortcutInfo> shortcuts) {
if (packageName != null) {
Iterator<ComponentKey> keysIter = deepShortcutMap.keySet().iterator();
while (keysIter.hasNext()) {
@@ -379,7 +379,7 @@
}
// Now add the new shortcuts to the map.
- for (ShortcutInfoCompat shortcut : shortcuts) {
+ for (ShortcutInfo shortcut : shortcuts) {
boolean shouldShowInContainer = shortcut.isEnabled()
&& (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
if (shouldShowInContainer) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index f8c48ed..0138572 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -30,6 +30,7 @@
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
@@ -66,7 +67,6 @@
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LooperIdleLock;
@@ -285,7 +285,7 @@
mPackageInstaller.updateAndGetActiveSessionCache();
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
- Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+ Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
final LoaderCursor c = new LoaderCursor(contentResolver.query(
LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
@@ -317,10 +317,10 @@
// We can only query for shortcuts when the user is unlocked.
if (userUnlocked) {
- List<ShortcutInfoCompat> pinnedShortcuts =
+ List<ShortcutInfo> pinnedShortcuts =
mShortcutManager.queryForPinnedShortcuts(null, user);
if (mShortcutManager.wasLastCallSuccess()) {
- for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+ for (ShortcutInfo shortcut : pinnedShortcuts) {
shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
shortcut);
}
@@ -473,7 +473,7 @@
ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
if (unlockedUsers.get(c.serialNumber)) {
- ShortcutInfoCompat pinnedShortcut =
+ ShortcutInfo pinnedShortcut =
shortcutKeyToPinnedShortcuts.get(key);
if (pinnedShortcut == null) {
// The shortcut is no longer valid.
@@ -839,7 +839,7 @@
if (mBgDataModel.hasShortcutHostPermission) {
for (UserHandle user : mUserManager.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
- List<ShortcutInfoCompat> shortcuts =
+ List<ShortcutInfo> shortcuts =
mShortcutManager.queryForAllShortcuts(user);
mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 671dc54..c37ed99 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -42,7 +43,6 @@
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -213,7 +213,7 @@
if (si.isPromise() && isNewApkAvailable) {
boolean isTargetValid = true;
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- List<ShortcutInfoCompat> shortcut = DeepShortcutManager
+ List<ShortcutInfo> shortcut = DeepShortcutManager
.getInstance(context).queryForPinnedShortcuts(
cn.getPackageName(),
Arrays.asList(si.getDeepShortcutId()), mUser);
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 6adcc6e..8528228 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -16,6 +16,7 @@
package com.android.launcher3.model;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import com.android.launcher3.AllAppsList;
@@ -25,7 +26,6 @@
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
@@ -40,11 +40,11 @@
public class ShortcutsChangedTask extends BaseModelUpdateTask {
private final String mPackageName;
- private final List<ShortcutInfoCompat> mShortcuts;
+ private final List<ShortcutInfo> mShortcuts;
private final UserHandle mUser;
private final boolean mUpdateIdMap;
- public ShortcutsChangedTask(String packageName, List<ShortcutInfoCompat> shortcuts,
+ public ShortcutsChangedTask(String packageName, List<ShortcutInfo> shortcuts,
UserHandle user, boolean updateIdMap) {
mPackageName = packageName;
mShortcuts = shortcuts;
@@ -56,7 +56,6 @@
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
final Context context = app.getContext();
DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
- deepShortcutManager.onShortcutsChanged(mShortcuts);
// Find WorkspaceItemInfo's that have changed on the workspace.
HashSet<ShortcutKey> removedKeys = new HashSet<>();
@@ -76,9 +75,9 @@
final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
if (!keyToShortcutInfo.isEmpty()) {
// Update the workspace to reflect the changes to updated shortcuts residing on it.
- List<ShortcutInfoCompat> shortcuts = deepShortcutManager.queryForFullDetails(
+ List<ShortcutInfo> shortcuts = deepShortcutManager.queryForFullDetails(
mPackageName, new ArrayList<>(allIds), mUser);
- for (ShortcutInfoCompat fullDetails : shortcuts) {
+ for (ShortcutInfo fullDetails : shortcuts) {
ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
List<WorkspaceItemInfo> workspaceItemInfos = keyToShortcutInfo.remove(key);
if (!fullDetails.isPinned()) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 478800e..2cb256e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import com.android.launcher3.AllAppsList;
@@ -28,7 +29,6 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -56,12 +56,12 @@
boolean isUserUnlocked = UserManagerCompat.getInstance(context).isUserUnlocked(mUser);
DeepShortcutManager deepShortcutManager = DeepShortcutManager.getInstance(context);
- HashMap<ShortcutKey, ShortcutInfoCompat> pinnedShortcuts = new HashMap<>();
+ HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
if (isUserUnlocked) {
- List<ShortcutInfoCompat> shortcuts =
+ List<ShortcutInfo> shortcuts =
deepShortcutManager.queryForPinnedShortcuts(null, mUser);
if (deepShortcutManager.wasLastCallSuccess()) {
- for (ShortcutInfoCompat shortcut : shortcuts) {
+ for (ShortcutInfo shortcut : shortcuts) {
pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut);
}
} else {
@@ -82,7 +82,7 @@
WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
if (isUserUnlocked) {
ShortcutKey key = ShortcutKey.fromItemInfo(si);
- ShortcutInfoCompat shortcut = pinnedShortcuts.get(key);
+ ShortcutInfo shortcut = pinnedShortcuts.get(key);
// We couldn't verify the shortcut during loader. If its no longer available
// (probably due to clear data), delete the workspace item as well
if (shortcut == null) {
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 2b55405..dbfe988 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -17,6 +17,7 @@
package com.android.launcher3.popup;
import android.content.ComponentName;
+import android.content.pm.ShortcutInfo;
import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -29,7 +30,6 @@
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
@@ -54,10 +54,10 @@
/**
* Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
*/
- private static final Comparator<ShortcutInfoCompat> SHORTCUT_RANK_COMPARATOR
- = new Comparator<ShortcutInfoCompat>() {
+ private static final Comparator<ShortcutInfo> SHORTCUT_RANK_COMPARATOR
+ = new Comparator<ShortcutInfo>() {
@Override
- public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
+ public int compare(ShortcutInfo a, ShortcutInfo b) {
if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
return -1;
}
@@ -76,11 +76,11 @@
* @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
* @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
*/
- public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
- List<ShortcutInfoCompat> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
+ public static List<ShortcutInfo> sortAndFilterShortcuts(
+ List<ShortcutInfo> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
// Remove up to one specific shortcut before sorting and doing somewhat fancy filtering.
if (shortcutIdToRemoveFirst != null) {
- Iterator<ShortcutInfoCompat> shortcutIterator = shortcuts.iterator();
+ Iterator<ShortcutInfo> shortcutIterator = shortcuts.iterator();
while (shortcutIterator.hasNext()) {
if (shortcutIterator.next().getId().equals(shortcutIdToRemoveFirst)) {
shortcutIterator.remove();
@@ -96,11 +96,11 @@
// The list of shortcuts is now sorted with static shortcuts followed by dynamic
// shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
- List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
+ List<ShortcutInfo> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
int numDynamic = 0;
int size = shortcuts.size();
for (int i = 0; i < size; i++) {
- ShortcutInfoCompat shortcut = shortcuts.get(i);
+ ShortcutInfo shortcut = shortcuts.get(i);
int filteredSize = filteredShortcuts.size();
if (filteredSize < MAX_SHORTCUTS) {
// Always add the first MAX_SHORTCUTS to the filtered list.
@@ -140,13 +140,13 @@
uiHandler.post(() -> container.applyNotificationInfos(infos));
}
- List<ShortcutInfoCompat> shortcuts = DeepShortcutManager.getInstance(launcher)
+ List<ShortcutInfo> shortcuts = DeepShortcutManager.getInstance(launcher)
.queryForShortcutsContainer(activity, user);
String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
: notificationKeys.get(0).shortcutId;
shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
- final ShortcutInfoCompat shortcut = shortcuts.get(i);
+ final ShortcutInfo shortcut = shortcuts.get(i);
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
// Use unbadged icon for the menu.
LauncherIcons li = LauncherIcons.obtain(launcher);
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 199d9c8..9274d44 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -17,6 +17,7 @@
package com.android.launcher3.shortcuts;
import android.content.Context;
+import android.content.pm.ShortcutInfo;
import android.graphics.Point;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -43,7 +44,7 @@
private View mDivider;
private WorkspaceItemInfo mInfo;
- private ShortcutInfoCompat mDetail;
+ private ShortcutInfo mDetail;
public DeepShortcutView(Context context) {
this(context, null, 0);
@@ -93,7 +94,7 @@
}
/** package private **/
- public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfoCompat detail,
+ public void applyShortcutInfo(WorkspaceItemInfo info, ShortcutInfo detail,
PopupContainerWithArrow container) {
mInfo = info;
mDetail = detail;
@@ -130,7 +131,7 @@
return mIconView;
}
- public ShortcutInfoCompat getDetail() {
+ public ShortcutInfo getDetail() {
return mDetail;
}
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
deleted file mode 100644
index 5ec1997..0000000
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2016 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.shortcuts;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.os.UserHandle;
-
-import com.android.launcher3.R;
-import com.android.launcher3.WorkspaceItemInfo;
-
-/**
- * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
- *
- * Not to be confused with {@link WorkspaceItemInfo}.
- */
-public class ShortcutInfoCompat {
- private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
- private static final String EXTRA_BADGEPKG = "badge_package";
- public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
- private ShortcutInfo mShortcutInfo;
-
- public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
- mShortcutInfo = shortcutInfo;
- }
-
- public Intent makeIntent() {
- return new Intent(Intent.ACTION_MAIN)
- .addCategory(INTENT_CATEGORY)
- .setComponent(getActivity())
- .setPackage(getPackage())
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
- .putExtra(EXTRA_SHORTCUT_ID, getId());
- }
-
- public ShortcutInfo getShortcutInfo() {
- return mShortcutInfo;
- }
-
- public String getPackage() {
- return mShortcutInfo.getPackage();
- }
-
- public String getBadgePackage(Context context) {
- String whitelistedPkg = context.getString(R.string.shortcutinfocompat_badgepkg_whitelist);
- if (whitelistedPkg.equals(getPackage())
- && mShortcutInfo.getExtras().containsKey(EXTRA_BADGEPKG)) {
- return mShortcutInfo.getExtras().getString(EXTRA_BADGEPKG);
- }
- return getPackage();
- }
-
- public String getId() {
- return mShortcutInfo.getId();
- }
-
- public CharSequence getShortLabel() {
- return mShortcutInfo.getShortLabel();
- }
-
- public CharSequence getLongLabel() {
- return mShortcutInfo.getLongLabel();
- }
-
- public long getLastChangedTimestamp() {
- return mShortcutInfo.getLastChangedTimestamp();
- }
-
- public ComponentName getActivity() {
- return mShortcutInfo.getActivity();
- }
-
- public UserHandle getUserHandle() {
- return mShortcutInfo.getUserHandle();
- }
-
- public boolean hasKeyFieldsOnly() {
- return mShortcutInfo.hasKeyFieldsOnly();
- }
-
- public boolean isPinned() {
- return mShortcutInfo.isPinned();
- }
-
- public boolean isDeclaredInManifest() {
- return mShortcutInfo.isDeclaredInManifest();
- }
-
- public boolean isEnabled() {
- return mShortcutInfo.isEnabled();
- }
-
- public boolean isDynamic() {
- return mShortcutInfo.isDynamic();
- }
-
- public int getRank() {
- return mShortcutInfo.getRank();
- }
-
- public CharSequence getDisabledMessage() {
- return mShortcutInfo.getDisabledMessage();
- }
-
- @Override
- public String toString() {
- return mShortcutInfo.toString();
- }
-}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index cbef85a..70665ca 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -2,6 +2,7 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import com.android.launcher3.ItemInfo;
@@ -12,6 +13,9 @@
*/
public class ShortcutKey extends ComponentKey {
+ public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
+ private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+
public ShortcutKey(String packageName, UserHandle user, String id) {
// Use the id as the class name.
super(new ComponentName(packageName, id), user);
@@ -25,18 +29,26 @@
return componentName.getClassName();
}
- public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) {
+ public static ShortcutKey fromInfo(ShortcutInfo shortcutInfo) {
return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(),
shortcutInfo.getId());
}
public static ShortcutKey fromIntent(Intent intent, UserHandle user) {
- String shortcutId = intent.getStringExtra(
- ShortcutInfoCompat.EXTRA_SHORTCUT_ID);
+ String shortcutId = intent.getStringExtra(EXTRA_SHORTCUT_ID);
return new ShortcutKey(intent.getPackage(), user, shortcutId);
}
public static ShortcutKey fromItemInfo(ItemInfo info) {
return fromIntent(info.getIntent(), info.user);
}
+
+ public static Intent makeIntent(ShortcutInfo si) {
+ return new Intent(Intent.ACTION_MAIN)
+ .addCategory(INTENT_CATEGORY)
+ .setComponent(si.getActivity())
+ .setPackage(si.getPackage())
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+ .putExtra(EXTRA_SHORTCUT_ID, si.getId());
+ }
}
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 4de082e..07ddccb 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -20,12 +20,12 @@
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
-import static android.view.ViewConfiguration.getLongPressTimeout;
import static com.android.launcher3.LauncherState.NORMAL;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.view.GestureDetector;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
@@ -45,7 +45,8 @@
/**
* Helper class to handle touch on empty space in workspace and show options popup on long press
*/
-public class WorkspaceTouchListener implements OnTouchListener, Runnable {
+public class WorkspaceTouchListener extends GestureDetector.SimpleOnGestureListener
+ implements OnTouchListener {
/**
* STATE_PENDING_PARENT_INFORM is the state between longPress performed & the next motionEvent.
@@ -66,16 +67,21 @@
private int mLongPressState = STATE_CANCELLED;
+ private final GestureDetector mGestureDetector;
+
public WorkspaceTouchListener(Launcher launcher, Workspace workspace) {
mLauncher = launcher;
mWorkspace = workspace;
// Use twice the touch slop as we are looking for long press which is more
// likely to cause movement.
mTouchSlop = 2 * ViewConfiguration.get(launcher).getScaledTouchSlop();
+ mGestureDetector = new GestureDetector(workspace.getContext(), this);
}
@Override
public boolean onTouch(View view, MotionEvent ev) {
+ mGestureDetector.onTouchEvent(ev);
+
int action = ev.getActionMasked();
if (action == ACTION_DOWN) {
// Check if we can handle long press.
@@ -97,7 +103,6 @@
if (handleLongPress) {
mLongPressState = STATE_REQUESTED;
mTouchDownPoint.set(ev.getX(), ev.getY());
- mWorkspace.postDelayed(this, getLongPressTimeout());
}
mWorkspace.onTouchEvent(ev);
@@ -143,9 +148,6 @@
}
}
- if (action == ACTION_UP || action == ACTION_CANCEL) {
- cancelLongPress();
- }
return result;
}
@@ -155,12 +157,11 @@
}
private void cancelLongPress() {
- mWorkspace.removeCallbacks(this);
mLongPressState = STATE_CANCELLED;
}
@Override
- public void run() {
+ public void onLongPress(MotionEvent event) {
if (mLongPressState == STATE_REQUESTED) {
if (canHandleLongPress()) {
mLongPressState = STATE_PENDING_PARENT_INFORM;
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 5747db1..2ee0328 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -22,6 +22,8 @@
import java.util.concurrent.ExecutionException;
+import androidx.annotation.VisibleForTesting;
+
/**
* Utility class for defining singletons which are initiated on main thread.
*/
@@ -53,6 +55,11 @@
return mValue;
}
+ @VisibleForTesting
+ public void initializeForTesting(T value) {
+ mValue = value;
+ }
+
public interface ObjectProvider<T> {
T get(Context context);
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index a264f9b..c360117 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -59,6 +59,8 @@
import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.util.MultiValueAlpha;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
import java.util.List;
@@ -92,6 +94,7 @@
private static final int WALLPAPERS = R.string.wallpaper_button_text;
private static final int WIDGETS = R.string.widget_button_text;
private static final int SETTINGS = R.string.settings_button_text;
+ private static final int ALPHA_CHANNEL_COUNT = 1;
private final Rect mTempRect = new Rect();
private final int[] mTempPos = new int[2];
@@ -115,6 +118,8 @@
private final Rect mDragHandleBounds;
private final RectF mHitRect = new RectF();
+ private final MultiValueAlpha mMultiValueAlpha;
+
private final AccessibilityHelper mAccessibilityHelper;
@Nullable
protected Drawable mDragHandle;
@@ -138,6 +143,11 @@
mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
setFocusable(false);
+ mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
+ }
+
+ public AlphaProperty getAlphaProperty(int index) {
+ return mMultiValueAlpha.getProperty(index);
}
@NonNull
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index e7008fd..6b6f70d 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -74,17 +74,13 @@
return mWasLastCallSuccess;
}
- public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
- // mShortcutCache.removeShortcuts(shortcuts);
- }
-
/**
* Queries for the shortcuts with the package name and provided ids.
*
* This method is intended to get the full details for shortcuts when they are added or updated,
* because we only get "key" fields in onShortcutsChanged().
*/
- public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
+ public List<ShortcutInfo> queryForFullDetails(String packageName,
List<String> shortcutIds, UserHandle user) {
return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
}
@@ -93,7 +89,7 @@
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
+ public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
UserHandle user) {
return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
activity.getPackageName(), activity, null, user);
@@ -149,10 +145,9 @@
}
}
- public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+ public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
try {
- Drawable icon = mLauncherApps.getShortcutIconDrawable(
- shortcutInfo.getShortcutInfo(), density);
+ Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
mWasLastCallSuccess = true;
return icon;
} catch (SecurityException|IllegalStateException e) {
@@ -167,22 +162,22 @@
*
* If packageName is null, returns all pinned shortcuts regardless of package.
*/
- public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName, UserHandle user) {
+ public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
return queryForPinnedShortcuts(packageName, null, user);
}
- public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+ public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
List<String> shortcutIds, UserHandle user) {
return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
}
- public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandle user) {
+ public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
return query(FLAG_GET_ALL, null, null, null, user);
}
- private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) {
+ private List<String> extractIds(List<ShortcutInfo> shortcuts) {
List<String> shortcutIds = new ArrayList<>(shortcuts.size());
- for (ShortcutInfoCompat shortcut : shortcuts) {
+ for (ShortcutInfo shortcut : shortcuts) {
shortcutIds.add(shortcut.getId());
}
return shortcutIds;
@@ -194,7 +189,7 @@
*
* TODO: Use the cache to optimize this so we don't make an RPC every time.
*/
- private List<ShortcutInfoCompat> query(int flags, String packageName,
+ private List<ShortcutInfo> query(int flags, String packageName,
ComponentName activity, List<String> shortcutIds, UserHandle user) {
ShortcutQuery q = new ShortcutQuery();
q.setQueryFlags(flags);
@@ -214,11 +209,7 @@
if (shortcutInfos == null) {
return Collections.EMPTY_LIST;
}
- List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
- for (ShortcutInfo shortcutInfo : shortcutInfos) {
- shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
- }
- return shortcutInfoCompats;
+ return shortcutInfos;
}
public boolean hasHostPermission() {
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 71e7880..a37218b 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -168,7 +168,7 @@
}
@After
- public void tearDown() {
+ public void verifyLauncherState() {
try {
// Limits UI tests affecting tests running after them.
waitForModelLoaded();
@@ -234,12 +234,8 @@
protected void resetLoaderState() {
try {
- mMainThreadExecutor.execute(new Runnable() {
- @Override
- public void run() {
- LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
- }
- });
+ mMainThreadExecutor.execute(
+ () -> LauncherAppState.getInstance(mTargetContext).getModel().forceReload());
} catch (Throwable t) {
throw new IllegalArgumentException(t);
}
diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
index 33b6f61..357e029 100644
--- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
+++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java
@@ -71,7 +71,7 @@
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
@@ -86,7 +86,7 @@
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testCustomProfileLoaded_with_widget() throws Exception {
// A non-restored widget with no config screen gets restored automatically.
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -106,7 +106,7 @@
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testCustomProfileLoaded_with_folder() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
.addApp(SETTINGS_APP, SETTINGS_APP)
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 5ab8d05..c55bc72 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -370,7 +370,7 @@
launch(getAppPackageName());
}
- private static String getAppPackageName() {
+ public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 80561fc..84452b4 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -71,25 +71,25 @@
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testWidgetConfig() throws Throwable {
runTest(false, true);
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testWidgetConfig_rotate() throws Throwable {
runTest(true, true);
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testConfigCancelled() throws Throwable {
runTest(false, false);
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testConfigCancelled_rotate() throws Throwable {
runTest(true, false);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index 7d3cf2b..90c339f 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -49,14 +49,14 @@
@Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testDragIcon_portrait() throws Throwable {
lockRotation(true);
performTest();
}
@Test
- @Ignore
+ @Ignore // Convert test to TAPL and enable them; b/131116002
public void testDragIcon_landscape() throws Throwable {
lockRotation(false);
performTest();
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index c8e7786..af50190 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -103,8 +103,6 @@
if (mSessionId > -1) {
mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
}
-
- super.tearDown();
}
@Test
@@ -125,7 +123,7 @@
verifyWidgetPresent(info);
}
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testUnboundWidget_removed() {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
LauncherAppWidgetInfo item = createWidgetInfo(info, false);
@@ -145,7 +143,7 @@
assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
}
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testPendingWidget_autoRestored() {
// A non-restored widget with no config screen gets restored automatically.
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
@@ -183,7 +181,7 @@
LauncherSettings.Favorites.APPWIDGET_ID))));
}
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testPendingWidget_notRestored_removed() {
LauncherAppWidgetInfo item = getInvalidWidgetInfo();
item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 4899334..65d8a82 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -80,7 +80,7 @@
@Test
public void testEmpty() throws Throwable { /* needed while the broken tests are being fixed */ }
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testPinWidgetNoConfig() throws Throwable {
runTest("pinWidgetNoConfig", true, new ItemOperator() {
@Override
@@ -93,7 +93,7 @@
});
}
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
Intent command = RequestPinItemActivity.getCommandIntent(
@@ -111,7 +111,7 @@
}, command);
}
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testPinWidgetWithConfig() throws Throwable {
runTest("pinWidgetWithConfig", true, new ItemOperator() {
@Override
@@ -124,7 +124,7 @@
});
}
- @Test @Ignore
+ @Test @Ignore // b/131116593
public void testPinShortcut() throws Throwable {
// Command to set the shortcut id
Intent command = RequestPinItemActivity.getCommandIntent(
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 8f777b6..27bc43e 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -175,6 +175,7 @@
// Workaround, use constructed context because both the instrumentation context and the
// app context are not constructed with resources that take overlays into account
final Context ctx = baseContext.createPackageContext("android", 0);
+ log("Interaction mode = " + getCurrentInteractionMode(ctx));
if (isGesturalMode(ctx)) {
return NavigationModel.ZERO_BUTTON;
} else if (isSwipeUpMode(ctx)) {