Merge "Avoid mis-trigger when motion do not endup within system gesture region" into sc-dev
diff --git a/Android.bp b/Android.bp
index cca48ce..002f6fe 100644
--- a/Android.bp
+++ b/Android.bp
@@ -93,3 +93,93 @@
sdk_version: "current",
min_sdk_version: "28",
}
+
+//
+// Build rule for Launcher3 dependencies lib.
+//
+android_library {
+ name: "Launcher3CommonDepsLib",
+ static_libs: [
+ "androidx.recyclerview_recyclerview",
+ "androidx.dynamicanimation_dynamicanimation",
+ "androidx.preference_preference",
+ "androidx.slice_slice-view",
+ "iconloader_base",
+ "LauncherPluginLib",
+ "launcher_quickstep_log_protos_lite"
+ ],
+ srcs: [
+ "src_build_config/**/*.java",
+ ],
+ resource_dirs: ["res"],
+ optimize: {
+ enabled: false,
+ },
+ sdk_version: "current",
+ min_sdk_version: "26",
+ manifest: "AndroidManifest-common.xml",
+}
+
+//
+// Build rule for Launcher3 app.
+//
+android_app {
+ name: "Launcher3",
+
+ static_libs: [
+ "Launcher3CommonDepsLib",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src_shortcuts_overrides/**/*.java",
+ "src_ui_overrides/**/*.java",
+ "ext_tests/src/**/*.java",
+ ],
+ resource_dirs: [
+ "ext_tests/res",
+ ],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ // Proguard is disable for testing. Derivarive prjects to keep proguard enabled
+ enabled: false,
+ },
+
+ sdk_version: "current",
+ min_sdk_version: "26",
+ target_sdk_version: "29",
+ privileged: true,
+ system_ext_specific: true,
+
+ overrides: [
+ "Home",
+ "Launcher2",
+ ],
+ required: ["privapp_whitelist_com.android.launcher3"],
+
+ jacoco: {
+ include_filter: ["com.android.launcher3.**"],
+ },
+ additional_manifests: [
+ "AndroidManifest-common.xml",
+ ],
+}
+
+//
+// Launcher Robolectric test target.
+//
+java_library {
+ name: "Launcher3TestCommon",
+ libs: [
+ "Launcher3CommonDepsLib",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src_shortcuts_overrides/**/*.java",
+ "src_ui_overrides/**/*.java",
+ "ext_tests/src/**/*.java",
+ "tests/src_common/**/*.java",
+ ],
+ target_sdk_version: "29",
+ sdk_version: "current",
+ min_sdk_version: "26",
+}
diff --git a/Android.mk b/Android.mk
index 304935b..89870a6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,79 +17,6 @@
LOCAL_PATH := $(call my-dir)
#
-# Build rule for Launcher3 dependencies lib.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT2_ONLY := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.recyclerview_recyclerview \
- androidx.dynamicanimation_dynamicanimation \
- androidx.preference_preference \
- androidx.slice_slice-view \
- iconloader_base
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- LauncherPluginLib \
- launcher_quickstep_log_protos_lite \
- search_ui
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src_build_config) \
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 26
-LOCAL_MODULE := Launcher3CommonDepsLib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := AndroidManifest-common.xml
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
-# Build rule for Launcher3 app.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := Launcher3CommonDepsLib
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, src_shortcuts_overrides) \
- $(call all-java-files-under, src_ui_overrides) \
- $(call all-java-files-under, ext_tests/src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-# Proguard is disable for testing. Derivarive prjects to keep proguard enabled
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 26
-LOCAL_PACKAGE_NAME := Launcher3
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-
-include $(BUILD_PACKAGE)
-
-#
# Build rule for Launcher3 Go app for Android Go devices.
#
include $(CLEAR_VARS)
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index a202095..cc5e1cb 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -53,10 +54,15 @@
*
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
*/
- public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsList(Context context) {
+ public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
return EMPTY_WIDGET_LIST;
}
+ /** Returns a mapping of packages to their widgets without static shortcuts. */
+ public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+ return Map.of();
+ }
+
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
diff --git a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
new file mode 100644
index 0000000..70a143e
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Process;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowAppWidgetManager;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsPredicationUpdateTaskTest {
+
+ private AppWidgetProviderInfo mApp1Provider1 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp1Provider2 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp2Provider1 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp4Provider1 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp4Provider2 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp5Provider1 = new AppWidgetProviderInfo();
+
+ private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
+ private Context mContext;
+ private LauncherModelHelper mModelHelper;
+ private UserHandle mUserHandle;
+ private InvariantDeviceProfile mTestProfile;
+
+ @Mock
+ private IconCache mIconCache;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doAnswer(invocation -> {
+ ComponentWithLabel componentWithLabel = invocation.getArgument(0);
+ return componentWithLabel.getComponent().getShortClassName();
+ }).when(mIconCache).getTitleNoCache(any());
+
+ mContext = RuntimeEnvironment.application;
+ mModelHelper = new LauncherModelHelper();
+ mUserHandle = Process.myUserHandle();
+ mTestProfile = new InvariantDeviceProfile();
+ // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
+ mModelHelper.initializeData("/widgets_predication_update_task_data.txt");
+
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ mApp1Provider1.provider = ComponentName.createRelative("app1", "provider1");
+ ReflectionHelpers.setField(mApp1Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp1Provider1.provider));
+ mApp1Provider2.provider = ComponentName.createRelative("app1", "provider2");
+ ReflectionHelpers.setField(mApp1Provider2, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp1Provider2.provider));
+ mApp2Provider1.provider = ComponentName.createRelative("app2", "provider1");
+ ReflectionHelpers.setField(mApp2Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp2Provider1.provider));
+ mApp4Provider1.provider = ComponentName.createRelative("app4", "provider1");
+ ReflectionHelpers.setField(mApp4Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp4Provider1.provider));
+ mApp4Provider2.provider = ComponentName.createRelative("app4", ".provider2");
+ ReflectionHelpers.setField(mApp4Provider2, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp4Provider2.provider));
+ mApp5Provider1.provider = ComponentName.createRelative("app5", "provider1");
+ ReflectionHelpers.setField(mApp5Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp5Provider1.provider));
+
+ ShadowAppWidgetManager shadowAppWidgetManager =
+ shadowOf(mContext.getSystemService(AppWidgetManager.class));
+ shadowAppWidgetManager.addInstalledProvider(mApp1Provider1);
+ shadowAppWidgetManager.addInstalledProvider(mApp1Provider2);
+ shadowAppWidgetManager.addInstalledProvider(mApp2Provider1);
+ shadowAppWidgetManager.addInstalledProvider(mApp4Provider1);
+ shadowAppWidgetManager.addInstalledProvider(mApp4Provider2);
+ shadowAppWidgetManager.addInstalledProvider(mApp5Provider1);
+
+ mModelHelper.getModel().addCallbacks(mCallback);
+
+ MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
+ LauncherAppState.getInstance(mContext), /* packageUser= */ null));
+ waitUntilIdle();
+ }
+
+
+ @Test
+ public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
+ throws Exception {
+ // WHEN newPredicationTask is executed with app predication of 5 apps.
+ AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
+ mUserHandle);
+ AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
+ mUserHandle);
+ AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
+ mUserHandle);
+ AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
+ mUserHandle);
+ AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
+ mUserHandle);
+ mModelHelper.executeTaskForTest(
+ newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
+ .forEach(Runnable::run);
+
+ // THEN only 3 widgets are returned because
+ // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
+ // excluded from the result.
+ // 2. app3 doesn't have a widget.
+ // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(3);
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
+ assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+ }
+
+ private void assertWidgetInfo(
+ LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
+ assertThat(actual.provider).isEqualTo(expected.provider);
+ assertThat(actual.getUser()).isEqualTo(expected.getProfile());
+ }
+
+ private void waitUntilIdle() {
+ shadowOf(MODEL_EXECUTOR.getLooper()).idle();
+ shadowOf(MAIN_EXECUTOR.getLooper()).idle();
+ }
+
+ private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) {
+ return new WidgetsPredictionUpdateTask(
+ new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"),
+ appTargets);
+ }
+
+ private final class FakeBgDataModelCallback implements BgDataModel.Callbacks {
+
+ private FixedContainerItems mRecommendedWidgets = null;
+
+ @Override
+ public void bindExtraContainerItems(FixedContainerItems item) {
+ mRecommendedWidgets = item;
+ }
+
+ @Override
+ public int getPageToBindSynchronously() {
+ return 0;
+ }
+
+ @Override
+ public void clearPendingBinds() { }
+
+ @Override
+ public void startBinding() { }
+
+ @Override
+ public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
+
+ @Override
+ public void bindScreens(IntArray orderedScreenIds) { }
+
+ @Override
+ public void finishFirstPageBind(ViewOnDrawExecutor executor) { }
+
+ @Override
+ public void finishBindingItems(int pageBoundFirst) { }
+
+ @Override
+ public void preAddApps() { }
+
+ @Override
+ public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
+ ArrayList<ItemInfo> addAnimated) { }
+
+ @Override
+ public void bindIncrementalDownloadProgressUpdated(AppInfo app) { }
+
+ @Override
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
+
+ @Override
+ public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
+
+ @Override
+ public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
+
+ @Override
+ public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
+
+ @Override
+ public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
+
+ @Override
+ public void onPageBoundSynchronously(int page) { }
+
+ @Override
+ public void executeOnNextDraw(ViewOnDrawExecutor executor) { }
+
+ @Override
+ public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) { }
+
+ @Override
+ public void bindAllApplications(AppInfo[] apps, int flags) { }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index b570d55..83234e3 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -41,7 +41,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsSectionDecorator;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
@@ -106,8 +105,6 @@
private boolean mPredictionsEnabled = false;
- AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
-
@Nullable
private List<ItemInfo> mPendingPredictedItems;
@@ -129,11 +126,6 @@
mIconFullTextAlpha = Color.alpha(mIconTextColor);
mIconCurrentTextAlpha = mIconFullTextAlpha;
- if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
- mDecorationHandler = new AllAppsSectionDecorator.SectionDecorationHandler(getContext(),
- false, 0, true, true);
- }
-
updateVisibility();
}
@@ -159,16 +151,6 @@
@Override
protected void dispatchDraw(Canvas canvas) {
- if (mDecorationHandler != null) {
- mDecorationHandler.reset();
- int childrenCount = getChildCount();
- for (int i = 0; i < childrenCount; i++) {
- mDecorationHandler.extendBounds(getChildAt(i));
- }
- mDecorationHandler.onGroupDraw(canvas);
- mDecorationHandler.onFocusDraw(canvas, getFocusedChild());
- mLauncher.getAppsView().getActiveRecyclerView().invalidateItemDecorations();
- }
mFocusHelper.draw(canvas);
super.dispatchDraw(canvas);
}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 2ea8bd2..b1b4d70 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.Utilities.getDevicePrefs;
@@ -80,6 +81,8 @@
new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
private final PredictorState mHotseatState =
new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions");
+ private final PredictorState mWidgetsRecommendationState =
+ new PredictorState(CONTAINER_WIDGETS_PREDICTION, "widgets_prediction");
private final InvariantDeviceProfile mIDP;
private final AppEventProducer mAppEventProducer;
@@ -111,6 +114,9 @@
mHotseatState.items.setItems(
mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
+
+ // Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
+ mDataModel.extraItems.put(CONTAINER_WIDGETS_PREDICTION, mWidgetsRecommendationState.items);
mActive = true;
}
@@ -161,6 +167,9 @@
if (mAllAppsState.predictor != null) {
mAllAppsState.predictor.requestPredictionUpdate();
}
+ if (mWidgetsRecommendationState.predictor != null) {
+ mWidgetsRecommendationState.predictor.requestPredictionUpdate();
+ }
}
@Override
@@ -176,6 +185,7 @@
private void destroyPredictors() {
mAllAppsState.destroyPredictor();
mHotseatState.destroyPredictor();
+ mWidgetsRecommendationState.destroyPredictor();
}
@WorkerThread
@@ -203,6 +213,12 @@
.setPredictedTargetCount(mIDP.numHotseatIcons)
.setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
.build()));
+
+ registerWidgetsPredictor(apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(context)
+ .setUiSurface("widgets")
+ .setPredictedTargetCount(mIDP.numColumns)
+ .build()));
}
private void registerPredictor(PredictorState state, AppPredictor predictor) {
@@ -220,6 +236,20 @@
mApp.getModel().enqueueModelUpdateTask(new PredictionUpdateTask(state, targets));
}
+ private void registerWidgetsPredictor(AppPredictor predictor) {
+ mWidgetsRecommendationState.predictor = predictor;
+ mWidgetsRecommendationState.predictor.registerPredictionUpdates(
+ Executors.MODEL_EXECUTOR, targets -> {
+ if (mWidgetsRecommendationState.setTargets(targets)) {
+ // No diff, skip
+ return;
+ }
+ mApp.getModel().enqueueModelUpdateTask(
+ new WidgetsPredictionUpdateTask(mWidgetsRecommendationState, targets));
+ });
+ mWidgetsRecommendationState.predictor.requestPredictionUpdate();
+ }
+
@Override
public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
new file mode 100644
index 0000000..a29ac1a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.app.prediction.AppTarget;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/** Task to update model as a result of predicted widgets update */
+public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {
+ private final PredictorState mPredictorState;
+ private final List<AppTarget> mTargets;
+
+ WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
+ mPredictorState = predictorState;
+ mTargets = targets;
+ }
+
+ /**
+ * Uses the app predication result to infer widgets that the user may want to use.
+ *
+ * <p>The algorithm uses the app prediction ranking to create a widgets ranking which only
+ * includes one widget per app and excludes widgets that have already been added to the
+ * workspace.
+ */
+ @Override
+ public void execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps) {
+ Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
+ widget -> new ComponentKey(widget.providerName, widget.user)).collect(
+ Collectors.toSet());
+ Map<PackageUserKey, List<WidgetItem>> allWidgets =
+ dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
+
+ ArrayList<ItemInfo> recommendedWidgetsInDescendingOrder = new ArrayList<>();
+ for (AppTarget app : mTargets) {
+ PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
+ if (allWidgets.containsKey(packageUserKey)) {
+ List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
+ .filter(item ->
+ !widgetsInWorkspace.contains(
+ new ComponentKey(item.componentName, item.user)))
+ .collect(Collectors.toList());
+ if (notAddedWidgets.size() > 0) {
+ // Even an apps have more than one widgets, we only include one widget.
+ recommendedWidgetsInDescendingOrder.add(
+ new PendingAddWidgetInfo(notAddedWidgets.get(0).widgetInfo));
+ }
+ }
+ }
+ FixedContainerItems fixedContainerItems = mPredictorState.items;
+ fixedContainerItems.items.clear();
+ fixedContainerItems.items.addAll(recommendedWidgetsInDescendingOrder);
+ bindExtraContainerItems(fixedContainerItems);
+
+ // Don't store widgets prediction to disk because it is not used frequently.
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 74a82ab..544835c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -85,6 +85,7 @@
// Contains all loaded Hotseat items.
private ItemInfo[] mLatestLoadedHotseatItems;
+ private @Nullable Animator mAnimator;
private boolean mIsAnimatingToLauncher;
public TaskbarController(BaseQuickstepLauncher launcher,
@@ -252,6 +253,10 @@
mTaskbarVisibilityController.cleanup();
mHotseatController.cleanup();
mRecentsController.cleanup();
+
+ if (mAnimator != null) {
+ mAnimator.end();
+ }
}
private void removeFromWindowManager() {
@@ -294,13 +299,21 @@
*/
public void onLauncherResumedOrPaused(boolean isResumed) {
long duration = QuickstepAppTransitionManagerImpl.CONTENT_ALPHA_DURATION;
- final Animator anim;
- if (isResumed) {
- anim = createAnimToLauncher(null, duration);
- } else {
- anim = createAnimToApp(duration);
+ if (mAnimator != null) {
+ mAnimator.cancel();
}
- anim.start();
+ if (isResumed) {
+ mAnimator = createAnimToLauncher(null, duration);
+ } else {
+ mAnimator = createAnimToApp(duration);
+ }
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimator = null;
+ }
+ });
+ mAnimator.start();
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 55dde45..b009629 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -218,6 +218,8 @@
.setPredictedApps(item.items);
} else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
mHotseatPredictionController.setPredictedItems(item);
+ } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
+ getPopupDataProvider().setRecommendedWidgets(item.items);
}
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 7f2af6b..e0c041e 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -934,7 +934,9 @@
duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
}
}
- Interpolator interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
+ Interpolator interpolator =
+ endTarget == RECENTS ? (mDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
+ ? ACCEL_DEACCEL : OVERSHOOT_1_2) : DEACCEL;
if (endTarget.isLauncher) {
mInputConsumerProxy.enable();
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 357aef1..5668817 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -37,6 +37,7 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.ISplitScreenListener;
+import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.RemoteTransitionCompat;
@@ -538,4 +539,18 @@
}
}
}
+
+ /**
+ * Sets listener to get callbacks when launching a task.
+ */
+ @Override
+ public void setStartingWindowListener(IStartingWindowListener listener) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setStartingWindowListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setStartingWindowListener", e);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index a762cb7..66c24c8 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.util.Log;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
@@ -55,6 +56,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LogConfig;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.Optional;
@@ -94,7 +96,7 @@
@Override
protected StatsLogger createLogger() {
- return new StatsCompatLogger();
+ return new StatsCompatLogger(mContext);
}
/**
@@ -136,6 +138,7 @@
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
+ private Context mContext;
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private OptionalInt mRank = OptionalInt.empty();
@@ -147,6 +150,10 @@
private Optional<String> mEditText = Optional.empty();
private SliceItem mSliceItem;
+ StatsCompatLogger(Context context) {
+ mContext = context;
+ }
+
@Override
public StatsLogger withItemInfo(ItemInfo itemInfo) {
if (mContainerInfo.isPresent()) {
@@ -220,7 +227,6 @@
if (!Utilities.ATLEAST_R) {
return;
}
-
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
if (mSliceItem != null) {
@@ -256,6 +262,26 @@
}
}
+ @Override
+ public void sendToInteractionJankMonitor(EventEnum event, View view) {
+ if (!(event instanceof LauncherEvent)) {
+ return;
+ }
+ switch ((LauncherEvent) event) {
+ case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN:
+ InteractionJankMonitorWrapper.begin(
+ view,
+ InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+ break;
+ case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+ break;
+ default:
+ break;
+ }
+ }
+
private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 9af4d30..e7101cc 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -130,16 +130,16 @@
applyPrimaryTranslation();
}
- public float getScrollAdjustment() {
+ public float getScrollAdjustment(boolean gridEnabled) {
float scrollAdjustment = 0;
- if (mGridProgress > 0) {
+ if (gridEnabled) {
scrollAdjustment += mGridTranslationPrimary;
}
return scrollAdjustment;
}
- public float getOffsetAdjustment() {
- return getScrollAdjustment();
+ public float getOffsetAdjustment(boolean gridEnabled) {
+ return getScrollAdjustment(gridEnabled);
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index d99f707..c62f3e2 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -160,6 +160,8 @@
reset();
}
setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
+ setOverviewGridEnabled(finalState.displayOverviewTasksAsGrid(mActivity));
+ setOverviewFullscreenEnabled(finalState.getOverviewFullscreenProgress() == 1);
setFreezeViewVisibility(false);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f20ca82..bdd0a36 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -299,6 +299,8 @@
protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
+ private boolean mOverviewGridEnabled;
+ private boolean mOverviewFullscreenEnabled;
private float mAdjacentPageOffset = 0;
private float mTaskViewsSecondaryTranslation = 0;
@@ -676,8 +678,10 @@
}
private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) {
- int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment();
- int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment());
+ int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
+ mOverviewFullscreenEnabled, mOverviewGridEnabled);
+ int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment(
+ mOverviewFullscreenEnabled, mOverviewGridEnabled));
int taskEnd = taskStart + taskSize;
return (taskStart >= start && taskStart <= end) || (taskEnd >= start
&& taskEnd <= end);
@@ -2664,9 +2668,10 @@
View child = getChildAt(i);
float scrollDiff = 0;
if (child instanceof TaskView) {
- scrollDiff = ((TaskView) child).getScrollAdjustment();
+ scrollDiff = ((TaskView) child).getScrollAdjustment(mOverviewFullscreenEnabled,
+ mOverviewGridEnabled);
} else if (child instanceof ClearAllButton) {
- scrollDiff = ((ClearAllButton) child).getScrollAdjustment();
+ scrollDiff = ((ClearAllButton) child).getScrollAdjustment(mOverviewGridEnabled);
}
if (scrollDiff != 0) {
@@ -2682,9 +2687,10 @@
int childOffset = super.getChildOffset(index);
View child = getChildAt(index);
if (child instanceof TaskView) {
- childOffset += ((TaskView) child).getOffsetAdjustment();
+ childOffset += ((TaskView) child).getOffsetAdjustment(mOverviewFullscreenEnabled,
+ mOverviewGridEnabled);
} else if (child instanceof ClearAllButton) {
- childOffset += ((ClearAllButton) child).getOffsetAdjustment();
+ childOffset += ((ClearAllButton) child).getOffsetAdjustment(mOverviewGridEnabled);
}
return childOffset;
}
@@ -2695,7 +2701,8 @@
if (taskView == null) {
return super.getChildVisibleSize(index);
}
- return (int) (super.getChildVisibleSize(index) * taskView.getSizeAdjustment());
+ return (int) (super.getChildVisibleSize(index) * taskView.getSizeAdjustment(
+ mOverviewFullscreenEnabled, mOverviewGridEnabled));
}
@Override
@@ -2810,6 +2817,23 @@
}
}
+ public void setOverviewGridEnabled(boolean overviewGridEnabled) {
+ if (mOverviewGridEnabled != overviewGridEnabled) {
+ mOverviewGridEnabled = overviewGridEnabled;
+ // Request layout to ensure scroll position is recalculated with updated mGridProgress.
+ requestLayout();
+ }
+ }
+
+ public void setOverviewFullscreenEnabled(boolean overviewFullscreenEnabled) {
+ if (mOverviewFullscreenEnabled != overviewFullscreenEnabled) {
+ mOverviewFullscreenEnabled = overviewFullscreenEnabled;
+ // Request layout to ensure scroll position is recalculated with updated
+ // mFullscreenProgress.
+ requestLayout();
+ }
+ }
+
/**
* Switch the current running task view to static snapshot mode,
* capturing the snapshot at the same time.
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 0cf7261..88545c6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -938,31 +938,31 @@
mNonRtlVisibleOffset = nonRtlVisibleOffset;
}
- public float getScrollAdjustment() {
+ public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
float scrollAdjustment = 0;
- if (mFullscreenProgress > 0) {
+ if (fullscreenEnabled) {
scrollAdjustment += mFullscreenTranslationX + mAccumulatedFullscreenTranslationX;
}
- if (mGridProgress > 0) {
+ if (gridEnabled) {
scrollAdjustment += mGridTranslationX;
}
return scrollAdjustment;
}
- public float getOffsetAdjustment() {
- float offsetAdjustment = getScrollAdjustment();
- if (mGridProgress > 0) {
+ public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+ float offsetAdjustment = getScrollAdjustment(fullscreenEnabled, gridEnabled);
+ if (gridEnabled) {
offsetAdjustment += mGridOffsetTranslationX + mNonRtlVisibleOffset;
}
return offsetAdjustment;
}
- public float getSizeAdjustment() {
+ public float getSizeAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
float sizeAdjustment = 1;
- if (mFullscreenProgress > 0) {
+ if (fullscreenEnabled) {
sizeAdjustment *= mFullscreenScale;
}
- if (mGridProgress > 0) {
+ if (gridEnabled) {
sizeAdjustment *= mGridScale;
}
return sizeAdjustment;
diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png
deleted file mode 100644
index a710932..0000000
--- a/res/drawable-hdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 7cb5214..0000000
--- a/res/drawable-hdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png
deleted file mode 100644
index 252482f..0000000
--- a/res/drawable-mdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png
deleted file mode 100644
index a2010e2..0000000
--- a/res/drawable-mdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png
deleted file mode 100644
index 563c75d..0000000
--- a/res/drawable-xhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 2b1ac05..0000000
--- a/res/drawable-xhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png
deleted file mode 100644
index ea527f4..0000000
--- a/res/drawable-xxhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 5412168..0000000
--- a/res/drawable-xxhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png
deleted file mode 100644
index 4644e9a..0000000
--- a/res/drawable-xxxhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 63cea84..0000000
--- a/res/drawable-xxxhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/widget_resize_frame.xml b/res/drawable/widget_resize_frame.xml
new file mode 100644
index 0000000..d157f5d
--- /dev/null
+++ b/res/drawable/widget_resize_frame.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@android:color/transparent" />
+ <corners android:radius="@android:dimen/system_app_widget_background_radius" />
+ <stroke android:width="2dp" android:color="?android:attr/colorAccent" />
+</shape>
\ No newline at end of file
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 830255b..b1a1efe 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -46,7 +46,7 @@
android:background="?android:attr/colorPrimaryDark"
android:theme="?attr/widgetsTheme">
- <com.android.launcher3.dragndrop.LivePreviewWidgetCell
+ <com.android.launcher3.widget.WidgetCell
android:id="@+id/widget_cell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -59,7 +59,7 @@
<include layout="@layout/widget_cell_content" />
- </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
+ </com.android.launcher3.widget.WidgetCell>
</FrameLayout>
</LinearLayout>
</ScrollView>
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 12561b6..dfce946 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -18,15 +18,20 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/widget_resize_shadow"
- android:foreground="@drawable/widget_resize_frame"
- android:foregroundTint="?attr/workspaceTextColor"
android:padding="0dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
+ <!-- Frame -->
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_margin="@dimen/resize_frame_margin"
+ android:src="@drawable/widget_resize_frame" />
+
<!-- Left -->
<ImageView
android:layout_width="wrap_content"
@@ -34,7 +39,7 @@
android:layout_gravity="left|center_vertical"
android:layout_marginLeft="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
<!-- Top -->
<ImageView
@@ -43,7 +48,7 @@
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
<!-- Right -->
<ImageView
@@ -52,7 +57,7 @@
android:layout_gravity="right|center_vertical"
android:layout_marginRight="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
<!-- Bottom -->
<ImageView
@@ -61,7 +66,7 @@
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
</FrameLayout>
</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/live_preview_widget_cell.xml b/res/layout/live_preview_widget_cell.xml
deleted file mode 100644
index 1e1ce6e..0000000
--- a/res/layout/live_preview_widget_cell.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.dragndrop.LivePreviewWidgetCell
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:paddingHorizontal="@dimen/widget_cell_horizontal_padding"
- android:paddingVertical="@dimen/widget_cell_vertical_padding"
- android:layout_weight="1"
- android:orientation="vertical"
- android:focusable="true"
- android:background="?android:attr/colorPrimaryDark"
- android:gravity="center_horizontal">
-
- <include layout="@layout/widget_cell_content" />
-
-</com.android.launcher3.dragndrop.LivePreviewWidgetCell>
\ No newline at end of file
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 73a5737..55dd1de 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -22,7 +22,6 @@
android:layout_weight="1"
android:orientation="vertical"
android:focusable="true"
- android:background="?android:attr/colorPrimaryDark"
android:gravity="center_horizontal">
<include layout="@layout/widget_cell_content" />
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index 1f401c4..ec07591 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -1,5 +1,4 @@
<resources>
- <bool name="config_largeHeap">true</bool>
<!-- All Apps & Widgets -->
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3b171c6..da43758 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -52,6 +52,7 @@
<!-- App Widget resize frame -->
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
+ <dimen name="resize_frame_margin">22dp</dimen>
<!-- Fast scroll -->
<dimen name="fastscroll_track_min_width">6dp</dimen>
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
new file mode 100644
index 0000000..c738df9
--- /dev/null
+++ b/robolectric_tests/Android.bp
@@ -0,0 +1,46 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//
+// Launcher Robolectric test target.
+//
+// "robolectric_android-all-stub", not needed, we write our own stubs
+android_robolectric_test {
+ name: "LauncherRoboTests",
+ srcs: [
+ "src/**/*.java",
+ ],
+ java_resource_dirs: [
+ "resources",
+ "res",
+ "config",
+ ],
+ static_libs: [
+ "truth-prebuilt",
+ "Launcher3TestCommon",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-robolectric-prebuilt",
+ ],
+ //robolectric_prebuilt_version: "4.4",
+ libs: [
+ "platform-robolectric-4.4-prebuilt",
+ ],
+ instrumentation_for: "Launcher3",
+
+ test_options: {
+ timeout: 36000,
+ },
+}
+
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
deleted file mode 100644
index 405a458..0000000
--- a/robolectric_tests/Android.mk
+++ /dev/null
@@ -1,68 +0,0 @@
-# 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.
-
-#############################################
-# Launcher Robolectric test target. #
-#############################################
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := LauncherRoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-LOCAL_SDK_VERSION := system_current
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, ../tests/src_common)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.runner \
- androidx.test.rules \
- mockito-robolectric-prebuilt \
- truth-prebuilt
-LOCAL_JAVA_LIBRARIES := \
- platform-robolectric-4.3.1-prebuilt
-
-LOCAL_JAVA_RESOURCE_DIRS := resources config
-
-LOCAL_INSTRUMENTATION_FOR := Launcher3
-LOCAL_MODULE_TAGS := optional
-
-# Generate test_config.properties
-include external/robolectric-shadows/gen_test_config.mk
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-############################################
-# Target to run the previous target. #
-############################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := RunLauncherRoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_SDK_VERSION := system_current
-LOCAL_JAVA_LIBRARIES := LauncherRoboTests
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_TEST_PACKAGE := Launcher3
-LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/Launcher3/src
-
-LOCAL_ROBOTEST_TIMEOUT := 36000
-
-include prebuilts/misc/common/robolectric/4.4/run_robotests.mk
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index 936ce12..1b170e1 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1,4 +1,5 @@
sdk=29
+
shadows= \
com.android.launcher3.shadows.LShadowAppPredictionManager \
com.android.launcher3.shadows.LShadowAppWidgetManager \
diff --git a/robolectric_tests/resources/widgets_predication_update_task_data.txt b/robolectric_tests/resources/widgets_predication_update_task_data.txt
new file mode 100644
index 0000000..941d195
--- /dev/null
+++ b/robolectric_tests/resources/widgets_predication_update_task_data.txt
@@ -0,0 +1,24 @@
+# Model data used by WidgetsPredictionUpdateTasksTest
+
+classMap s com.android.launcher3.model.data.WorkspaceItemInfo
+classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
+
+# Items for the BgDataModel
+
+# App shortcuts
+bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
+bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
+bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
+bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
+
+# Promise icons for app3
+bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
+bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
+bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
+
+# Promise icon for app4
+bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
+
+# Widget
+bgItem w providerName=app4/provider1 id=9
+bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/util/SettingsCacheTest.java b/robolectric_tests/unstaged/SettingsCacheTest.java
similarity index 100%
rename from robolectric_tests/src/com/android/launcher3/util/SettingsCacheTest.java
rename to robolectric_tests/unstaged/SettingsCacheTest.java
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index dfdc53c..22c257a 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -193,6 +193,7 @@
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;
public static final int CONTAINER_PREDICTION = -102;
+ public static final int CONTAINER_WIDGETS_PREDICTION = -111;
public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
public static final int CONTAINER_ALL_APPS = -104;
public static final int CONTAINER_WIDGETS_TRAY = -105;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index c6766a4..50f1e44 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -96,8 +96,6 @@
private static final int MIN_FLING_VELOCITY = 250;
private boolean mFreeScroll = false;
- /** If {@code false}, disable swipe gesture to switch between pages. */
- private boolean mSwipeGestureEnabled = true;
protected final int mFlingThresholdVelocity;
protected final int mEasyFlingThresholdVelocity;
@@ -860,14 +858,6 @@
}
/**
- * If {@code enableSwipeGesture} is {@code true}, enables swipe gesture to navigate between
- * pages. Otherwise, disables the navigation gesture.
- */
- public void setSwipeGestureEnabled(boolean swipeGestureEnabled) {
- mSwipeGestureEnabled = swipeGestureEnabled;
- }
-
- /**
* {@inheritDoc}
*/
@Override
@@ -889,8 +879,6 @@
* scrolling there.
*/
- if (!mSwipeGestureEnabled) return false;
-
// Skip touch handling if there are no pages to swipe
if (getChildCount() <= 0) return false;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index a92e1aa..edd9a9f 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -199,7 +199,9 @@
break;
}
}
- rebindAdapters(hasWorkApps);
+ if (!mAH[AdapterHolder.MAIN].appsList.hasFilter()) {
+ rebindAdapters(hasWorkApps);
+ }
if (hasWorkApps) {
resetWorkProfile();
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 5030c5e..bb175ea 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -240,20 +240,18 @@
@Override
public int getSpanSize(int position) {
int viewType = mApps.getAdapterItems().get(position).viewType;
+ int totalSpans = mGridLayoutMgr.getSpanCount();
if (isIconViewType(viewType)) {
- return 1 * SPAN_MULTIPLIER;
+ return totalSpans / mAppsPerRow;
} else if (mSearchAdapterProvider.isSearchView(viewType)) {
- return mSearchAdapterProvider.getGridSpanSize(viewType, mAppsPerRow);
+ return totalSpans / mSearchAdapterProvider.getItemsPerRow(viewType, mAppsPerRow);
} else {
// Section breaks span the full width
- return mAppsPerRow * SPAN_MULTIPLIER;
+ return totalSpans;
}
}
}
- // multiplier to support adapter item column count that is not mAppsPerRow.
- public static final int SPAN_MULTIPLIER = 3;
-
private final BaseDraggingActivity mLauncher;
private final LayoutInflater mLayoutInflater;
private final AlphabeticalAppsList mApps;
@@ -285,14 +283,19 @@
mOnIconClickListener = launcher.getItemOnClickListener();
- setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
-
mSearchAdapterProvider = searchAdapterProvider;
+ setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
}
public void setAppsPerRow(int appsPerRow) {
mAppsPerRow = appsPerRow;
- mGridLayoutMgr.setSpanCount(mAppsPerRow * SPAN_MULTIPLIER);
+ int totalSpans = mAppsPerRow;
+ for (int itemPerRow : mSearchAdapterProvider.getSupportedItemsPerRowArray()) {
+ if (totalSpans % itemPerRow != 0) {
+ totalSpans *= itemPerRow;
+ }
+ }
+ mGridLayoutMgr.setSpanCount(totalSpans);
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index ace9938..179cb77 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,6 +19,9 @@
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
+
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -36,6 +39,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.views.RecyclerViewFastScroller;
import java.util.ArrayList;
@@ -176,6 +180,23 @@
}
@Override
+ public void onScrollStateChanged(int state) {
+ super.onScrollStateChanged(state);
+
+ StatsLogManager mgr = BaseDraggingActivity.fromContext(getContext()).getStatsLogManager();
+ switch (state) {
+ case SCROLL_STATE_DRAGGING:
+ mgr.logger().sendToInteractionJankMonitor(
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
+ break;
+ case SCROLL_STATE_IDLE:
+ mgr.logger().sendToInteractionJankMonitor(
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+ break;
+ }
+ }
+
+ @Override
public boolean onInterceptTouchEvent(MotionEvent e) {
boolean result = super.onInterceptTouchEvent(e);
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
index 9328a3d..f4d735e 100644
--- a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
+++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
@@ -48,7 +48,6 @@
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
List<AllAppsGridAdapter.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
- boolean drawFallbackFocusedView = true;
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
@@ -60,28 +59,12 @@
decorationHandler.extendBounds(view);
if (sectionInfo.isFocusedView()) {
decorationHandler.onFocusDraw(c, view);
- drawFallbackFocusedView = false;
} else {
decorationHandler.onGroupDraw(c);
}
}
}
}
- // fallback logic in case none of the SearchTarget is labeled as focused item
- if (drawFallbackFocusedView) {
- for (int i = 0; i < parent.getChildCount(); i++) {
- View view = parent.getChildAt(i);
- int position = parent.getChildAdapterPosition(view);
- AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
- if (adapterItem.sectionDecorationInfo != null) {
- SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo;
- SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler();
- if (decorationHandler != null) {
- drawDecoration(c, decorationHandler, parent);
- }
- }
- }
- }
}
// Fallback logic in case non of the SearchTarget is labeled as focused item.
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index dc58c99..a48e423 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -144,11 +144,14 @@
*/
public void setProgress(float progress) {
mProgress = progress;
+
mScrimView.setProgress(progress);
float shiftCurrent = progress * mShiftRange;
-
mAppsView.setTranslationY(shiftCurrent);
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mSearchImeEnabled) {
+ if (mInsetController == null) {
+ setupInsetTransitionController();
+ }
mInsetController.setProgress(progress);
}
}
@@ -240,13 +243,17 @@
mAppsView = appsView;
mScrimView = scrimView;
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()) {
- mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView);
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ setupInsetTransitionController();
}
}
+ private void setupInsetTransitionController() {
+ mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView);
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+
/**
* Updates the total scroll range but does not update the UI.
*/
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
index a79ec43..fdacd3d 100644
--- a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -51,10 +51,17 @@
ViewGroup parent, int viewType);
/**
+ * Returns supported item per row combinations supported
+ */
+ public int[] getSupportedItemsPerRowArray() {
+ return new int[]{};
+ }
+
+ /**
* Returns how many cells a view should span
*/
- public int getGridSpanSize(int viewType, int appsPerRow) {
- return appsPerRow * AllAppsGridAdapter.SPAN_MULTIPLIER;
+ public int getItemsPerRow(int viewType, int appsPerRow) {
+ return appsPerRow;
}
/**
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 5dc94ff..c972cbb 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -58,6 +58,7 @@
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -78,7 +79,7 @@
private LauncherAppState mApp;
private InvariantDeviceProfile mIdp;
- private LivePreviewWidgetCell mWidgetCell;
+ private WidgetCell mWidgetCell;
// Widget request specific options.
private LauncherAppWidgetHost mAppWidgetHost;
@@ -117,8 +118,9 @@
}
}
- mWidgetCell.setOnTouchListener(this);
- mWidgetCell.setOnLongClickListener(this);
+ WidgetImageView preview = mWidgetCell.findViewById(R.id.widget_preview);
+ preview.setOnTouchListener(this);
+ preview.setOnLongClickListener(this);
// savedInstanceState is null when the activity is created the first time (i.e., avoids
// duplicate logging during rotation)
diff --git a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
deleted file mode 100644
index fa062f5..0000000
--- a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.android.launcher3.dragndrop;
-
-import static com.android.launcher3.Utilities.ATLEAST_S;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.RemoteViews;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.icons.BitmapRenderer;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.widget.WidgetCell;
-
-/**
- * Extension of {@link WidgetCell} which supports generating previews from {@link RemoteViews}
- */
-public class LivePreviewWidgetCell extends WidgetCell {
-
- private RemoteViews mPreview;
-
- public LivePreviewWidgetCell(Context context) {
- this(context, null);
- }
-
- public LivePreviewWidgetCell(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public LivePreviewWidgetCell(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setPreview(RemoteViews view) {
- mPreview = view;
- }
-
- public RemoteViews getPreview() {
- return mPreview;
- }
-
- /** Resets any resource. This should be called before recycling this view. */
- public void reset() {
- mPreview = null;
- }
-
- @Override
- public void ensurePreview() {
- if (mPreview != null && mActiveRequest == null) {
- Bitmap preview = generateFromRemoteViews(
- mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]);
- if (preview != null) {
- applyPreview(preview);
- return;
- }
- }
- super.ensurePreview();
- }
-
- @Override
- public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
- if (ATLEAST_S
- && mPreview == null
- && item.widgetInfo != null
- && item.widgetInfo.previewLayout != Resources.ID_NULL) {
- mPreview = new RemoteViews(item.widgetInfo.provider.getPackageName(),
- item.widgetInfo.previewLayout);
- }
-
- super.applyFromCellItem(item, loader);
- }
-
- /**
- * Generates a bitmap by inflating {@param views}.
- * @see com.android.launcher3.WidgetPreviewLoader#generateWidgetPreview
- *
- * TODO: Consider moving this to the background thread.
- */
- public static Bitmap generateFromRemoteViews(BaseActivity activity, RemoteViews views,
- LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
-
- DeviceProfile dp = activity.getDeviceProfile();
- int viewWidth = dp.cellWidthPx * info.spanX;
- int viewHeight = dp.cellHeightPx * info.spanY;
-
- final View v;
- try {
- v = views.apply(activity, new FrameLayout(activity));
- v.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
-
- viewWidth = v.getMeasuredWidth();
- viewHeight = v.getMeasuredHeight();
- v.layout(0, 0, viewWidth, viewHeight);
- } catch (Exception e) {
- return null;
- }
-
- preScaledWidthOut[0] = viewWidth;
- final int bitmapWidth, bitmapHeight;
- final float scale;
- if (viewWidth > previewSize) {
- scale = ((float) previewSize) / viewWidth;
- bitmapWidth = previewSize;
- bitmapHeight = (int) (viewHeight * scale);
- } else {
- scale = 1;
- bitmapWidth = viewWidth;
- bitmapHeight = viewHeight;
- }
-
- return BitmapRenderer.createSoftwareBitmap(bitmapWidth, bitmapHeight, c -> {
- c.scale(scale, scale);
- v.draw(c);
- });
- }
-}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index d554bb9..c5ffff9 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
import android.content.Context;
+import android.view.View;
import androidx.annotation.Nullable;
import androidx.slice.SliceItem;
@@ -407,6 +408,12 @@
@UiEvent(doc = "User switched to AllApps Work tab by tapping on it.")
LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB(722),
+
+ @UiEvent(doc = "All apps vertical fling started.")
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN(724),
+
+ @UiEvent(doc = "All apps vertical fling ended.")
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END(725)
;
// ADD MORE
@@ -525,6 +532,13 @@
*/
default void log(EventEnum event) {
}
+
+ /**
+ * Builds the final message and logs it to two different atoms, one for
+ * event tracking and the other for jank tracking.
+ */
+ default void sendToInteractionJankMonitor(EventEnum event, View v) {
+ }
}
/**
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 543ee2a..ad553d5 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -124,7 +124,7 @@
public void bindUpdatedWidgets(BgDataModel dataModel) {
final ArrayList<WidgetsListBaseEntry> widgets =
- dataModel.widgetsModel.getWidgetsList(mApp.getContext());
+ dataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 1853632..7780894 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -58,8 +58,11 @@
private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
/** Maps packages to their DotInfo's . */
private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
- /** Maps packages to their Widgets */
+
+ /** All installed widgets. */
private List<WidgetsListBaseEntry> mAllWidgets = List.of();
+ /** Widgets that can be recommended to the users. */
+ private List<ItemInfo> mRecommendedWidgets = List.of();
private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
@@ -187,6 +190,15 @@
notificationListener.cancelNotificationFromLauncher(notificationKey);
}
+ /**
+ * Sets a list of recommended widgets ordered by their order of appearance in the widgets
+ * recommendation UI.
+ */
+ public void setRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
+ mRecommendedWidgets = recommendedWidgets;
+ mChangeListener.onRecommendedWidgetsBound();
+ }
+
public void setAllWidgets(List<WidgetsListBaseEntry> allWidgets) {
mAllWidgets = allWidgets;
mChangeListener.onWidgetsBound();
@@ -200,6 +212,21 @@
return mAllWidgets;
}
+ /** Returns a list of recommended widgets. */
+ public List<WidgetItem> getRecommendedWidgets() {
+ HashMap<ComponentKey, WidgetItem> allWidgetItems = new HashMap<>();
+ mAllWidgets.stream()
+ .filter(entry -> entry instanceof WidgetsListContentEntry)
+ .forEach(entry -> ((WidgetsListContentEntry) entry).mWidgets
+ .forEach(widget -> allWidgetItems.put(
+ new ComponentKey(widget.componentName, widget.user), widget)));
+ return mRecommendedWidgets.stream()
+ .map(recommendedWidget -> allWidgetItems.get(
+ new ComponentKey(recommendedWidget.getTargetComponent(),
+ recommendedWidget.user)))
+ .collect(Collectors.toList());
+ }
+
public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
return mAllWidgets.stream()
.filter(row -> row instanceof WidgetsListContentEntry
@@ -244,5 +271,8 @@
default void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) { }
default void onWidgetsBound() { }
+
+ /** A callback to get notified when recommended widgets are bound. */
+ default void onRecommendedWidgetsBound() { }
}
}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 15566a4..03c58bb 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -30,7 +30,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
@@ -86,6 +85,8 @@
if (v instanceof WidgetCell) {
return beginDraggingWidget((WidgetCell) v);
+ } else if (v.getParent() instanceof WidgetCell) {
+ return beginDraggingWidget((WidgetCell) v.getParent());
}
return true;
}
@@ -101,9 +102,7 @@
}
PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
- if (v instanceof LivePreviewWidgetCell) {
- dragHelper.setPreview(((LivePreviewWidgetCell) v).getPreview());
- }
+ dragHelper.setPreview(v.getPreview());
int[] loc = new int[2];
getPopupContainer().getLocationInDragLayer(image, loc);
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 8fe42f4..6e83836 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -35,7 +35,6 @@
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
-import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -92,7 +91,7 @@
int[] previewSizeBeforeScale = new int[1];
if (mPreview != null) {
- preview = LivePreviewWidgetCell.generateFromRemoteViews(launcher, mPreview,
+ preview = WidgetCell.generateFromRemoteViews(launcher, mPreview,
createWidgetInfo.info, maxWidth, previewSizeBeforeScale);
}
if (preview == null) {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 8c315fd..229df50 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -18,7 +18,9 @@
import static com.android.launcher3.Utilities.ATLEAST_S;
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.CancellationSignal;
import android.util.AttributeSet;
@@ -28,7 +30,9 @@
import android.view.View.OnLayoutChangeListener;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.launcher3.BaseActivity;
@@ -37,6 +41,7 @@
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.model.WidgetItem;
/**
@@ -61,8 +66,8 @@
/** Widget preview width is calculated by multiplying this factor to the widget cell width. */
private static final float PREVIEW_SCALE = 0.8f;
- private int mPreviewWidth;
- private int mPreviewHeight;
+ protected int mPreviewWidth;
+ protected int mPreviewHeight;
protected int mPresetPreviewSize;
private int mCellSize;
@@ -85,6 +90,9 @@
protected final DeviceProfile mDeviceProfile;
private final CheckLongPressHelper mLongPressHelper;
+ private RemoteViews mPreview;
+ private AppWidgetHostView mPreviewAppWidgetHostView;
+
public WidgetCell(Context context) {
this(context, null);
}
@@ -123,6 +131,14 @@
mWidgetDescription = findViewById(R.id.widget_description);
}
+ public void setPreview(RemoteViews view) {
+ mPreview = view;
+ }
+
+ public RemoteViews getPreview() {
+ return mPreview;
+ }
+
/**
* Called to clear the view and free attached resources. (e.g., {@link Bitmap}
*/
@@ -142,9 +158,13 @@
mActiveRequest.cancel();
mActiveRequest = null;
}
+ mPreview = null;
+ mPreviewAppWidgetHostView = null;
}
public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
+ applyPreviewLayout(item);
+
mItem = item;
mWidgetName.setText(mItem.label);
mWidgetDims.setText(getContext().getString(R.string.widget_dims_format,
@@ -169,6 +189,27 @@
}
}
+ private void applyPreviewLayout(WidgetItem item) {
+ if (ATLEAST_S
+ && mPreview == null
+ && item.widgetInfo != null
+ && item.widgetInfo.previewLayout != Resources.ID_NULL) {
+ mPreviewAppWidgetHostView = new AppWidgetHostView(getContext());
+ LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
+ LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(),
+ item.widgetInfo.clone());
+ // A hack to force the initial layout to be the preview layout since there is no API for
+ // rendering a preview layout for work profile apps yet. For non-work profile layout, a
+ // proper solution is to use RemoteViews(PackageName, LayoutId).
+ launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;
+ mPreviewAppWidgetHostView.setAppWidget(/* appWidgetId= */ -1,
+ launcherAppWidgetProviderInfo);
+ mPreviewAppWidgetHostView.setPadding(/* left= */ 0, /* top= */0, /* right= */
+ 0, /* bottom= */ 0);
+ mPreviewAppWidgetHostView.updateAppWidget(/* remoteViews= */ null);
+ }
+ }
+
public WidgetImageView getWidgetView() {
return mWidgetImage;
}
@@ -217,6 +258,23 @@
}
public void ensurePreview() {
+ if (mPreview != null && mActiveRequest == null) {
+ Bitmap preview = generateFromRemoteViews(
+ mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]);
+ if (preview != null) {
+ applyPreview(preview);
+ return;
+ }
+ }
+
+ if (mPreviewAppWidgetHostView != null) {
+ Bitmap preview = generateFromView(mActivity, mPreviewAppWidgetHostView,
+ mItem.widgetInfo, mPreviewWidth, new int[1]);
+ if (preview != null) {
+ applyPreview(preview);
+ return;
+ }
+ }
if (mActiveRequest != null) {
return;
}
@@ -273,4 +331,53 @@
super.onInitializeAccessibilityNodeInfo(info);
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
}
+
+ /**
+ * Generates a bitmap by inflating {@param views}.
+ * @see com.android.launcher3.WidgetPreviewLoader#generateWidgetPreview
+ *
+ * TODO: Consider moving this to the background thread.
+ */
+ public static Bitmap generateFromRemoteViews(BaseActivity activity, RemoteViews views,
+ LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
+ try {
+ return generateFromView(activity, views.apply(activity, new FrameLayout(activity)),
+ info, previewSize, preScaledWidthOut);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static Bitmap generateFromView(BaseActivity activity, View v,
+ LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
+
+ DeviceProfile dp = activity.getDeviceProfile();
+ int viewWidth = dp.cellWidthPx * info.spanX;
+ int viewHeight = dp.cellHeightPx * info.spanY;
+
+ v.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
+
+ viewWidth = v.getMeasuredWidth();
+ viewHeight = v.getMeasuredHeight();
+ v.layout(0, 0, viewWidth, viewHeight);
+
+ preScaledWidthOut[0] = viewWidth;
+ final int bitmapWidth, bitmapHeight;
+ final float scale;
+ if (viewWidth > previewSize) {
+ scale = ((float) previewSize) / viewWidth;
+ bitmapWidth = previewSize;
+ bitmapHeight = (int) (viewHeight * scale);
+ } else {
+ scale = 1;
+ bitmapWidth = viewWidth;
+ bitmapHeight = viewHeight;
+ }
+
+ return BitmapRenderer.createSoftwareBitmap(bitmapWidth, bitmapHeight, c -> {
+ c.scale(scale, scale);
+ v.draw(c);
+ });
+ }
}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 6abbf21..e6d54a9 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -24,6 +24,7 @@
import android.util.AttributeSet;
import android.util.IntProperty;
import android.util.Pair;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -38,7 +39,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.PackageUserKey;
@@ -140,6 +140,7 @@
WidgetsTableUtils.groupWidgetItemsIntoTable(widgets, mMaxHorizontalSpan).forEach(row -> {
TableRow tableRow = new TableRow(getContext());
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.setPreviewSize(widgetItem.spanX, widgetItem.spanY);
@@ -153,11 +154,12 @@
}
protected WidgetCell addItemCell(ViewGroup parent) {
- LivePreviewWidgetCell widget = (LivePreviewWidgetCell) LayoutInflater.from(
- getContext()).inflate(R.layout.live_preview_widget_cell, parent, false);
+ WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
+ .inflate(R.layout.widget_cell, parent, false);
- widget.setOnClickListener(this);
- widget.setOnLongClickListener(this);
+ WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+ preview.setOnClickListener(this);
+ preview.setOnLongClickListener(this);
widget.setAnimatePreview(false);
parent.addView(widget);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 52a2fc5..bf9b849 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -110,9 +110,6 @@
RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
if (mHasWorkProfile) {
mViewPager = findViewById(R.id.widgets_view_pager);
- // Temporarily disable swipe gesture until widgets list horizontal scrollviews per
- // app are replaced by gird views.
- mViewPager.setSwipeGestureEnabled(false);
mViewPager.initParentViews(this);
mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 9c8684a..47fa71a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.util.Log;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -30,6 +31,7 @@
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -144,6 +146,7 @@
tableRow = (TableRow) table.getChildAt(i);
} else {
tableRow = new TableRow(table.getContext());
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
table.addView(tableRow);
}
if (tableRow.getChildCount() > widgetItems.size()) {
@@ -155,8 +158,9 @@
WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
R.layout.widget_cell, tableRow, false);
// set up touch.
- widget.setOnClickListener(mIconClickListener);
- widget.setOnLongClickListener(mIconLongClickListener);
+ WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+ preview.setOnClickListener(mIconClickListener);
+ preview.setOnLongClickListener(mIconLongClickListener);
tableRow.addView(widget);
}
}
diff --git a/src_build_config/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
similarity index 100%
rename from src_build_config/BuildConfig.java
rename to src_build_config/com/android/launcher3/BuildConfig.java
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index 73b1601..abce2a2 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -48,7 +48,7 @@
@Override
public void bindWidgets() {
final List<WidgetsListBaseEntry> widgets =
- mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
+ mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
}
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index a8c7c48..3ea4766 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -5,6 +5,8 @@
import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
+import static java.util.stream.Collectors.toList;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -67,7 +69,7 @@
*
* @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
*/
- public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsList(Context context) {
+ public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
@@ -82,6 +84,22 @@
return result;
}
+ /** Returns a mapping of packages to their widgets without static shortcuts. */
+ public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+ Map<PackageUserKey, List<WidgetItem>> packagesToWidgets = new HashMap<>();
+ mWidgetsList.forEach((packageItemInfo, widgetsAndShortcuts) -> {
+ List<WidgetItem> widgets = widgetsAndShortcuts.stream()
+ .filter(item -> item.widgetInfo != null)
+ .collect(toList());
+ if (widgets.size() > 0) {
+ packagesToWidgets.put(
+ new PackageUserKey(packageItemInfo.packageName, packageItemInfo.user),
+ widgets);
+ }
+ });
+ return packagesToWidgets;
+ }
+
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 63220ad..a6c7c03 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+import static com.android.launcher3.tapl.LauncherInstrumentation.LONG_WAIT_TIME_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -140,9 +141,13 @@
WorkEduView.KEY_WORK_EDU_STEP).remove(
WorkEduView.KEY_LEGACY_WORK_EDU_SEEN).commit());
- waitForLauncherCondition("Work tab not setup",
- launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
- 60000);
+ waitForLauncherCondition("Work tab not setup", launcher -> {
+ if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
+ launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
+ return true;
+ }
+ return false;
+ }, LONG_WAIT_TIME_MS);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
WorkEduView workEduView = getEduView();
@@ -153,16 +158,6 @@
workEduView.findViewById(R.id.proceed).callOnClick();
});
- executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
- "work profile status (" + mProfileUserId + ") :"
- + launcher.getAppsView().isWorkTabVisible()));
-
-
- executeOnLauncher(launcher -> {
- launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
- Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Defer all apps update");
- });
-
AtomicInteger attempt = new AtomicInteger(0);
// verify work edu is seen next
waitForLauncherCondition("Launcher did not show the next edu screen", l -> {
@@ -178,7 +173,7 @@
}
return ((TextView) workEduView.findViewById(R.id.content_text)).getText().equals(
l.getResources().getString(R.string.work_profile_edu_work_apps));
- }, 60000);
+ });
}
@Test