diff --git a/Android.mk b/Android.mk index 752b530ab0..25f5412d21 100644 --- a/Android.mk +++ b/Android.mk @@ -28,6 +28,7 @@ LOCAL_STATIC_ANDROID_LIBRARIES := \ androidx.recyclerview_recyclerview \ androidx.dynamicanimation_dynamicanimation \ androidx.preference_preference \ + androidx.slice_slice-view \ iconloader_base LOCAL_STATIC_JAVA_LIBRARIES := \ diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml index 397ea82489..8f4ce4343b 100644 --- a/quickstep/res/values/override.xml +++ b/quickstep/res/values/override.xml @@ -27,8 +27,6 @@ com.android.quickstep.logging.UserEventDispatcherExtension - com.android.launcher3.hybridhotseat.HotseatPredictionModel - com.android.launcher3.model.QuickstepModelDelegate diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index a0016cb905..68111d2632 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -26,12 +26,10 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Intent; import android.content.IntentSender; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.CancellationSignal; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.proxy.ProxyActivityStarter; @@ -40,14 +38,12 @@ import com.android.launcher3.statehandlers.BackButtonAlphaHandler; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statemanager.StateManager.StateHandler; import com.android.launcher3.uioverrides.RecentsViewStateController; -import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.RecentsModel; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SystemUiProxy; -import com.android.quickstep.util.QuickstepOnboardingPrefs; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.views.OverviewActionsView; @@ -73,7 +69,6 @@ public abstract class BaseQuickstepLauncher extends Launcher Float.intBitsToFloat(arg1), arg2 != 0); private OverviewActionsView mActionsView; - protected HotseatPredictionController mHotseatPredictionController; @Override protected void onCreate(Bundle savedInstanceState) { @@ -221,11 +216,6 @@ public abstract class BaseQuickstepLauncher extends Launcher return mDepthController; } - @Override - protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) { - return new QuickstepOnboardingPrefs(this, sharedPrefs); - } - @Override public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) { QuickstepAppTransitionManagerImpl appTransitionManager = @@ -314,13 +304,6 @@ public abstract class BaseQuickstepLauncher extends Launcher Stream.of(WellbeingModel.SHORTCUT_FACTORY)); } - /** - * Returns Prediction controller for hybrid hotseat - */ - public HotseatPredictionController getHotseatPredictionController() { - return mHotseatPredictionController; - } - public void setHintUserWillBeActive() { addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE); } diff --git a/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java index 8477b103a2..98bf483a3d 100644 --- a/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java +++ b/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java @@ -19,7 +19,6 @@ package com.android.launcher3.appprediction; import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE; import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP; import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS; import android.os.UserManager; @@ -31,7 +30,6 @@ import com.android.launcher3.Utilities; import com.android.launcher3.allapps.FloatingHeaderView; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.views.ArrowTipView; -import com.android.systemui.shared.system.LauncherEventUtil; /** * ArrowTip helper aligned just above prediction apps, shown to users that enter all apps for the @@ -57,8 +55,7 @@ public class AllAppsTipView { floatingHeaderView.findFixedRowByType(PredictionRowView.class).getLocationOnScreen(coords); ArrowTipView arrowTipView = new ArrowTipView(launcher).setOnClosedCallback(() -> { launcher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply(); - launcher.getUserEventDispatcher().logActionTip(LauncherEventUtil.DISMISS, - ALL_APPS_PREDICTION_TIPS); + // TODO: add log to WW }); arrowTipView.show(launcher.getString(R.string.all_apps_prediction_tip), coords[1]); diff --git a/quickstep/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/src/com/android/launcher3/appprediction/ComponentKeyMapper.java deleted file mode 100644 index d200868899..0000000000 --- a/quickstep/src/com/android/launcher3/appprediction/ComponentKeyMapper.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * 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 com.android.launcher3.allapps.AllAppsStore; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.ItemInfoWithIcon; -import com.android.launcher3.util.ComponentKey; - -public class ComponentKeyMapper { - - protected final ComponentKey componentKey; - private final DynamicItemCache mCache; - - public ComponentKeyMapper(ComponentKey key, DynamicItemCache cache) { - 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 { - return mCache.getShortcutInfo(componentKey); - } - } -} diff --git a/quickstep/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/src/com/android/launcher3/appprediction/DynamicItemCache.java deleted file mode 100644 index ab96b1340a..0000000000 --- a/quickstep/src/com/android/launcher3/appprediction/DynamicItemCache.java +++ /dev/null @@ -1,268 +0,0 @@ -/** - * 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 static com.android.launcher3.util.Executors.MODEL_EXECUTOR; -import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER; - -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 androidx.annotation.MainThread; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; - -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.allapps.AllAppsStore; -import com.android.launcher3.icons.IconCache; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.shortcuts.ShortcutKey; -import com.android.launcher3.shortcuts.ShortcutRequest; -import com.android.launcher3.util.ComponentKey; -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; - -/** - * 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 IconCache mIconCache; - - private final Map mShortcuts; - private final Map mInstantApps; - - public DynamicItemCache(Context context, Runnable onUpdateCallback) { - mContext = context; - mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage); - mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage); - mInstantAppResolver = InstantAppResolver.newInstance(context); - mOnUpdateCallback = onUpdateCallback; - mIconCache = LauncherAppState.getInstance(mContext).getIconCache(); - - mShortcuts = new HashMap<>(); - mInstantApps = new HashMap<>(); - } - - public void cacheItems(List shortcutKeys, List 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 shortcutKeys = msg.obj != null ? - (List) msg.obj : Collections.EMPTY_LIST; - Map 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 pkgNames = msg.obj != null ? - (List) msg.obj : Collections.EMPTY_LIST; - List 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) msg.obj); - mOnUpdateCallback.run(); - return true; - } - case UI_MSG_UPDATE_INSTANT_APPS: { - List instantAppItemInfos = (List) 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) { - List details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL); - if (!details.isEmpty()) { - WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext); - mIconCache.getShortcutIcon(si, details.get(0)); - 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.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, 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 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(ComponentKey key) { - return mShortcuts.get(key); - } - - /** - * requests and caches icons for app targets - */ - public void updateDependencies(List componentKeyMappers, - AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) { - List instantAppsToLoad = new ArrayList<>(); - List shortcutsToLoad = new ArrayList<>(); - int total = componentKeyMappers.size(); - for (int i = 0, count = 0; i < total && count < itemCount; i++) { - ComponentKeyMapper mapper = componentKeyMappers.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(appsStore); - if (info != null) { - if (info.usingLowResIcon()) { - mIconCache.updateIconInBackground(callback, info); - } - count++; - } - } - } - cacheItems(shortcutsToLoad, instantAppsToLoad); - } -} diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java index 8ebf1251ec..4451e7a6c1 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java @@ -47,35 +47,27 @@ import java.util.stream.IntStream; */ public class HotseatEduController { - public static final String HOTSEAT_EDU_ACTION = - "com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"; public static final String SETTINGS_ACTION = "android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS"; private final Launcher mLauncher; private final Hotseat mHotseat; - private HotseatRestoreHelper mRestoreHelper; private List mPredictedApps; private HotseatEduDialog mActiveDialog; private ArrayList mNewItems = new ArrayList<>(); private IntArray mNewScreens = null; - private Runnable mOnOnboardingComplete; - HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) { + HotseatEduController(Launcher launcher) { mLauncher = launcher; mHotseat = launcher.getHotseat(); - mRestoreHelper = restoreHelper; - mOnOnboardingComplete = runnable; } /** * Checks what type of migration should be used and migrates hotseat */ void migrate() { - if (mRestoreHelper != null) { - mRestoreHelper.createBackup(); - } + HotseatRestoreHelper.createBackup(mLauncher); if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) { migrateToFolder(); } else { @@ -227,7 +219,7 @@ public class HotseatEduController { } void finishOnboarding() { - mOnOnboardingComplete.run(); + mLauncher.getModel().onWorkspaceUiChanged(); } void showDimissTip() { diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index 4b8e434d3b..c3677ea41e 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -40,7 +40,6 @@ import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.uioverrides.PredictedAppIcon; -import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.AbstractSlideInView; import java.util.List; @@ -121,16 +120,6 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable handleClose(true); } - @Override - public void logActionCommand(int command) { - // Since this is on-boarding popup, it is not a user controlled action. - } - - @Override - public int getLogContainerType() { - return LauncherLogProto.ContainerType.TIP; - } - @Override protected boolean isOfType(int type) { return (type & TYPE_ON_BOARD_POPUP) != 0; diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java index b94e6337d0..151a113d1e 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java @@ -15,23 +15,17 @@ */ package com.android.launcher3.hybridhotseat; -import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -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.content.ComponentName; import android.os.Process; -import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.View; import android.view.ViewGroup; @@ -44,77 +38,51 @@ import com.android.launcher3.DropTarget; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.anim.AnimationSuccessListener; -import com.android.launcher3.appprediction.ComponentKeyMapper; -import com.android.launcher3.appprediction.DynamicItemCache; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.icons.IconCache; +import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.logger.LauncherAtom.ContainerInfo; import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer; import com.android.launcher3.logging.InstanceId; -import com.android.launcher3.model.data.AppInfo; -import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; -import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.userevent.nano.LauncherLogProto; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.IntArray; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.views.ArrowTipView; import com.android.launcher3.views.Snackbar; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.OptionalInt; -import java.util.stream.IntStream; +import java.util.stream.Collectors; /** * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows * pinning of predicted apps and manages replacement of predicted apps with user drag. */ public class HotseatPredictionController implements DragController.DragListener, - View.OnAttachStateChangeListener, SystemShortcut.Factory, - InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener, - IconCache.ItemInfoUpdateReceiver, DragSource { + SystemShortcut.Factory, InvariantDeviceProfile.OnIDPChangeListener, + DragSource { - private static final String TAG = "PredictiveHotseat"; - private static final boolean DEBUG = false; - - private static final String PREDICTION_CLIENT = "hotseat"; - private DropTarget.DragObject mDragObject; private int mHotSeatItemsCount; - private int mPredictedSpotsCount = 0; private Launcher mLauncher; private final Hotseat mHotseat; - private final HotseatRestoreHelper mRestoreHelper; + private List mPredictedItems = Collections.emptyList(); - private List mComponentKeyMappers = new ArrayList<>(); - - private DynamicItemCache mDynamicItemCache; - - private final HotseatPredictionModel mPredictionModel; - private AppPredictor mAppPredictor; - private AllAppsStore mAllAppsStore; private AnimatorSet mIconRemoveAnimators; private boolean mUIUpdatePaused = false; - private boolean mIsDestroyed = false; - + private boolean mDragInProgress = false; private List mOutlineDrawings = new ArrayList<>(); @@ -130,26 +98,23 @@ public class HotseatPredictionController implements DragController.DragListener, mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); return true; } + // Start the drag - mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions()); + // Use a new itemInfo so that the original predicted item is stable + WorkspaceItemInfo dragItem = new WorkspaceItemInfo((WorkspaceItemInfo) v.getTag()); + v.setVisibility(View.INVISIBLE); + mLauncher.getWorkspace().beginDragShared( + v, null, this, dragItem, new DragPreviewProvider(v), new DragOptions()); return true; }; public HotseatPredictionController(Launcher launcher) { mLauncher = launcher; mHotseat = launcher.getHotseat(); - mAllAppsStore = mLauncher.getAppsView().getAppsStore(); - LauncherAppState appState = LauncherAppState.getInstance(launcher); - mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel(); - mAllAppsStore.addUpdateListener(this); - mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction); mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons; + mLauncher.getDragController().addDragListener(this); + launcher.getDeviceProfile().inv.addOnChangeListener(this); - mHotseat.addOnAttachStateChangeListener(this); - mRestoreHelper = new HotseatRestoreHelper(mLauncher); - if (mHotseat.isAttachedToWindow()) { - onViewAttachedToWindow(mHotseat); - } } /** @@ -157,7 +122,7 @@ public class HotseatPredictionController implements DragController.DragListener, */ public void showEdu() { mLauncher.getStateManager().goToState(NORMAL, true, () -> { - if (mComponentKeyMappers.isEmpty()) { + if (mPredictedItems.isEmpty()) { // launcher has empty predictions set Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled, R.string.hotseat_prediction_settings, null, @@ -165,10 +130,10 @@ public class HotseatPredictionController implements DragController.DragListener, } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) { showDiscoveryTip(); } else { - HotseatEduController eduController = new HotseatEduController(mLauncher, - mRestoreHelper, - this::createPredictor); - eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers)); + HotseatEduController eduController = new HotseatEduController(mLauncher); + eduController.setPredictedApps(mPredictedItems.stream() + .map(i -> (WorkspaceItemInfo) i) + .collect(Collectors.toList())); eduController.showEdu(); } }); @@ -192,17 +157,7 @@ public class HotseatPredictionController implements DragController.DragListener, * Returns if hotseat client has predictions */ public boolean hasPredictions() { - return !mComponentKeyMappers.isEmpty(); - } - - @Override - public void onViewAttachedToWindow(View view) { - mLauncher.getDragController().addDragListener(this); - } - - @Override - public void onViewDetachedFromWindow(View view) { - mLauncher.getDragController().removeDragListener(this); + return !mPredictedItems.isEmpty(); } private void fillGapsWithPrediction() { @@ -210,15 +165,10 @@ public class HotseatPredictionController implements DragController.DragListener, } private void fillGapsWithPrediction(boolean animate, Runnable callback) { - if (mUIUpdatePaused || mDragObject != null) { - return; - } - List predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers); - if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) { - // Safely ignore update as AppsList is not ready yet. This will called again once - // apps are ready (HotseatPredictionController#onAppsUpdated) + if (mUIUpdatePaused || mDragInProgress) { return; } + int predictionIndex = 0; ArrayList newItems = new ArrayList<>(); // make sure predicted icon removal and filling predictions don't step on each other @@ -240,14 +190,15 @@ public class HotseatPredictionController implements DragController.DragListener, if (child != null && !isPredictedIcon(child)) { continue; } - if (predictedApps.size() <= predictionIndex) { + if (mPredictedItems.size() <= predictionIndex) { // Remove predicted apps from the past if (isPredictedIcon(child)) { mHotseat.removeView(child); } continue; } - WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++); + WorkspaceItemInfo predictedItem = + (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++); if (isPredictedIcon(child) && child.isEnabled()) { PredictedAppIcon icon = (PredictedAppIcon) child; icon.applyFromWorkspaceItem(predictedItem); @@ -257,7 +208,6 @@ public class HotseatPredictionController implements DragController.DragListener, } preparePredictionInfo(predictedItem, rank); } - mPredictedSpotsCount = predictionIndex; bindItems(newItems, animate, callback); } @@ -285,13 +235,7 @@ public class HotseatPredictionController implements DragController.DragListener, * Unregisters callbacks and frees resources */ public void destroy() { - mIsDestroyed = true; - mAllAppsStore.removeUpdateListener(this); mLauncher.getDeviceProfile().inv.removeOnChangeListener(this); - mHotseat.removeOnAttachStateChangeListener(this); - if (mAppPredictor != null) { - mAppPredictor.destroy(); - } } /** @@ -305,97 +249,14 @@ public class HotseatPredictionController implements DragController.DragListener, } /** - * Creates App Predictor with all the current apps pinned on the hotseat + * Sets or updates the predicted items */ - public void createPredictor() { - AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class); - if (apm == null) { - return; + public void setPredictedItems(FixedContainerItems items) { + mPredictedItems = items.items; + if (mPredictedItems.isEmpty()) { + HotseatRestoreHelper.restoreBackup(mLauncher); } - if (mAppPredictor != null) { - mAppPredictor.destroy(); - mAppPredictor = null; - } - WeakReference controllerRef = new WeakReference<>(this); - - - mPredictionModel.createBundle(bundle -> { - if (mIsDestroyed) return; - mAppPredictor = apm.createAppPredictionSession( - new AppPredictionContext.Builder(mLauncher) - .setUiSurface(PREDICTION_CLIENT) - .setPredictedTargetCount(mHotSeatItemsCount) - .setExtras(bundle) - .build()); - mAppPredictor.registerPredictionUpdates( - mLauncher.getApplicationContext().getMainExecutor(), - list -> { - if (controllerRef.get() != null) { - controllerRef.get().setPredictedApps(list); - } - }); - mAppPredictor.requestPredictionUpdate(); - }); - setPauseUIUpdate(false); - } - - /** - * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items. - */ - public void showCachedItems(List apps, IntArray ranks) { - if (hasPredictions() && mAppPredictor != null) { - mAppPredictor.requestPredictionUpdate(); - fillGapsWithPrediction(); - return; - } - int count = Math.min(ranks.size(), apps.size()); - List items = new ArrayList<>(count); - for (int i = 0; i < count; i++) { - WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i)); - ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user); - preparePredictionInfo(item, ranks.get(i)); - items.add(item); - - mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache)); - } - updateDependencies(); - bindItems(items, false, null); - } - - private void setPredictedApps(List appTargets) { - mComponentKeyMappers.clear(); - if (appTargets.isEmpty()) { - mRestoreHelper.restoreBackup(); - } - StringBuilder predictionLog = new StringBuilder("predictedApps: [\n"); - ArrayList componentKeys = new ArrayList<>(); - 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()); - } - componentKeys.add(key); - predictionLog.append(key.toString()); - predictionLog.append(",rank:"); - predictionLog.append(appTarget.getRank()); - predictionLog.append("\n"); - mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache)); - } - predictionLog.append("]"); - if (Utilities.IS_DEBUG_DEVICE) { - HotseatFileLog.INSTANCE.get(mLauncher).log(TAG, predictionLog.toString()); - } - updateDependencies(); fillGapsWithPrediction(); - mPredictionModel.cachePredictionComponentKeys(componentKeys); - } - - private void updateDependencies() { - mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this, - mHotSeatItemsCount); } /** @@ -414,42 +275,9 @@ public class HotseatPredictionController implements DragController.DragListener, workspaceItemInfo.cellX, workspaceItemInfo.cellY); ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start(); icon.pin(workspaceItemInfo); - AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo); - if (appTarget != null) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget, - AppTargetEvent.ACTION_PIN, workspaceItemInfo)); - } - } - - private List mapToWorkspaceItemInfo( - List components) { - AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore(); - if (allAppsStore.getApps().length == 0) { - return Collections.emptyList(); - } - - List predictedApps = new ArrayList<>(); - for (ComponentKeyMapper mapper : components) { - ItemInfoWithIcon info = mapper.getApp(allAppsStore); - if (info instanceof AppInfo) { - WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info); - predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; - predictedApps.add(predictedApp); - } else if (info instanceof WorkspaceItemInfo) { - WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((WorkspaceItemInfo) info); - predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; - predictedApps.add(predictedApp); - } else { - if (DEBUG) { - Log.e(TAG, "Predicted app not found: " + mapper); - } - } - // Stop at the number of hotseat items - if (predictedApps.size() == mHotSeatItemsCount) { - break; - } - } - return predictedApps; + mLauncher.getStatsLogManager().logger() + .withItemInfo(workspaceItemInfo) + .log(LAUNCHER_HOTSEAT_PREDICTION_PINNED); } private List getPredictedIcons() { @@ -465,7 +293,7 @@ public class HotseatPredictionController implements DragController.DragListener, } private void removePredictedApps(List outlines, - ItemInfo draggedInfo) { + DropTarget.DragObject dragObject) { if (mIconRemoveAnimators != null) { mIconRemoveAnimators.end(); } @@ -475,7 +303,7 @@ public class HotseatPredictionController implements DragController.DragListener, if (!icon.isEnabled()) { continue; } - if (icon.getTag().equals(draggedInfo)) { + if (dragObject.dragSource == this && icon.equals(dragObject.originalView)) { mHotseat.removeView(icon); continue; } @@ -497,84 +325,23 @@ public class HotseatPredictionController implements DragController.DragListener, mIconRemoveAnimators.start(); } - private void notifyItemAction(AppTargetEvent event) { - if (mAppPredictor != null) { - mAppPredictor.notifyAppTargetEvent(event); - } - } - @Override public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { - removePredictedApps(mOutlineDrawings, dragObject.dragInfo); - mDragObject = dragObject; + removePredictedApps(mOutlineDrawings, dragObject); if (mOutlineDrawings.isEmpty()) return; for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) { mHotseat.addDelegatedCellDrawing(outlineDrawing); } + mDragInProgress = true; mHotseat.invalidate(); } - /** - * Unpins pinned app when it's converted into a folder - */ - public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) { - AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo); - AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo); - if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget, - AppTargetEvent.ACTION_PIN, folderInfo)); - } - // using folder info with isTrackedForPrediction as itemInfo.container is already changed - // to folder by this point - if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget, - AppTargetEvent.ACTION_UNPIN, folderInfo - )); - } - } - - /** - * Pins workspace item created when all folder items are removed but one - */ - public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) { - AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo); - AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo); - if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget, - AppTargetEvent.ACTION_UNPIN, folderInfo)); - } - if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget, - AppTargetEvent.ACTION_PIN, itemInfo)); - } - } - @Override public void onDragEnd() { - if (mDragObject == null) { - return; - } - - ItemInfo dragInfo = mDragObject.dragInfo; - if (mDragObject.isMoved()) { - AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo); - //always send pin event first to prevent AiAi from predicting an item moved within - // the same page - if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget, - AppTargetEvent.ACTION_PIN, dragInfo)); - } - if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction( - mDragObject.originalDragInfo)) { - notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget, - AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo)); - } - } - mDragObject = null; + mDragInProgress = false; fillGapsWithPrediction(true, this::removeOutlineDrawings); } - @Nullable @Override public SystemShortcut getShortcut(QuickstepLauncher activity, @@ -604,19 +371,7 @@ public class HotseatPredictionController implements DragController.DragListener, @Override public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) { - if ((changeFlags & CHANGE_FLAG_GRID) != 0) { - this.mHotSeatItemsCount = profile.numHotseatIcons; - createPredictor(); - } - } - - @Override - public void onAppsUpdated() { - fillGapsWithPrediction(); - } - - @Override - public void reapplyItemInfo(ItemInfoWithIcon info) { + this.mHotSeatItemsCount = profile.numHotseatIcons; } @Override @@ -624,12 +379,6 @@ public class HotseatPredictionController implements DragController.DragListener, //Does nothing } - @Override - public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child, - ArrayList parents) { - mHotseat.fillInLogContainerData(childInfo, child, parents); - } - /** * Logs rank info based on current list of predicted items */ @@ -643,17 +392,20 @@ public class HotseatPredictionController implements DragController.DragListener, + ",launchLocation:" + itemInfo.container); } - if (itemInfo.getTargetComponent() == null || itemInfo.user == null) { + + ComponentName targetCN = itemInfo.getTargetComponent(); + if (targetCN == null) { return; } - - final ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user); - - final List predictedApps = new ArrayList<>(mComponentKeyMappers); - OptionalInt rank = IntStream.range(0, predictedApps.size()) - .filter(index -> key.equals(predictedApps.get(index).getComponentKey())) - .findFirst(); - if (!rank.isPresent()) { + int rank = -1; + for (int i = mPredictedItems.size() - 1; i >= 0; i--) { + ItemInfo info = mPredictedItems.get(i); + if (targetCN.equals(info.getTargetComponent()) && itemInfo.user.equals(info.user)) { + rank = i; + break; + } + } + if (rank < 0) { return; } @@ -666,11 +418,11 @@ public class HotseatPredictionController implements DragController.DragListener, PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder(); containerBuilder.setCardinality(cardinality); if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) { - containerBuilder.setIndex(rank.getAsInt()); + containerBuilder.setIndex(rank); } mLauncher.getStatsLogManager().logger() .withInstanceId(instanceId) - .withRank(rank.getAsInt()) + .withRank(rank) .withContainerInfo(ContainerInfo.newBuilder() .setPredictedHotseatContainer(containerBuilder) .build()) @@ -691,30 +443,6 @@ public class HotseatPredictionController implements DragController.DragListener, } } - /** - * Fill in predicted_rank field based on app prediction. - * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT - */ - public static void encodeHotseatLayoutIntoPredictionRank( - @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) { - QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity(); - if (launcher == null || launcher.getHotseatPredictionController() == null - || itemInfo.getTargetComponent() == null) { - return; - } - HotseatPredictionController controller = launcher.getHotseatPredictionController(); - - final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user); - - final List predictedApps = controller.mComponentKeyMappers; - OptionalInt rank = IntStream.range(0, predictedApps.size()) - .filter((i) -> k.equals(predictedApps.get(i).getComponentKey())) - .findFirst(); - - target.predictedRank = 10000 + (controller.mPredictedSpotsCount * 100) - + (rank.isPresent() ? rank.getAsInt() + 1 : 0); - } - private static boolean isPredictedIcon(View view) { return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo && ((WorkspaceItemInfo) view.getTag()).container diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java index 5a038d27af..8f31c22d15 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.hybridhotseat; -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; @@ -24,13 +24,10 @@ import android.content.ComponentName; import android.content.Context; import android.os.Bundle; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.Workspace; -import com.android.launcher3.model.AllAppsList; -import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; -import com.android.launcher3.model.PredictionModel; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; @@ -38,55 +35,48 @@ import com.android.launcher3.shortcuts.ShortcutKey; import java.util.ArrayList; import java.util.Locale; -import java.util.function.Consumer; /** * Model helper for app predictions in workspace */ -public class HotseatPredictionModel extends PredictionModel { +public class HotseatPredictionModel { private static final String APP_LOCATION_HOTSEAT = "hotseat"; private static final String APP_LOCATION_WORKSPACE = "workspace"; private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events"; private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items"; - - public HotseatPredictionModel(Context context) { } - /** - * Creates and returns bundle using workspace items and cached items + * Creates and returns bundle using workspace items */ - public void createBundle(Consumer cb) { - LauncherAppState appState = LauncherAppState.getInstance(mContext); - appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { - @Override - public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { - Bundle bundle = new Bundle(); - ArrayList events = new ArrayList<>(); - ArrayList workspaceItems = new ArrayList<>(dataModel.workspaceItems); - workspaceItems.addAll(dataModel.appWidgets); - for (ItemInfo item : workspaceItems) { - AppTarget target = getAppTargetFromInfo(item); - if (target != null && !isTrackedForPrediction(item)) continue; - events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item)); - } - ArrayList currentTargets = new ArrayList<>(); - for (ItemInfo itemInfo : dataModel.cachedPredictedItems) { - AppTarget target = getAppTargetFromInfo(itemInfo); - if (target != null) currentTargets.add(target); - } - bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events); - bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets); - MAIN_EXECUTOR.execute(() -> cb.accept(bundle)); + public static Bundle convertDataModelToAppTargetBundle(Context context, BgDataModel dataModel) { + Bundle bundle = new Bundle(); + ArrayList events = new ArrayList<>(); + ArrayList workspaceItems = new ArrayList<>(dataModel.workspaceItems); + workspaceItems.addAll(dataModel.appWidgets); + for (ItemInfo item : workspaceItems) { + AppTarget target = getAppTargetFromInfo(context, item); + if (target != null && !isTrackedForPrediction(item)) continue; + events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item)); + } + ArrayList currentTargets = new ArrayList<>(); + FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION); + if (hotseatItems != null) { + for (ItemInfo itemInfo : hotseatItems.items) { + AppTarget target = getAppTargetFromInfo(context, itemInfo); + if (target != null) currentTargets.add(target); } - }); + } + bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events); + bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets); + return bundle; } /** * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null * if item is not supported prediction */ - public AppTarget getAppTargetFromInfo(ItemInfo info) { + public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) { if (info == null) return null; if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET && info instanceof LauncherAppWidgetInfo @@ -107,17 +97,17 @@ public class HotseatPredictionModel extends PredictionModel { shortcutKey.componentName.getPackageName(), shortcutKey.user).build(); } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { return new AppTarget.Builder(new AppTargetId("folder:" + info.id), - mContext.getPackageName(), info.user).build(); + context.getPackageName(), info.user).build(); } return null; } - /** * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item * location using {@link ItemInfo} */ - public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) { + public static AppTargetEvent wrapAppTargetWithLocation( + AppTarget target, int action, ItemInfo info) { String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java index 9e7c9fba6d..90f762e24c 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java @@ -19,8 +19,10 @@ import static com.android.launcher3.LauncherSettings.Favorites.HYBRID_HOTSEAT_BA import static com.android.launcher3.provider.LauncherDbUtils.tableExists; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import android.content.Context; + import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.GridBackupTable; import com.android.launcher3.provider.LauncherDbUtils; @@ -29,29 +31,24 @@ import com.android.launcher3.provider.LauncherDbUtils; * A helper class to manage migration revert restoration for hybrid hotseat */ public class HotseatRestoreHelper { - private final Launcher mLauncher; - - HotseatRestoreHelper(Launcher context) { - mLauncher = context; - } /** * Creates a snapshot backup of Favorite table for future restoration use. */ - public void createBackup() { + public static void createBackup(Context context) { MODEL_EXECUTOR.execute(() -> { try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) LauncherSettings.Settings.call( - mLauncher.getContentResolver(), + context.getContentResolver(), LauncherSettings.Settings.METHOD_NEW_TRANSACTION) .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { - InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; - GridBackupTable backupTable = new GridBackupTable(mLauncher, + InvariantDeviceProfile idp = LauncherAppState.getIDP(context); + GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(), idp.numHotseatIcons, idp.numColumns, idp.numRows); backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE); transaction.commit(); - LauncherSettings.Settings.call(mLauncher.getContentResolver(), + LauncherSettings.Settings.call(context.getContentResolver(), LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE); } }); @@ -60,23 +57,23 @@ public class HotseatRestoreHelper { /** * Finds and restores a previously saved snapshow of Favorites table */ - public void restoreBackup() { + public static void restoreBackup(Context context) { MODEL_EXECUTOR.execute(() -> { try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction) LauncherSettings.Settings.call( - mLauncher.getContentResolver(), + context.getContentResolver(), LauncherSettings.Settings.METHOD_NEW_TRANSACTION) .getBinder(LauncherSettings.Settings.EXTRA_VALUE)) { if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) { return; } - InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv; - GridBackupTable backupTable = new GridBackupTable(mLauncher, + InvariantDeviceProfile idp = LauncherAppState.getIDP(context); + GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(), idp.numHotseatIcons, idp.numColumns, idp.numRows); backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true); transaction.commit(); - mLauncher.getModel().forceReload(); + LauncherAppState.getInstance(context).getModel().forceReload(); } }); } diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java index 8e4c43f464..364a321f75 100644 --- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java +++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java @@ -15,8 +15,21 @@ */ package com.android.launcher3.model; +import static android.app.prediction.AppTargetEvent.ACTION_DISMISS; +import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH; +import static android.app.prediction.AppTargetEvent.ACTION_PIN; +import static android.app.prediction.AppTargetEvent.ACTION_UNPIN; + +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN; @@ -47,11 +60,12 @@ import com.android.launcher3.logger.LauncherAtom.FolderContainer; import com.android.launcher3.logger.LauncherAtom.HotseatContainer; import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer; import com.android.launcher3.logging.StatsLogManager.EventEnum; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.UserCache; import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer; import java.util.Locale; -import java.util.function.Consumer; +import java.util.function.ObjIntConsumer; import java.util.function.Predicate; /** @@ -64,9 +78,11 @@ public class AppEventProducer implements StatsLogConsumer { private final Context mContext; private final Handler mMessageHandler; - private final Consumer mCallback; + private final ObjIntConsumer mCallback; - public AppEventProducer(Context context, Consumer callback) { + private LauncherAtom.ItemInfo mLastDragItem; + + public AppEventProducer(Context context, ObjIntConsumer callback) { mContext = context; mMessageHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleMessage); mCallback = callback; @@ -76,7 +92,7 @@ public class AppEventProducer implements StatsLogConsumer { private boolean handleMessage(Message msg) { switch (msg.what) { case MSG_LAUNCH: { - mCallback.accept((AppTargetEvent) msg.obj); + mCallback.accept((AppTargetEvent) msg.obj, msg.arg1); return true; } } @@ -84,13 +100,18 @@ public class AppEventProducer implements StatsLogConsumer { } @AnyThread - private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId) { - AppTarget target = toAppTarget(atomInfo); + private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId, int targetPredictor) { + sendEvent(toAppTarget(atomInfo), atomInfo, eventId, targetPredictor); + } + + @AnyThread + private void sendEvent(AppTarget target, LauncherAtom.ItemInfo locationInfo, int eventId, + int targetPredictor) { if (target != null) { AppTargetEvent event = new AppTargetEvent.Builder(target, eventId) - .setLaunchLocation(getContainer(atomInfo)) + .setLaunchLocation(getContainer(locationInfo)) .build(); - Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget(); + mMessageHandler.obtainMessage(MSG_LAUNCH, targetPredictor, 0, event).sendToTarget(); } } @@ -101,9 +122,42 @@ public class AppEventProducer implements StatsLogConsumer { || event == LAUNCHER_TASK_LAUNCH_TAP || event == LAUNCHER_QUICKSWITCH_RIGHT || event == LAUNCHER_QUICKSWITCH_LEFT) { - sendEvent(atomInfo, AppTargetEvent.ACTION_LAUNCH); + sendEvent(atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION); } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) { - sendEvent(atomInfo, AppTargetEvent.ACTION_DISMISS); + sendEvent(atomInfo, ACTION_DISMISS, CONTAINER_PREDICTION); + } else if (event == LAUNCHER_ITEM_DRAG_STARTED) { + mLastDragItem = atomInfo; + } else if (event == LAUNCHER_ITEM_DROP_COMPLETED) { + if (mLastDragItem == null) { + return; + } + if (isTrackedForHotseatPrediction(atomInfo)) { + sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION); + } + if (isTrackedForHotseatPrediction(mLastDragItem)) { + sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION); + } + mLastDragItem = null; + } else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) { + if (isTrackedForHotseatPrediction(atomInfo)) { + sendEvent(createTempFolderTarget(), atomInfo, ACTION_PIN, + CONTAINER_HOTSEAT_PREDICTION); + sendEvent(atomInfo, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION); + } + } else if (event == LAUNCHER_FOLDER_CONVERTED_TO_ICON) { + if (isTrackedForHotseatPrediction(atomInfo)) { + sendEvent(createTempFolderTarget(), atomInfo, ACTION_UNPIN, + CONTAINER_HOTSEAT_PREDICTION); + sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION); + } + } else if (event == LAUNCHER_ITEM_DROPPED_ON_REMOVE) { + if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) { + sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION); + } + } else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) { + if (isTrackedForHotseatPrediction(atomInfo)) { + sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION); + } } } @@ -152,10 +206,8 @@ public class AppEventProducer implements StatsLogConsumer { } break; } - case FOLDER_ICON: { - id = "folder:" + SystemClock.uptimeMillis(); - cn = new ComponentName(mContext.getPackageName(), "#folder"); - } + case FOLDER_ICON: + return createTempFolderTarget(); } if (id != null && cn != null) { return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle) @@ -165,6 +217,12 @@ public class AppEventProducer implements StatsLogConsumer { return null; } + private AppTarget createTempFolderTarget() { + return new AppTarget.Builder(new AppTargetId("folder:" + SystemClock.uptimeMillis()), + mContext.getPackageName(), Process.myUserHandle()) + .build(); + } + private String getContainer(LauncherAtom.ItemInfo info) { ContainerInfo ci = info.getContainerInfo(); switch (ci.getContainerCase()) { @@ -212,11 +270,26 @@ public class AppEventProducer implements StatsLogConsumer { } private static String getHotseatContainerString(HotseatContainer hc) { - return String.format(Locale.ENGLISH, "hotseat/%d", hc.getIndex()); + return String.format(Locale.ENGLISH, "hotseat/%1$d/[%1$d,0]/[1,1]", hc.getIndex()); } private static ComponentName parseNullable(String componentNameString) { return TextUtils.isEmpty(componentNameString) ? null : ComponentName.unflattenFromString(componentNameString); } + + /** + * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors + */ + private static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) { + ContainerInfo ci = info.getContainerInfo(); + switch (ci.getContainerCase()) { + case HOTSEAT: + return true; + case WORKSPACE: + return ci.getWorkspace().getPageIndex() == 0; + default: + return false; + } + } } diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 166cb6cc4d..be57dece79 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -16,9 +16,11 @@ package com.android.launcher3.model; 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.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; +import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionManager; @@ -62,6 +64,8 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange private final PredictorState mAllAppsState = new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions"); + private final PredictorState mHotseatState = + new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions"); private final InvariantDeviceProfile mIDP; private final AppEventProducer mAppEventProducer; @@ -81,13 +85,23 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange // TODO: Implement caching and preloading super.loadItems(ums, pinnedShortcuts); - WorkspaceItemFactory factory = + WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns); mAllAppsState.items.setItems( - mAllAppsState.storage.read(mApp.getContext(), factory, ums.allUsers::get)); + mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get)); mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items); + WorkspaceItemFactory hotseatFactory = + new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numHotseatIcons); + mHotseatState.items.setItems( + mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get)); + mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items); mActive = true; + } + + @Override + public void workspaceLoadComplete() { + super.workspaceLoadComplete(); recreatePredictors(); } @@ -111,6 +125,7 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange private void destroyPredictors() { mAllAppsState.destroyPredictor(); + mHotseatState.destroyPredictor(); } @WorkerThread @@ -125,18 +140,28 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange return; } - int count = mIDP.numAllAppsColumns; - - mAllAppsState.predictor = apm.createAppPredictionSession( + registerPredictor(mAllAppsState, apm.createAppPredictionSession( new AppPredictionContext.Builder(context) .setUiSurface("home") - .setPredictedTargetCount(count) - .build()); - mAllAppsState.predictor.registerPredictionUpdates( - Executors.MODEL_EXECUTOR, t -> handleUpdate(mAllAppsState, t)); - mAllAppsState.predictor.requestPredictionUpdate(); + .setPredictedTargetCount(mIDP.numAllAppsColumns) + .build())); + + // TODO: get bundle + registerPredictor(mHotseatState, apm.createAppPredictionSession( + new AppPredictionContext.Builder(context) + .setUiSurface("hotseat") + .setPredictedTargetCount(mIDP.numHotseatIcons) + .setExtras(convertDataModelToAppTargetBundle(context, mDataModel)) + .build())); + } + private void registerPredictor(PredictorState state, AppPredictor predictor) { + state.predictor = predictor; + state.predictor.registerPredictionUpdates( + Executors.MODEL_EXECUTOR, t -> handleUpdate(state, t)); + state.predictor.requestPredictionUpdate(); + } private void handleUpdate(PredictorState state, List targets) { if (state.setTargets(targets)) { @@ -154,9 +179,10 @@ public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChange } } - private void onAppTargetEvent(AppTargetEvent event) { - if (mAllAppsState.predictor != null) { - mAllAppsState.predictor.notifyAppTargetEvent(event); + private void onAppTargetEvent(AppTargetEvent event, int client) { + PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState; + if (state.predictor != null) { + state.predictor.notifyAppTargetEvent(event); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index d5d6fddc94..a284f5d5cd 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -33,8 +33,8 @@ import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Configuration; -import android.os.Bundle; import android.util.Log; import android.view.View; @@ -48,14 +48,11 @@ import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.appprediction.PredictionRowView; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.folder.Folder; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.model.BgDataModel.FixedContainerItems; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory; import com.android.launcher3.testing.TestProtocol; @@ -70,13 +67,14 @@ import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchContro import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController; import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController; -import com.android.launcher3.util.IntArray; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TouchController; import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.UiThreadHelper.AsyncCommand; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.util.QuickstepOnboardingPrefs; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -84,7 +82,6 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.stream.Stream; @@ -98,14 +95,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2); private FixedContainerItems mAllAppsPredictions; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (mHotseatPredictionController != null) { - mHotseatPredictionController.createPredictor(); - } - } + private HotseatPredictionController mHotseatPredictionController; @Override protected void setupViews() { @@ -143,6 +133,18 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } + /** + * Returns Prediction controller for hybrid hotseat + */ + public HotseatPredictionController getHotseatPredictionController() { + return mHotseatPredictionController; + } + + @Override + protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) { + return new QuickstepOnboardingPrefs(this, sharedPrefs); + } + @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); @@ -178,22 +180,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { super.showAllAppsFromIntent(alreadyOnHome); } - @Override - public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo) { - super.folderCreatedFromItem(folder, itemInfo); - if (mHotseatPredictionController != null) { - mHotseatPredictionController.folderCreatedFromWorkspaceItem(itemInfo, folder.getInfo()); - } - } - - @Override - public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) { - super.folderConvertedToItem(folder, itemInfo); - if (mHotseatPredictionController != null) { - mHotseatPredictionController.folderConvertedToWorkspaceItem(itemInfo, folder.getInfo()); - } - } - @Override public Stream getSupportedShortcuts() { if (mHotseatPredictionController != null) { @@ -222,20 +208,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { } } - @Override - public void bindPredictedItems(List appInfos, IntArray ranks) { - super.bindPredictedItems(appInfos, ranks); - if (mHotseatPredictionController != null) { - mHotseatPredictionController.showCachedItems(appInfos, ranks); - } - } - @Override public void bindExtraContainerItems(FixedContainerItems item) { if (item.containerId == Favorites.CONTAINER_PREDICTION) { mAllAppsPredictions = item; getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class) .setPredictedApps(item.items); + } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION + && mHotseatPredictionController != null) { + mHotseatPredictionController.setPredictedItems(item); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 6ec114e216..bbe7821864 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -16,7 +16,6 @@ package com.android.launcher3.uioverrides.states; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; -import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; @@ -30,7 +29,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Workspace; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.util.LayoutUtils; @@ -177,8 +175,6 @@ public class OverviewState extends LauncherState { public void onBackPressed(Launcher launcher) { TaskView taskView = launcher.getOverviewPanel().getRunningTaskView(); if (taskView != null) { - launcher.getUserEventDispatcher().logActionCommand(Action.Command.BACK, - newContainerTarget(ContainerType.OVERVIEW)); taskView.launchTask(true); } else { super.onBackPressed(launcher); diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index da3c4858a8..d57429478c 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -19,7 +19,6 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU; 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.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.config.FeatureFlags.ENABLE_ALL_APPS_EDU; @@ -221,7 +220,7 @@ public class NavBarToHomeTouchController implements TouchController, recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation(true /* toRecents */, null)); } - if (mStartState == OVERVIEW) { + if (mStartState.overviewUi) { new OverviewToHomeAnim(mLauncher, () -> onSwipeInteractionCompleted(mEndState)) .animateWithVelocity(velocity); } else { @@ -229,12 +228,12 @@ public class NavBarToHomeTouchController implements TouchController, () -> onSwipeInteractionCompleted(mEndState)); } if (mStartState != mEndState) { - logStateChange(mStartState.containerType, logAction); + // TODO: add to WW log } AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher); if (topOpenView != null) { AbstractFloatingView.closeAllOpenViews(mLauncher); - logStateChange(topOpenView.getLogContainerType(), logAction); + // TODO: add to WW log } ActivityManagerWrapper.getInstance() .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index dca33789e5..365cff576a 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -29,7 +29,6 @@ import android.view.ViewConfiguration; import androidx.annotation.BinderThread; -import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.util.ActivityInitListener; @@ -87,12 +86,6 @@ public class OverviewCommandHelper { MAIN_EXECUTOR.execute(new HideRecentsCommand()); } - @BinderThread - public void onTip(int actionType, int viewType) { - MAIN_EXECUTOR.execute(() -> - UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType)); - } - private class ShowRecentsCommand extends RecentsActivityCommand { private final boolean mTriggeredFromAltTab; diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index e08741d483..f29a5c8ed2 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -22,7 +22,6 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; -import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.GestureState.DEFAULT_STATE; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; @@ -61,7 +60,6 @@ import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; -import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.testing.TestLogging; @@ -141,7 +139,6 @@ public class TouchInteractionService extends Service implements PluginListener + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/search_result_slice.xml b/res/layout/search_result_slice.xml new file mode 100644 index 0000000000..ea1d49a56d --- /dev/null +++ b/res/layout/search_result_slice.xml @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index fe9717cd7a..f56fbaaace 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -35,9 +35,6 @@ #E0E0E0 - #32c0c0c0 - #40c0c0c0 - #A0C2F9 #6DA1FF #FFFFFFFF diff --git a/res/values/config.xml b/res/values/config.xml index dc8bdffca3..fc0a5e10dc 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -69,7 +69,6 @@ - diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml index 3455cb8661..7e7220813a 100644 --- a/res/xml/launcher_preferences.xml +++ b/res/xml/launcher_preferences.xml @@ -44,12 +44,6 @@ android:defaultValue="@bool/allow_rotation" android:persistent="true" /> - - 0)) { getIconBounds(mDotParams.iconBounds); - Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale()); + Utilities.scaleRectAboutCenter(mDotParams.iconBounds, + IconShape.getNormalizationScale()); final int scrollX = getScrollX(); final int scrollY = getScrollY(); canvas.translate(scrollX, scrollY); @@ -507,6 +512,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, /** * Creates an animator to fade the text in or out. + * * @param fadeIn Whether the text should fade in or fade out. */ public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) { @@ -663,7 +669,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, applyFromWorkspaceItem((WorkspaceItemInfo) info); mActivity.invalidateParent(info); } else if (info instanceof PackageItemInfo) { - applyFromPackageItemInfo((PackageItemInfo) info); + applyFromItemInfoWithIcon((PackageItemInfo) info); } mDisableRelayout = false; @@ -761,7 +767,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, public SafeCloseable prepareDrawDragView() { resetIconScale(); setForceHideDot(true); - return () -> { }; + return () -> { + }; } private void resetIconScale() { diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java index d4d7b99fd4..ba227d4b90 100644 --- a/src/com/android/launcher3/DragSource.java +++ b/src/com/android/launcher3/DragSource.java @@ -19,12 +19,11 @@ package com.android.launcher3; import android.view.View; import com.android.launcher3.DropTarget.DragObject; -import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; /** * Interface defining an object that can originate a drag. */ -public interface DragSource extends LogContainerProvider { +public interface DragSource { /** * A callback made back to the source after an item from this source has been dropped on a diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java index b27abc4369..fd4c30c6f2 100644 --- a/src/com/android/launcher3/DropTarget.java +++ b/src/com/android/launcher3/DropTarget.java @@ -113,18 +113,6 @@ public interface DropTarget { return res; } - - - /** - * This is used to determine if an object is dropped at a different location than it was - * dragged from - */ - public boolean isMoved() { - return dragInfo.cellX != originalDragInfo.cellX - || dragInfo.cellY != originalDragInfo.cellY - || dragInfo.screenId != originalDragInfo.screenId - || dragInfo.container != originalDragInfo.container; - } } /** diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 51a9dfe7ac..cacd3fbe7f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -110,7 +110,6 @@ import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragView; -import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderGridOrganizer; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.icons.IconCache; @@ -911,7 +910,6 @@ public class Launcher extends StatefulActivity implements Launche @CallSuper protected void onDeferredResumed() { logStopAndResume(Action.Command.RESUME); - getUserEventDispatcher().startSession(); // Process any items that were added while Launcher was away. ItemInstallQueue.INSTANCE.get(this) @@ -929,6 +927,7 @@ public class Launcher extends StatefulActivity implements Launche protected void handlePendingActivityRequest() { } private void logStopAndResume(int command) { + if (mPendingExecutor != null) return; int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage(); int containerType = mStateManager.getState().containerType; @@ -1746,16 +1745,6 @@ public class Launcher extends StatefulActivity implements Launche return newFolder; } - /** - * Called when a workspace item is converted into a folder - */ - public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo){} - - /** - * Called when a folder is converted into a workspace item - */ - public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {} - /** * Unbinds the view for the specified item, and removes the item and all its children. * @@ -2193,9 +2182,6 @@ public class Launcher extends StatefulActivity implements Launche workspace.requestLayout(); } - @Override - public void bindPredictedItems(List appInfos, IntArray ranks) { } - /** * Add the views for a widget to the workspace. */ diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 782a8699cc..b278e81655 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -33,7 +33,6 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconProvider; import com.android.launcher3.icons.LauncherIcons; -import com.android.launcher3.model.PredictionModel; import com.android.launcher3.notification.NotificationListener; import com.android.launcher3.pm.InstallSessionHelper; import com.android.launcher3.pm.InstallSessionTracker; @@ -58,7 +57,6 @@ public class LauncherAppState { private final IconCache mIconCache; private final WidgetPreviewLoader mWidgetCache; private final InvariantDeviceProfile mInvariantDeviceProfile; - private final PredictionModel mPredictionModel; private SecureSettingsObserver mNotificationDotsObserver; private InstallSessionTracker mInstallSessionTracker; @@ -125,7 +123,6 @@ public class LauncherAppState { mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName); mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); mModel = new LauncherModel(context, this, mIconCache, AppFilter.newInstance(mContext)); - mPredictionModel = PredictionModel.newInstance(mContext); } protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { @@ -182,10 +179,6 @@ public class LauncherAppState { return mModel; } - public PredictionModel getPredictionModel() { - return mPredictionModel; - } - public WidgetPreviewLoader getWidgetCache() { return mWidgetCache; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index c51a84e5ed..8458152146 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -218,6 +218,13 @@ public class LauncherModel extends LauncherApps.Callback implements InstallSessi } } + /** + * Called when the workspace items have drastically changed + */ + public void onWorkspaceUiChanged() { + MODEL_EXECUTOR.execute(mModelDelegate::workspaceLoadComplete); + } + /** * Called when the model is destroyed */ diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java index 56875bb337..2df7f5ad20 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -49,7 +49,6 @@ import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.Themes; import java.net.URISyntaxException; -import java.util.ArrayList; /** * Drop target which provides a secondary option for an item. @@ -339,12 +338,6 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList mDragObject = d; } - @Override - public void fillInLogContainerData(ItemInfo childInfo, Target child, - ArrayList parents) { - mOriginal.fillInLogContainerData(childInfo, child, parents); - } - @Override public void onLauncherResume() { // We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well. diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6bfd3495e5..8da81ac2d2 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -28,7 +28,6 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY; -import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT; @@ -99,7 +98,6 @@ import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.touch.WorkspaceTouchListener; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.Executors; import com.android.launcher3.util.IntArray; import com.android.launcher3.util.IntSparseArrayMap; @@ -1506,7 +1504,6 @@ public class Workspace extends PagedView .showForIcon((BubbleTextView) child); if (popupContainer != null) { dragOptions.preDragCondition = popupContainer.createPreDragCondition(); - mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started"); } } @@ -1706,7 +1703,6 @@ public class Workspace extends PagedView fi.addItem(destInfo); fi.addItem(sourceInfo); } - mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo); return true; } return false; @@ -1723,7 +1719,7 @@ public class Workspace extends PagedView if (dropOverView instanceof FolderIcon) { FolderIcon fi = (FolderIcon) dropOverView; if (fi.acceptDrop(d.dragInfo)) { - mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId) + mStatsLogManager.logger().withItemInfo(d.dragInfo).withInstanceId(d.logInstanceId) .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED); fi.onDrop(d, false /* itemReturnedOnFailedDrop */); @@ -3272,24 +3268,6 @@ public class Workspace extends PagedView return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens); } - @Override - public void fillInLogContainerData(ItemInfo childInfo, Target child, - ArrayList parents) { - if (childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT - || childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) { - getHotseat().fillInLogContainerData(childInfo, child, parents); - return; - } else if (childInfo.container >= 0) { - FolderIcon icon = (FolderIcon) getHomescreenIconByItemId(childInfo.container); - icon.getFolder().fillInLogContainerData(childInfo, child, parents); - return; - } - child.gridX = childInfo.cellX; - child.gridY = childInfo.cellY; - child.pageIndex = getCurrentPage(); - parents.add(newContainerTarget(ContainerType.WORKSPACE)); - } - /** * Used as a workaround to ensure that the AppWidgetService receives the * PACKAGE_ADDED broadcast before updating widgets. diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 0684fe0526..1f515669a6 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -17,7 +17,6 @@ package com.android.launcher3.allapps; import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; -import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; @@ -62,8 +61,6 @@ import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -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; @@ -71,8 +68,6 @@ import com.android.launcher3.util.Themes; import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.SpringRelativeLayout; -import java.util.ArrayList; - /** * The all apps view container. */ @@ -354,13 +349,6 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo public void onDropCompleted(View target, DragObject d, boolean success) { } - @Override - public void fillInLogContainerData(ItemInfo childInfo, Target child, - ArrayList parents) { - parents.add(newContainerTarget( - getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS)); - } - @Override public void setInsets(Rect insets) { mInsets.set(insets); diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index c61f01f206..da161ac1cc 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -20,6 +20,7 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.net.Uri; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -30,18 +31,27 @@ import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; +import androidx.lifecycle.LiveData; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import androidx.slice.Slice; +import androidx.slice.widget.SliceLiveData; +import androidx.slice.widget.SliceView; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BubbleTextView; import com.android.launcher3.R; import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; import com.android.launcher3.allapps.search.SearchSectionInfo; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.PackageManagerHelper; @@ -50,7 +60,9 @@ import java.util.List; /** * The grid view adapter of all the apps. */ -public class AllAppsGridAdapter extends RecyclerView.Adapter { +public class AllAppsGridAdapter extends + RecyclerView.Adapter implements + LifecycleOwner { public static final String TAG = "AppsGridAdapter"; @@ -71,12 +83,18 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter Play load Type */ public static class AdapterItemWithPayload extends AdapterItem { @@ -310,6 +331,12 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter) mApps.getAdapterItems().get(position)) + .getPayload(); + try { + LiveData liveData = SliceLiveData.fromUri(mLauncher, uri); + liveData.observe(this::getLifecycle, sliceView); + } catch (Exception ignored) { + } + break; case VIEW_TYPE_SEARCH_CORPUS_TITLE: - case DETAIL_ROW_WITH_BUTTON: + case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON: case VIEW_TYPE_SEARCH_HERO_APP: + case VIEW_TYPE_SEARCH_ROW: PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView; payloadResultView.applyAdapterInfo( (AdapterItemWithPayload) mApps.getAdapterItems().get(position)); @@ -451,4 +495,9 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter> callback) { mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { @Override public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { List matchingResults = getTitleMatchResult(apps.data, query); - if (mHeroSectionSupported && matchingResults.size() <= MAX_HERO_SECTION_COUNT) { - callback.accept(getHeroAdapterItems(app.getContext(), matchingResults)); - } else { - callback.accept(getAdapterItems(matchingResults)); - } + callback.accept(getAdapterItems(matchingResults)); } }); } - /** - * Returns MAX_SHORTCUTS_COUNT shortcuts from local cache - * TODO: Shortcuts should be ranked based on relevancy - */ - private ArrayList getShortcutInfos(Context context, AppInfo appInfo) { - List shortcuts = new ShortcutRequest(context, appInfo.user) - .withContainer(appInfo.getTargetComponent()) - .query(ShortcutRequest.PUBLISHED); - shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, null); - IconCache cache = LauncherAppState.getInstance(context).getIconCache(); - ArrayList shortcutItems = new ArrayList<>(); - for (int i = 0; i < shortcuts.size() && i < MAX_SHORTCUTS_COUNT; i++) { - final ShortcutInfo shortcut = shortcuts.get(i); - final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, context); - cache.getUnbadgedShortcutIcon(si, shortcut); - si.rank = i; - si.container = CONTAINER_SHORTCUTS; - shortcutItems.add(si); - } - return shortcutItems; - } - /** * Filters {@link AppInfo}s matching specified query */ @@ -121,21 +75,6 @@ public class AppsSearchPipeline implements SearchPipeline { return result; } - private ArrayList getHeroAdapterItems(Context context, List apps) { - ArrayList adapterItems = new ArrayList<>(); - for (int i = 0; i < apps.size(); i++) { - //hero app - AppInfo appInfo = apps.get(i); - ArrayList shortcuts = getShortcutInfos(context, appInfo); - AdapterItem adapterItem = new AllAppsGridAdapter.AdapterItemWithPayload(shortcuts, - VIEW_TYPE_SEARCH_HERO_APP); - adapterItem.appInfo = appInfo; - adapterItem.searchSectionInfo = mSearchSectionInfo; - adapterItems.add(adapterItem); - } - return adapterItems; - } - private ArrayList getAdapterItems(List matchingApps) { ArrayList items = new ArrayList<>(); for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) { diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java index 470191c3fc..5ed7de54fa 100644 --- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java +++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.allapps.search; +import android.content.Context; import android.os.Handler; import com.android.launcher3.LauncherAppState; @@ -30,9 +31,9 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm { protected final Handler mResultHandler; private final AppsSearchPipeline mAppsSearchPipeline; - public DefaultAppSearchAlgorithm(LauncherAppState launcherAppState) { + public DefaultAppSearchAlgorithm(Context context, LauncherAppState launcherAppState) { mResultHandler = new Handler(); - mAppsSearchPipeline = new AppsSearchPipeline(launcherAppState, false); + mAppsSearchPipeline = new AppsSearchPipeline(context, launcherAppState); } @Override diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java index bf3aa7f7cb..6104d80676 100644 --- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java +++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java @@ -16,7 +16,6 @@ package com.android.launcher3.dragndrop; -import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import android.annotation.TargetApi; import android.appwidget.AppWidgetManager; @@ -33,15 +32,11 @@ import com.android.launcher3.DragSource; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.PendingAddItemInfo; -import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingItemDragHelper; import com.android.launcher3.widget.WidgetAddFlowHandler; -import java.util.ArrayList; - /** * {@link DragSource} for handling drop from a different window. This object is initialized * in the source window and is passed on to the Launcher activity as an Intent extra. @@ -106,12 +101,6 @@ public class PinItemDragListener extends BaseItemDragListener { return dragHelper; } - @Override - public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child, - ArrayList parents) { - parents.add(newContainerTarget(LauncherLogProto.ContainerType.PINITEM)); - } - @Override protected void postCleanup() { super.postCleanup(); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index b91d1c3260..f1606ea747 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -19,11 +19,11 @@ package com.android.launcher3.folder; import static android.text.TextUtils.isEmpty; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; -import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED; @@ -1183,7 +1183,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo newIcon.requestFocus(); } if (finalItem != null) { - mLauncher.folderConvertedToItem(mFolderIcon.getFolder(), finalItem); + mStatsLogManager.logger().withItemInfo(finalItem) + .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON); } } } @@ -1481,27 +1482,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo outRect.right += mScrollAreaOffset; } - @Override - public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child, - ArrayList targets) { - child.gridX = childInfo.cellX; - child.gridY = childInfo.cellY; - child.pageIndex = mContent.getCurrentPage(); - - LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.FOLDER); - target.pageIndex = mInfo.screenId; - target.gridX = mInfo.cellX; - target.gridY = mInfo.cellY; - targets.add(target); - - // continue to parent - if (mInfo.container == CONTAINER_HOTSEAT) { - mLauncher.getHotseat().fillInLogContainerData(mInfo, target, targets); - } else { - mLauncher.getWorkspace().fillInLogContainerData(mInfo, target, targets); - } - } - private class OnScrollHintListener implements OnAlarmListener { private final DragObject mDragObject; @@ -1589,17 +1569,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo return getOpenView(launcher, TYPE_FOLDER); } - @Override - public void logActionCommand(int command) { - mLauncher.getUserEventDispatcher().logActionCommand( - command, getFolderIcon(), getLogContainerType()); - } - - @Override - public int getLogContainerType() { - return LauncherLogProto.ContainerType.FOLDER; - } - /** * Navigation bar back key or hardware input back key has been issued. */ diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java index cca9836835..0f1432a9b6 100644 --- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java +++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java @@ -19,6 +19,7 @@ import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; import static android.view.View.VISIBLE; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER; import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks; @@ -72,12 +73,12 @@ import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.Callbacks; +import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.LoaderResults; import com.android.launcher3.model.LoaderTask; import com.android.launcher3.model.ModelDelegate; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetsModel; -import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; @@ -95,6 +96,7 @@ import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -467,12 +469,14 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper } IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems, mIdp.numHotseatIcons); - int count = Math.min(ranks.size(), workspaceResult.mCachedPredictedItems.size()); + List predictions = workspaceResult.mHotseatPredictions == null + ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items; + int count = Math.min(ranks.size(), predictions.size()); for (int i = 0; i < count; i++) { - AppInfo appInfo = workspaceResult.mCachedPredictedItems.get(i); int rank = ranks.get(i); - WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(appInfo); - itemInfo.container = Favorites.CONTAINER_HOTSEAT_PREDICTION; + WorkspaceItemInfo itemInfo = + new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i)); + itemInfo.container = CONTAINER_HOTSEAT_PREDICTION; itemInfo.rank = rank; itemInfo.cellX = mHotseat.getCellXFromOrder(rank); itemInfo.cellY = mHotseat.getCellYFromOrder(rank); @@ -569,8 +573,7 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper return null; } - return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets, - mBgDataModel.cachedPredictedItems, mBgDataModel.widgetsModel, null); + return new WorkspaceResult(mBgDataModel, mBgDataModel.widgetsModel, null); } } @@ -594,11 +597,10 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper } @Override - public WorkspaceResult call() throws Exception { + public WorkspaceResult call() { List allShortcuts = new ArrayList<>(); loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI); - return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets, - mBgDataModel.cachedPredictedItems, null, mWidgetProvidersMap); + return new WorkspaceResult(mBgDataModel, null, mWidgetProvidersMap); } } @@ -618,17 +620,16 @@ public class LauncherPreviewRenderer extends ContextThemeWrapper private static class WorkspaceResult { private final ArrayList mWorkspaceItems; private final ArrayList mAppWidgets; - private final ArrayList mCachedPredictedItems; + private final FixedContainerItems mHotseatPredictions; private final WidgetsModel mWidgetsModel; private final Map mWidgetProvidersMap; - private WorkspaceResult(ArrayList workspaceItems, - ArrayList appWidgets, - ArrayList cachedPredictedItems, WidgetsModel widgetsModel, + private WorkspaceResult(BgDataModel dataModel, + WidgetsModel widgetsModel, Map widgetProviderInfoMap) { - mWorkspaceItems = workspaceItems; - mAppWidgets = appWidgets; - mCachedPredictedItems = cachedPredictedItems; + mWorkspaceItems = dataModel.workspaceItems; + mAppWidgets = dataModel.appWidgets; + mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION); mWidgetsModel = widgetsModel; mWidgetProvidersMap = widgetProviderInfoMap; } diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java index 22823391dd..ec1c3ef779 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -316,7 +316,13 @@ public class StatsLogManager implements ResourceBasedOverride { LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON(625), @UiEvent(doc = "User tapped on image content in Overview Select mode.") - LAUNCHER_SELECT_MODE_IMAGE(627); + LAUNCHER_SELECT_MODE_IMAGE(627), + + @UiEvent(doc = "A folder was replaced by a single item") + LAUNCHER_FOLDER_CONVERTED_TO_ICON(628), + + @UiEvent(doc = "A hotseat prediction item was pinned") + LAUNCHER_HOTSEAT_PREDICTION_PINNED(629); // ADD MORE diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java index 31a81b8164..a40cc263db 100644 --- a/src/com/android/launcher3/logging/UserEventDispatcher.java +++ b/src/com/android/launcher3/logging/UserEventDispatcher.java @@ -19,26 +19,21 @@ package com.android.launcher3.logging; import static com.android.launcher3.logging.LoggerUtils.newAction; import static com.android.launcher3.logging.LoggerUtils.newCommandAction; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; -import static com.android.launcher3.logging.LoggerUtils.newControlTarget; import static com.android.launcher3.logging.LoggerUtils.newDropTarget; import static com.android.launcher3.logging.LoggerUtils.newItemTarget; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; import static com.android.launcher3.logging.LoggerUtils.newTarget; import static com.android.launcher3.logging.LoggerUtils.newTouchAction; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; import static com.android.launcher3.userevent.nano.LauncherLogProto.TipType; import static java.util.Optional.ofNullable; -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.os.Process; import android.os.SystemClock; -import android.os.UserHandle; import android.util.Log; import android.view.View; @@ -54,7 +49,6 @@ import com.android.launcher3.userevent.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.InstantAppResolver; import com.android.launcher3.util.LogConfig; import com.android.launcher3.util.ResourceBasedOverride; @@ -101,7 +95,7 @@ public class UserEventDispatcher implements ResourceBasedOverride { * * @return whether container data was added. */ - public boolean fillLogContainer(@Nullable View v, Target child, + private boolean fillLogContainer(@Nullable View v, Target child, @Nullable ArrayList targets) { LogContainerProvider firstParent = StatsLogUtils.getLaunchProviderRecursive(v); if (v == null || !(v.getTag() instanceof ItemInfo) || firstParent == null) { @@ -125,55 +119,6 @@ public class UserEventDispatcher implements ResourceBasedOverride { private boolean mAppOrTaskLaunch; private boolean mPreviousHomeGesture; - // APP_ICON SHORTCUT WIDGET - // -------------------------------------------------------------- - // packageNameHash required optional required - // componentNameHash required required - // intentHash required - // -------------------------------------------------------------- - - @Deprecated - public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) { - Target itemTarget = newItemTarget(v, mInstantAppResolver); - Action action = newTouchAction(Action.Touch.TAP); - ArrayList targets = makeTargetsList(itemTarget); - if (fillLogContainer(v, itemTarget, targets)) { - onFillInLogContainerData((ItemInfo) v.getTag(), itemTarget, targets); - fillIntentInfo(itemTarget, intent, userHandle); - } - LauncherEvent event = newLauncherEvent(action, targets); - dispatchUserEvent(event, intent); - mAppOrTaskLaunch = true; - } - - /** - * Placeholder method. - */ - public void logActionTip(int actionType, int viewType) { - } - - @Deprecated - public void logTaskLaunchOrDismiss(int action, int direction, int taskIndex, - ComponentKey componentKey) { - LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE or FLING - newTarget(Target.Type.ITEM)); - if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) { - // Direction DOWN means the task was launched, UP means it was dismissed. - event.action.dir = direction; - } - event.srcTarget[0].itemType = ItemType.TASK; - event.srcTarget[0].pageIndex = taskIndex; - fillComponentInfo(event.srcTarget[0], componentKey.componentName); - dispatchUserEvent(event, null); - mAppOrTaskLaunch = true; - } - - protected void fillIntentInfo(Target target, Intent intent, @Nullable UserHandle userHandle) { - target.intentHash = intent.hashCode(); - target.isWorkApp = userHandle != null && !userHandle.equals(Process.myUserHandle()); - fillComponentInfo(target, intent.getComponent()); - } - private void fillComponentInfo(Target target, ComponentName cn) { if (cn != null) { target.packageNameHash = (mUuidStr + cn.getPackageName()).hashCode(); @@ -181,22 +126,6 @@ public class UserEventDispatcher implements ResourceBasedOverride { } } - public void logNotificationLaunch(View v, PendingIntent intent) { - LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP), - newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER)); - Target itemTarget = newItemTarget(v, mInstantAppResolver); - ArrayList targets = makeTargetsList(itemTarget); - - if (fillLogContainer(v, itemTarget, targets)) { - itemTarget.packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode(); - } - dispatchUserEvent(event, null); - } - - public void logActionCommand(int command, Target srcTarget) { - logActionCommand(command, srcTarget, null); - } - public void logActionCommand(int command, int srcContainerType, int dstContainerType) { logActionCommand(command, newContainerTarget(srcContainerType), dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null); @@ -227,25 +156,6 @@ public class UserEventDispatcher implements ResourceBasedOverride { dispatchUserEvent(event, null); } - /** - * TODO: Make this function work when a container view is passed as the 2nd param. - */ - public void logActionCommand(int command, View itemView, int srcContainerType) { - LauncherEvent event = newLauncherEvent(newCommandAction(command), - newItemTarget(itemView, mInstantAppResolver), newTarget(Target.Type.CONTAINER)); - - Target itemTarget = newItemTarget(itemView, mInstantAppResolver); - ArrayList targets = makeTargetsList(itemTarget); - - if (fillLogContainer(itemView, itemTarget, targets)) { - // TODO: Remove the following two lines once fillInLogContainerData can take in a - // container view. - itemTarget.type = Target.Type.CONTAINER; - itemTarget.containerType = srcContainerType; - } - dispatchUserEvent(event, null); - } - public void logActionOnControl(int action, int controlType) { logActionOnControl(action, controlType, null); } @@ -332,7 +242,6 @@ public class UserEventDispatcher implements ResourceBasedOverride { event.srcTarget[0].spanX = downX; event.srcTarget[0].spanY = downY; dispatchUserEvent(event, null); - resetElapsedContainerMillis("state changed"); } public void logActionOnItem(int action, int dir, int itemType) { @@ -386,7 +295,6 @@ public class UserEventDispatcher implements ResourceBasedOverride { ArrayList targets = makeTargetsList(child); fillLogContainer(icon, child, targets); dispatchUserEvent(newLauncherEvent(newTouchAction(Action.Touch.TAP), targets), null); - resetElapsedContainerMillis("deep shortcut open"); } public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) { @@ -397,7 +305,7 @@ public class UserEventDispatcher implements ResourceBasedOverride { Target destChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver); ArrayList destTargets = makeTargetsList(destChild); - dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets); + //dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets); if (dropTargetAsView instanceof LogContainerProvider) { ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(dragObj.dragInfo, destChild, destTargets); @@ -414,35 +322,6 @@ public class UserEventDispatcher implements ResourceBasedOverride { dispatchUserEvent(event, null); } - public void logActionBack(boolean completed, int downX, int downY, boolean isButton, - boolean gestureSwipeLeft, int containerType) { - int actionTouch = isButton ? Action.Touch.TAP : Action.Touch.SWIPE; - Action action = newCommandAction(actionTouch); - action.command = Action.Command.BACK; - action.dir = isButton ? Action.Direction.NONE : - gestureSwipeLeft ? Action.Direction.LEFT : Action.Direction.RIGHT; - Target target = newControlTarget(isButton ? ControlType.BACK_BUTTON : - ControlType.BACK_GESTURE); - target.spanX = downX; - target.spanY = downY; - target.cardinality = completed ? 1 : 0; - LauncherEvent event = newLauncherEvent(action, target, newContainerTarget(containerType)); - - dispatchUserEvent(event, null); - } - - /** - * Currently logs following containers: workspace, allapps, widget tray. - */ - public final void resetElapsedContainerMillis(String reason) { - mElapsedContainerMillis = SystemClock.uptimeMillis(); - if (!IS_VERBOSE) { - return; - } - Log.d(TAG, "resetElapsedContainerMillis reason=" + reason); - - } - public final void startSession() { mSessionStarted = true; mElapsedSessionMillis = SystemClock.uptimeMillis(); diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java index eb5d1068ac..2695e66591 100644 --- a/src/com/android/launcher3/model/AllAppsList.java +++ b/src/com/android/launcher3/model/AllAppsList.java @@ -305,7 +305,7 @@ public class AllAppsList { * * @return the corresponding AppInfo or null */ - private @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName, + public @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName, @NonNull UserHandle user) { for (AppInfo info: data) { if (componentName.equals(info.componentName) && user.equals(info.user)) { diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java index 586333fd2e..5c85babb76 100644 --- a/src/com/android/launcher3/model/BaseLoaderResults.java +++ b/src/com/android/launcher3/model/BaseLoaderResults.java @@ -17,7 +17,6 @@ package com.android.launcher3.model; import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; -import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks; import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially; import android.util.Log; @@ -206,9 +205,6 @@ public abstract class BaseLoaderResults { mExtraItems.forEach(item -> executeCallbacksTask(c -> c.bindExtraContainerItems(item), mainExecutor)); - // Locate available spots for prediction using currentWorkspaceItems - IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons); - bindPredictedItems(gaps, mainExecutor); // In case of validFirstPage, only bind the first screen, and defer binding the // remaining screens after first onDraw (and an optional the fade animation whichever // happens later). @@ -261,11 +257,6 @@ public abstract class BaseLoaderResults { } } - private void bindPredictedItems(IntArray ranks, final Executor executor) { - ArrayList items = new ArrayList<>(mBgDataModel.cachedPredictedItems); - executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor); - } - protected void executeCallbacksTask(CallbackTask task, Executor executor) { executor.execute(() -> { if (mMyBindingId != mBgDataModel.lastBindId) { diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java index cf3251416e..49b40edb76 100644 --- a/src/com/android/launcher3/model/BgDataModel.java +++ b/src/com/android/launcher3/model/BgDataModel.java @@ -102,11 +102,6 @@ public class BgDataModel { */ public final IntSparseArrayMap extraItems = new IntSparseArrayMap<>(); - /** - * List of all cached predicted items visible on home screen - */ - public final ArrayList cachedPredictedItems = new ArrayList<>(); - /** * Maps all launcher activities to counts of their shortcuts. */ @@ -470,10 +465,5 @@ public class BgDataModel { default void bindExtraContainerItems(FixedContainerItems item) { } void bindAllApplications(AppInfo[] apps, int flags); - - /** - * Binds predicted appInfos at at available prediction slots. - */ - void bindPredictedItems(List appInfos, IntArray ranks); } } diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java index 5112304fb8..70d1b481e6 100644 --- a/src/com/android/launcher3/model/FirstScreenBroadcast.java +++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java @@ -17,25 +17,30 @@ package com.android.launcher3.model; import static android.os.Process.myUserHandle; +import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle; + +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.mapping; + import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInstaller.SessionInfo; +import android.os.UserHandle; import android.util.Log; import com.android.launcher3.LauncherSettings; import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.LauncherAppWidgetInfo; -import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; /** * Helper class to send broadcasts to package installers that have: @@ -61,26 +66,10 @@ public class FirstScreenBroadcast { private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken"; - private final MultiHashMap mPackagesForInstaller; + private final HashMap mSessionInfoForPackage; public FirstScreenBroadcast(HashMap sessionInfoForPackage) { - mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage); - } - - /** - * @return Map where the key is the package name of the installer, and the value is a list - * of packages with active sessions for that installer. - */ - private MultiHashMap getPackagesForInstaller( - HashMap sessionInfoForPackage) { - MultiHashMap packagesForInstaller = new MultiHashMap<>(); - for (Map.Entry entry : sessionInfoForPackage.entrySet()) { - if (myUserHandle().equals(entry.getKey().mUser)) { - packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(), - entry.getKey().mPackageName); - } - } - return packagesForInstaller; + mSessionInfoForPackage = sessionInfoForPackage; } /** @@ -88,9 +77,15 @@ public class FirstScreenBroadcast { * first screen. */ public void sendBroadcasts(Context context, List firstScreenItems) { - for (Map.Entry> entry : mPackagesForInstaller.entrySet()) { - sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems); - } + UserHandle myUser = myUserHandle(); + mSessionInfoForPackage + .values() + .stream() + .filter(info -> myUser.equals(getUserHandle(info))) + .collect(groupingBy(SessionInfo::getInstallerPackageName, + mapping(SessionInfo::getAppPackageName, Collectors.toSet()))) + .forEach((installer, packages) -> + sendBroadcastToInstaller(context, installer, packages, firstScreenItems)); } /** @@ -99,7 +94,7 @@ public class FirstScreenBroadcast { * @param firstScreenItems List of items on the first screen. */ private void sendBroadcastToInstaller(Context context, String installerPackageName, - List packages, List firstScreenItems) { + Set packages, List firstScreenItems) { Set folderItems = new HashSet<>(); Set workspaceItems = new HashSet<>(); Set hotseatItems = new HashSet<>(); diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 1dd8c112c6..a9e385fcc2 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -48,8 +48,6 @@ import android.util.Log; import android.util.LongSparseArray; import android.util.TimingLogger; -import androidx.annotation.WorkerThread; - import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; @@ -83,7 +81,6 @@ import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.IOUtils; import com.android.launcher3.util.LooperIdleLock; -import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.TraceHelper; @@ -92,8 +89,10 @@ import com.android.launcher3.widget.WidgetManagerHelper; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CancellationException; /** @@ -190,13 +189,13 @@ public class LoaderTask implements Runnable { try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) { List allShortcuts = new ArrayList<>(); loadWorkspace(allShortcuts); - loadCachedPredictions(); logger.addSplit("loadWorkspace"); verifyNotStopped(); mResults.bindWorkspace(); logger.addSplit("bindWorkspace"); + mModelDelegate.workspaceLoadComplete(); // Notify the installer packages of packages with active installs on the first screen. sendFirstScreenActiveInstallsBroadcast(); logger.addSplit("sendFirstScreenActiveInstallsBroadcast"); @@ -304,7 +303,7 @@ public class LoaderTask implements Runnable { final PackageManagerHelper pmHelper = new PackageManagerHelper(context); final boolean isSafeMode = pmHelper.isSafeMode(); final boolean isSdCardReady = Utilities.isBootCompleted(); - final MultiHashMap pendingPackages = new MultiHashMap<>(); + final Set pendingPackages = new HashSet<>(); boolean clearDb = false; try { @@ -485,7 +484,7 @@ public class LoaderTask implements Runnable { // SdCard is not ready yet. Package might get available, // once it is ready. Log.d(TAG, "Missing pkg, will check later: " + targetPkg); - pendingPackages.addToList(c.user, targetPkg); + pendingPackages.add(new PackageUserKey(targetPkg, c.user)); // Add the icon on the workspace anyway. allowMissingTarget = true; } else { @@ -839,24 +838,6 @@ public class LoaderTask implements Runnable { } } - @WorkerThread - private void loadCachedPredictions() { - synchronized (mBgDataModel) { - List componentKeys = - mApp.getPredictionModel().getPredictionComponentKeys(); - List l; - mBgDataModel.cachedPredictedItems.clear(); - for (ComponentKey key : componentKeys) { - l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user); - if (l.size() == 0) continue; - AppInfo info = new AppInfo(l.get(0), key.user, - mUserManagerState.isUserQuiet(key.user)); - mBgDataModel.cachedPredictedItems.add(info); - mIconCache.getTitleAndIcon(info, false); - } - } - } - private void sanitizeData() { Context context = mApp.getContext(); if (mItemsDeleted) { @@ -900,14 +881,6 @@ public class LoaderTask implements Runnable { PackageInstallInfo.fromInstallingState(info)); } } - for (AppInfo item : mBgDataModel.cachedPredictedItems) { - List l = mLauncherApps.getActivityList( - item.componentName.getPackageName(), item.user); - for (LauncherActivityInfo info : l) { - boolean quietMode = mUserManagerState.isUserQuiet(item.user); - mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info); - } - } mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED, mUserManagerState.isAnyProfileQuietModeEnabled()); diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java index 53e8a86078..3ed880906d 100644 --- a/src/com/android/launcher3/model/ModelDelegate.java +++ b/src/com/android/launcher3/model/ModelDelegate.java @@ -71,6 +71,12 @@ public class ModelDelegate implements ResourceBasedOverride { @WorkerThread public void loadItems(UserManagerState ums, Map pinnedShortcuts) { } + /** + * Called during loader after workspace loading is complete + */ + @WorkerThread + public void workspaceLoadComplete() { } + /** * Called when the delegate is no loner needed */ diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java index a8cc9ad3f3..9b5fac8734 100644 --- a/src/com/android/launcher3/model/ModelUtils.java +++ b/src/com/android/launcher3/model/ModelUtils.java @@ -36,8 +36,8 @@ import com.android.launcher3.util.IntSet; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.stream.IntStream; /** @@ -56,13 +56,7 @@ public class ModelUtils { ArrayList currentScreenItems, ArrayList otherScreenItems) { // Purge any null ItemInfos - Iterator iter = allWorkspaceItems.iterator(); - while (iter.hasNext()) { - ItemInfo i = iter.next(); - if (i == null) { - iter.remove(); - } - } + allWorkspaceItems.removeIf(Objects::isNull); // Order the set of items by their containers first, this allows use to walk through the // list sequentially, build up a list of containers that are in the specified screen, // as well as all items in those containers. diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java deleted file mode 100644 index cb3903d4ed..0000000000 --- a/src/com/android/launcher3/model/PredictionModel.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.model; - -import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; - -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.os.UserHandle; - -import androidx.annotation.AnyThread; -import androidx.annotation.WorkerThread; - -import com.android.launcher3.LauncherAppState; -import com.android.launcher3.R; -import com.android.launcher3.Utilities; -import com.android.launcher3.pm.UserCache; -import com.android.launcher3.util.ComponentKey; -import com.android.launcher3.util.Preconditions; -import com.android.launcher3.util.ResourceBasedOverride; - -import java.util.ArrayList; -import java.util.List; - -/** - * Model Helper for app predictions - */ -public class PredictionModel implements ResourceBasedOverride { - - private static final String CACHED_ITEMS_KEY = "predicted_item_keys"; - private static final int MAX_CACHE_ITEMS = 5; - - protected Context mContext; - private SharedPreferences mDevicePrefs; - private UserCache mUserCache; - - - /** - * Retrieve instance of this object that can be overridden in runtime based on the build - * variant of the application. - */ - public static PredictionModel newInstance(Context context) { - PredictionModel model = Overrides.getObject(PredictionModel.class, context, - R.string.prediction_model_class); - model.init(context); - return model; - } - - protected void init(Context context) { - mContext = context; - mDevicePrefs = Utilities.getDevicePrefs(mContext); - mUserCache = UserCache.INSTANCE.get(mContext); - - } - - /** - * Formats and stores a list of component key in device preferences. - */ - @AnyThread - public void cachePredictionComponentKeys(List componentKeys) { - MODEL_EXECUTOR.execute(() -> { - LauncherAppState appState = LauncherAppState.getInstance(mContext); - StringBuilder builder = new StringBuilder(); - int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS); - for (int i = 0; i < count; i++) { - builder.append(serializeComponentKeyToString(componentKeys.get(i))); - builder.append("\n"); - } - if (componentKeys.isEmpty() /* should invalidate loader items */) { - appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { - @Override - public void execute(LauncherAppState app, BgDataModel model, AllAppsList apps) { - model.cachedPredictedItems.clear(); - } - }); - } - mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply(); - }); - } - - /** - * parses and returns ComponentKeys saved by - * {@link PredictionModel#cachePredictionComponentKeys(List)} - */ - @WorkerThread - public List getPredictionComponentKeys() { - Preconditions.assertWorkerThread(); - ArrayList items = new ArrayList<>(); - String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, ""); - for (String line : cachedBlob.split("\n")) { - ComponentKey key = getComponentKeyFromSerializedString(line); - if (key != null) { - items.add(key); - } - - } - return items; - } - - private String serializeComponentKeyToString(ComponentKey componentKey) { - long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user); - return componentKey.componentName.flattenToString() + "#" + userSerialNumber; - } - - private ComponentKey getComponentKeyFromSerializedString(String str) { - int sep = str.indexOf('#'); - if (sep < 0 || (sep + 1) >= str.length()) { - return null; - } - ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep)); - if (componentName == null) { - return null; - } - try { - long serialNumber = Long.parseLong(str.substring(sep + 1)); - UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber); - return userHandle != null ? new ComponentKey(componentName, userHandle) : null; - } catch (NumberFormatException ex) { - return null; - } - } -} diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java index eb3cb52796..3798575f8a 100644 --- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java +++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java @@ -24,12 +24,11 @@ import android.os.UserHandle; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel; -import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageManagerHelper; +import com.android.launcher3.util.PackageUserKey; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Map.Entry; +import java.util.Set; /** * Helper class to re-query app status when SD-card becomes available. @@ -42,10 +41,9 @@ public class SdCardAvailableReceiver extends BroadcastReceiver { private final LauncherModel mModel; private final Context mContext; - private final MultiHashMap mPackages; + private final Set mPackages; - public SdCardAvailableReceiver(LauncherAppState app, - MultiHashMap packages) { + public SdCardAvailableReceiver(LauncherAppState app, Set packages) { mModel = app.getModel(); mContext = app.getContext(); mPackages = packages; @@ -55,19 +53,17 @@ public class SdCardAvailableReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { final LauncherApps launcherApps = context.getSystemService(LauncherApps.class); final PackageManagerHelper pmHelper = new PackageManagerHelper(context); - for (Entry> entry : mPackages.entrySet()) { - UserHandle user = entry.getKey(); + for (PackageUserKey puk : mPackages) { + UserHandle user = puk.mUser; final ArrayList packagesRemoved = new ArrayList<>(); final ArrayList packagesUnavailable = new ArrayList<>(); - for (String pkg : new HashSet<>(entry.getValue())) { - if (!launcherApps.isPackageEnabled(pkg, user)) { - if (pmHelper.isAppOnSdcard(pkg, user)) { - packagesUnavailable.add(pkg); - } else { - packagesRemoved.add(pkg); - } + if (!launcherApps.isPackageEnabled(puk.mPackageName, user)) { + if (pmHelper.isAppOnSdcard(puk.mPackageName, user)) { + packagesUnavailable.add(puk.mPackageName); + } else { + packagesRemoved.add(puk.mPackageName); } } if (!packagesRemoved.isEmpty()) { diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java index 88006ba6fb..6fedad1c9d 100644 --- a/src/com/android/launcher3/model/ShortcutsChangedTask.java +++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java @@ -25,11 +25,12 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.launcher3.util.ItemInfoMatcher; -import com.android.launcher3.util.MultiHashMap; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * Handles changes due to shortcut manager updates (deep shortcut changes) @@ -53,54 +54,53 @@ public class ShortcutsChangedTask extends BaseModelUpdateTask { public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { final Context context = app.getContext(); // Find WorkspaceItemInfo's that have changed on the workspace. - HashSet removedKeys = new HashSet<>(); - MultiHashMap keyToShortcutInfo = new MultiHashMap<>(); - HashSet allIds = new HashSet<>(); + ArrayList matchingWorkspaceItems = new ArrayList<>(); synchronized (dataModel) { dataModel.forAllWorkspaceItemInfos(mUser, si -> { if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) && mPackageName.equals(si.getIntent().getPackage())) { - keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si); - allIds.add(si.getDeepShortcutId()); + matchingWorkspaceItems.add(si); } }); } - final ArrayList updatedWorkspaceItemInfos = new ArrayList<>(); - if (!keyToShortcutInfo.isEmpty()) { + if (!matchingWorkspaceItems.isEmpty()) { // Update the workspace to reflect the changes to updated shortcuts residing on it. + List allLauncherKnownIds = matchingWorkspaceItems.stream() + .map(WorkspaceItemInfo::getDeepShortcutId) + .distinct() + .collect(Collectors.toList()); List shortcuts = new ShortcutRequest(context, mUser) - .forPackage(mPackageName, new ArrayList<>(allIds)) + .forPackage(mPackageName, allLauncherKnownIds) .query(ShortcutRequest.ALL); + + Set nonPinnedIds = new HashSet<>(allLauncherKnownIds); + ArrayList updatedWorkspaceItemInfos = new ArrayList<>(); for (ShortcutInfo fullDetails : shortcuts) { - ShortcutKey key = ShortcutKey.fromInfo(fullDetails); - List workspaceItemInfos = keyToShortcutInfo.remove(key); if (!fullDetails.isPinned()) { - // The shortcut was previously pinned but is no longer, so remove it from - // the workspace and our pinned shortcut counts. - // Note that we put this check here, after querying for full details, - // because there's a possible race condition between pinning and - // receiving this callback. - removedKeys.add(key); continue; } - for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) { - workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context); - app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails); - updatedWorkspaceItemInfos.add(workspaceItemInfo); - } + + String sid = fullDetails.getId(); + nonPinnedIds.remove(sid); + matchingWorkspaceItems + .stream() + .filter(itemInfo -> sid.equals(itemInfo.getDeepShortcutId())) + .forEach(workspaceItemInfo -> { + workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context); + app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails); + updatedWorkspaceItemInfos.add(workspaceItemInfo); + }); } - } - // If there are still entries in keyToShortcutInfo, that means that - // the corresponding shortcuts weren't passed in onShortcutsChanged(). This - // means they were cleared, so we remove and unpin them now. - removedKeys.addAll(keyToShortcutInfo.keySet()); - - bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos); - if (!keyToShortcutInfo.isEmpty()) { - deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys)); + bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos); + if (!nonPinnedIds.isEmpty()) { + deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys( + nonPinnedIds.stream() + .map(id -> new ShortcutKey(mPackageName, mUser, id)) + .collect(Collectors.toSet()))); + } } if (mUpdateIdMap) { diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java index 835f72d5be..80eeb22747 100644 --- a/src/com/android/launcher3/notification/NotificationInfo.java +++ b/src/com/android/launcher3/notification/NotificationInfo.java @@ -106,7 +106,6 @@ public class NotificationInfo implements View.OnClickListener { view, 0, 0, view.getWidth(), view.getHeight()).toBundle(); try { intent.send(null, 0, null, null, null, null, activityOptions); - launcher.getUserEventDispatcher().logNotificationLaunch(view, intent); launcher.getStatsLogManager().logger().withItemInfo(mItemInfo) .log(LAUNCHER_NOTIFICATION_LAUNCH_TAP); } catch (PendingIntent.CanceledException e) { diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 896fb186d4..26b32b8195 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -20,12 +20,9 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCU import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; -import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO; import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS; import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS; import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; -import static com.android.launcher3.userevent.nano.LauncherLogProto.Target; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.animation.AnimatorSet; @@ -147,17 +144,6 @@ public class PopupContainerWithArrow extends Arr return (type & TYPE_ACTION_POPUP) != 0; } - @Override - public void logActionCommand(int command) { - mLauncher.getUserEventDispatcher().logActionCommand( - command, mOriginalIcon, getLogContainerType()); - } - - @Override - public int getLogContainerType() { - return ContainerType.DEEPSHORTCUTS; - } - public OnClickListener getItemClickListener() { return (view) -> { mLauncher.getItemOnClickListener().onClick(view); @@ -495,18 +481,6 @@ public class PopupContainerWithArrow extends Arr } } - @Override - public void fillInLogContainerData(ItemInfo childInfo, Target child, - ArrayList parents) { - if (childInfo == NOTIFICATION_ITEM_INFO) { - child.itemType = ItemType.NOTIFICATION; - } else { - child.itemType = ItemType.DEEPSHORTCUT; - child.rank = childInfo.rank; - } - parents.add(newContainerTarget(ContainerType.DEEPSHORTCUTS)); - } - @Override protected void onCreateCloseAnimation(AnimatorSet anim) { // Animate original icon's text back in. diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java index 5a5f66832d..76048ba610 100644 --- a/src/com/android/launcher3/popup/PopupDataProvider.java +++ b/src/com/android/launcher3/popup/PopupDataProvider.java @@ -38,7 +38,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -202,20 +201,11 @@ public class PopupDataProvider implements NotificationListener.NotificationsChan } public List getWidgetsForPackageUser(PackageUserKey packageUserKey) { - for (WidgetListRowEntry entry : mAllWidgets) { - if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) { - ArrayList widgets = new ArrayList<>(entry.widgets); - // Remove widgets not associated with the correct user. - Iterator iterator = widgets.iterator(); - while (iterator.hasNext()) { - if (!iterator.next().user.equals(packageUserKey.mUser)) { - iterator.remove(); - } - } - return widgets.isEmpty() ? null : widgets; - } - } - return null; + return mAllWidgets.stream() + .filter(row -> row.pkgItem.packageName.equals(packageUserKey.mPackageName)) + .flatMap(row -> row.widgets.stream()) + .filter(widget -> packageUserKey.mUser.equals(widget.user)) + .collect(Collectors.toList()); } /** diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 79984889ef..81302ac1f7 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -99,7 +99,7 @@ public abstract class SystemShortcut extends Ite final List widgets = launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey( itemInfo.getTargetComponent().getPackageName(), itemInfo.user)); - if (widgets == null) { + if (widgets.isEmpty()) { return null; } return new Widgets(launcher, itemInfo); diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index 31c3014874..2b04365ddc 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -196,9 +196,6 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity @Override public void bindItems(List shortcuts, boolean forceAnimateIcons) { } - @Override - public void bindPredictedItems(List appInfos, IntArray ranks) { } - @Override public void bindScreens(IntArray orderedScreenIds) { } diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java index e98af35563..d26bb5779e 100644 --- a/src/com/android/launcher3/util/ItemInfoMatcher.java +++ b/src/com/android/launcher3/util/ItemInfoMatcher.java @@ -27,6 +27,7 @@ import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; import java.util.HashSet; +import java.util.Set; /** * A utility class to check for {@link ItemInfo} @@ -99,7 +100,7 @@ public interface ItemInfoMatcher { return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user); } - static ItemInfoMatcher ofShortcutKeys(HashSet keys) { + static ItemInfoMatcher ofShortcutKeys(Set keys) { return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && keys.contains(ShortcutKey.fromItemInfo(info)); } diff --git a/src/com/android/launcher3/util/MultiHashMap.java b/src/com/android/launcher3/util/MultiHashMap.java deleted file mode 100644 index b7275c1f76..0000000000 --- a/src/com/android/launcher3/util/MultiHashMap.java +++ /dev/null @@ -1,52 +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.util; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * A utility map from keys to an ArrayList of values. - */ -public class MultiHashMap extends HashMap> { - - public MultiHashMap() { } - - public MultiHashMap(int size) { - super(size); - } - - public void addToList(K key, V value) { - ArrayList list = get(key); - if (list == null) { - list = new ArrayList<>(); - list.add(value); - put(key, list); - } else { - list.add(value); - } - } - - @Override - public MultiHashMap clone() { - MultiHashMap map = new MultiHashMap<>(size()); - for (Entry> entry : entrySet()) { - map.put(entry.getKey(), new ArrayList(entry.getValue())); - } - return map; - } -} diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java index 6e5e7d9355..1b33197879 100644 --- a/src/com/android/launcher3/util/OnboardingPrefs.java +++ b/src/com/android/launcher3/util/OnboardingPrefs.java @@ -44,7 +44,8 @@ public class OnboardingPrefs { */ @StringDef(value = { HOME_BOUNCE_SEEN, - SHELF_BOUNCE_SEEN + SHELF_BOUNCE_SEEN, + HOTSEAT_LONGPRESS_TIP_SEEN }) @Retention(RetentionPolicy.SOURCE) public @interface EventBoolKey {} diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java index b4a6b141ce..1f12a2f8c8 100644 --- a/src/com/android/launcher3/views/ArrowTipView.java +++ b/src/com/android/launcher3/views/ArrowTipView.java @@ -86,10 +86,6 @@ public class ArrowTipView extends AbstractFloatingView { } } - @Override - public void logActionCommand(int command) { - } - @Override protected boolean isOfType(int type) { return (type & TYPE_ON_BOARD_POPUP) != 0; diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java index 9582232279..011f6de8e2 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -121,9 +121,6 @@ public class FloatingSurfaceView extends AbstractFloatingView implements launcher.getDragLayer().addView(view); } - @Override - public void logActionCommand(int command) { } - @Override protected boolean isOfType(int type) { return (type & TYPE_ICON_SURFACE) != 0; diff --git a/src/com/android/launcher3/views/HeroSearchResultView.java b/src/com/android/launcher3/views/HeroSearchResultView.java index 10f3c41f35..761ef0d639 100644 --- a/src/com/android/launcher3/views/HeroSearchResultView.java +++ b/src/com/android/launcher3/views/HeroSearchResultView.java @@ -37,20 +37,20 @@ import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.touch.ItemLongClickListener; -import com.android.launcher3.userevent.nano.LauncherLogProto; -import java.util.ArrayList; import java.util.List; /** * A view representing a high confidence app search result that includes shortcuts */ public class HeroSearchResultView extends LinearLayout implements DragSource, - AllAppsSearchBarController.PayloadResultHandler> { + AllAppsSearchBarController.PayloadResultHandler> { + public static final int MAX_SHORTCUTS_COUNT = 2; BubbleTextView mBubbleTextView; View mIconView; BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2]; @@ -99,15 +99,15 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, * Apply {@link ItemInfo} for appIcon and shortcut Icons */ @Override - public void applyAdapterInfo(AdapterItemWithPayload> adapterItem) { + public void applyAdapterInfo(AdapterItemWithPayload> adapterItem) { mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo); mIconView.setBackground(mBubbleTextView.getIcon()); mIconView.setTag(adapterItem.appInfo); - List shorcutInfos = adapterItem.getPayload(); + List shorcutInfos = adapterItem.getPayload(); for (int i = 0; i < mDeepShortcutTextViews.length; i++) { mDeepShortcutTextViews[i].setVisibility(shorcutInfos.size() > i ? VISIBLE : GONE); if (i < shorcutInfos.size()) { - mDeepShortcutTextViews[i].applyFromWorkspaceItem(shorcutInfos.get(i)); + mDeepShortcutTextViews[i].applyFromItemInfoWithIcon(shorcutInfos.get(i)); } } } @@ -118,12 +118,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource, mBubbleTextView.setIconVisible(true); } - @Override - public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child, - ArrayList parents) { - - } - private void setWillDrawIcon(boolean willDraw) { mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE); } diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java index 3ef778b575..6e3f0ce690 100644 --- a/src/com/android/launcher3/views/ListenerView.java +++ b/src/com/android/launcher3/views/ListenerView.java @@ -84,11 +84,6 @@ public class ListenerView extends AbstractFloatingView { mIsOpen = false; } - @Override - public void logActionCommand(int command) { - // Users do not interact with FloatingIconView, so there is nothing to log here. - } - @Override protected boolean isOfType(int type) { return (type & TYPE_LISTENER) != 0; diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 9ad2331112..3ec20d57a1 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -107,11 +107,6 @@ public class OptionsPopupView extends ArrowPopup return true; } - @Override - public void logActionCommand(int command) { - // TODO: - } - @Override protected boolean isOfType(int type) { return (type & TYPE_OPTIONS_POPUP) != 0; diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java index 7f0765becd..77cec80fd7 100644 --- a/src/com/android/launcher3/views/ScrimView.java +++ b/src/com/android/launcher3/views/ScrimView.java @@ -32,6 +32,7 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.uioverrides.WallpaperColorInfo; import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener; import com.android.launcher3.util.Themes; @@ -41,6 +42,7 @@ import com.android.launcher3.util.Themes; */ public class ScrimView extends View implements Insettable, OnChangeListener { + private static final float SCRIM_ALPHA = .75f; protected final T mLauncher; private final WallpaperColorInfo mWallpaperColorInfo; protected final int mEndScrim; @@ -59,7 +61,11 @@ public class ScrimView extends View implements Insettable, O super(context, attrs); mLauncher = Launcher.cast(Launcher.getLauncher(context)); mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context); - mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor); + int endScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor); + if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { + endScrim = ColorUtils.setAlphaComponent(endScrim, (int) (255 * SCRIM_ALPHA)); + } + mEndScrim = endScrim; mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f; mMaxScrimAlpha = 0.7f; diff --git a/src/com/android/launcher3/views/SearchSettingsRowView.java b/src/com/android/launcher3/views/SearchSettingsRowView.java new file mode 100644 index 0000000000..08c78ffc3a --- /dev/null +++ b/src/com/android/launcher3/views/SearchSettingsRowView.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.views; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.Launcher; +import com.android.launcher3.R; +import com.android.launcher3.allapps.AllAppsGridAdapter; +import com.android.launcher3.allapps.search.AllAppsSearchBarController; + +import java.util.ArrayList; + +/** + * A row of tappable TextViews with a breadcrumb for settings search. + */ +public class SearchSettingsRowView extends LinearLayout implements + View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler { + + private TextView mTitleView; + private TextView mDescriptionView; + private TextView mBreadcrumbsView; + private Intent mIntent; + + public SearchSettingsRowView(@NonNull Context context) { + super(context); + } + + public SearchSettingsRowView(@NonNull Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public SearchSettingsRowView(@NonNull Context context, @Nullable AttributeSet attrs, + int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mTitleView = findViewById(R.id.title); + mDescriptionView = findViewById(R.id.description); + mBreadcrumbsView = findViewById(R.id.breadcrumbs); + setOnClickListener(this); + } + + @Override + public void applyAdapterInfo( + AllAppsGridAdapter.AdapterItemWithPayload adapterItemWithPayload) { + Bundle bundle = adapterItemWithPayload.getPayload(); + mIntent = bundle.getParcelable("intent"); + showIfAvailable(mTitleView, bundle.getString("title")); + showIfAvailable(mDescriptionView, bundle.getString("description")); + ArrayList breadcrumbs = bundle.getStringArrayList("breadcrumbs"); + //TODO: implement RTL friendly breadcrumbs view + showIfAvailable(mBreadcrumbsView, breadcrumbs != null + ? String.join(" > ", breadcrumbs) : null); + adapterItemWithPayload.setSelectionHandler(() -> onClick(this)); + } + + private void showIfAvailable(TextView view, @Nullable String string) { + if (TextUtils.isEmpty(string)) { + view.setVisibility(GONE); + } else { + view.setVisibility(VISIBLE); + view.setText(string); + } + } + + @Override + public void onClick(View view) { + if (mIntent == null) return; + // TODO: create ItemInfo object and then use it to call startActivityForResult for proper + // WW logging + Launcher launcher = Launcher.getLauncher(view.getContext()); + launcher.startActivityForResult(mIntent, 0); + } +} diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java index 513ce59a62..49fcd2ebde 100644 --- a/src/com/android/launcher3/views/Snackbar.java +++ b/src/com/android/launcher3/views/Snackbar.java @@ -166,11 +166,6 @@ public class Snackbar extends AbstractFloatingView { } } - @Override - public void logActionCommand(int command) { - // TODO - } - @Override protected boolean isOfType(int type) { return (type & TYPE_SNACKBAR) != 0; diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java index d35a38fba8..d6737db725 100644 --- a/src/com/android/launcher3/views/WorkEduView.java +++ b/src/com/android/launcher3/views/WorkEduView.java @@ -38,7 +38,6 @@ import com.android.launcher3.allapps.AllAppsPagedView; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.statemanager.StateManager.StateListener; -import com.android.launcher3.userevent.nano.LauncherLogProto; /** * On boarding flow for users right after setting up work profile @@ -88,16 +87,6 @@ public class WorkEduView extends AbstractSlideInView mLauncher.getStateManager().removeStateListener(this); } - @Override - public void logActionCommand(int command) { - // Since this is on-boarding popup, it is not a user controlled action. - } - - @Override - public int getLogContainerType() { - return LauncherLogProto.ContainerType.TIP; - } - @Override protected boolean isOfType(int type) { return (type & TYPE_ON_BOARD_POPUP) != 0; diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java index 3e5113a231..01af96cb33 100644 --- a/src/com/android/launcher3/widget/BaseWidgetSheet.java +++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java @@ -16,7 +16,6 @@ package com.android.launcher3.widget; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; -import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import android.content.Context; import android.graphics.Point; @@ -31,20 +30,15 @@ import com.android.launcher3.DropTarget.DragObject; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.dragndrop.DragOptions; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.uioverrides.WallpaperColorInfo; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; -import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; import com.android.launcher3.views.AbstractSlideInView; -import java.util.ArrayList; - /** * Base class for various widgets popup */ @@ -149,28 +143,6 @@ abstract class BaseWidgetSheet extends AbstractSlideInView isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV); } - @Override - public void fillInLogContainerData(ItemInfo childInfo, Target child, - ArrayList parents) { - Target target = newContainerTarget(ContainerType.WIDGETS); - target.cardinality = getElementsRowCount(); - parents.add(target); - } - - @Override - public final void logActionCommand(int command) { - Target target = newContainerTarget(getLogContainerType()); - target.cardinality = getElementsRowCount(); - mLauncher.getUserEventDispatcher().logActionCommand(command, target); - } - - @Override - public int getLogContainerType() { - return ContainerType.WIDGETS; - } - - protected abstract int getElementsRowCount(); - protected SystemUiController getSystemUiController() { return mLauncher.getSystemUiController(); } diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java index 30be7a673d..3585a1814b 100644 --- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java +++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java @@ -179,11 +179,6 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable { setPadding(leftInset, getPaddingTop(), rightInset, bottomInset); } - @Override - protected int getElementsRowCount() { - return 1; - } - @Override protected Pair getAccessibilityTarget() { return Pair.create(findViewById(R.id.title), getContext().getString( diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java index 68a3ec51ee..4c8c339998 100644 --- a/src/com/android/launcher3/widget/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java @@ -242,11 +242,6 @@ public class WidgetsFullSheet extends BaseWidgetSheet return launcher.findViewById(R.id.widgets_list_view); } - @Override - protected int getElementsRowCount() { - return mAdapter.getItemCount(); - } - @Override public void addHintCloseAnim( float distanceToMove, Interpolator interpolator, PendingAnimation target) { diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index a45521dee7..5bf96906be 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -23,9 +23,12 @@ import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import com.android.launcher3.icons.IconCache; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.Adapter; + import com.android.launcher3.R; import com.android.launcher3.WidgetPreviewLoader; +import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.WidgetItem; import com.android.launcher3.util.LabelComparator; @@ -34,9 +37,6 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; -import androidx.recyclerview.widget.RecyclerView; -import androidx.recyclerview.widget.RecyclerView.Adapter; - /** * List view adapter for the widget tray. * @@ -128,7 +128,7 @@ public class WidgetsListAdapter extends Adapter { int childCount = row.getChildCount(); if (expectedChildCount > childCount) { - for (int i = childCount ; i < expectedChildCount; i++) { + for (int i = childCount; i < expectedChildCount; i++) { if ((i & 1) == 1) { // Add a divider for odd index mLayoutInflater.inflate(R.layout.widget_list_divider, row); @@ -144,24 +144,24 @@ public class WidgetsListAdapter extends Adapter { } } } else if (expectedChildCount < childCount) { - for (int i = expectedChildCount ; i < childCount; i++) { + for (int i = expectedChildCount; i < childCount; i++) { row.getChildAt(i).setVisibility(View.GONE); } } // Bind the views in the application info section. - holder.title.applyFromPackageItemInfo(entry.pkgItem); + holder.title.applyFromItemInfoWithIcon(entry.pkgItem); // Bind the view in the widget horizontal tray region. - for (int i=0; i < infoList.size(); i++) { - WidgetCell widget = (WidgetCell) row.getChildAt(2*i); + for (int i = 0; i < infoList.size(); i++) { + WidgetCell widget = (WidgetCell) row.getChildAt(2 * i); widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader); widget.setApplyBitmapDeferred(mApplyBitmapDeferred); widget.ensurePreview(); widget.setVisibility(View.VISIBLE); if (i > 0) { - row.getChildAt(2*i - 1).setVisibility(View.VISIBLE); + row.getChildAt(2 * i - 1).setVisibility(View.VISIBLE); } } } @@ -185,7 +185,7 @@ public class WidgetsListAdapter extends Adapter { @Override public void onViewRecycled(WidgetsRowViewHolder holder) { int total = holder.cellContainer.getChildCount(); - for (int i = 0; i < total; i+=2) { + for (int i = 0; i < total; i += 2) { WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i); widget.clear(); } diff --git a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java b/src_plugins/com/android/systemui/plugins/UserEventPlugin.java deleted file mode 100644 index 0e3664ae3f..0000000000 --- a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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.systemui.plugins; - -import com.android.systemui.plugins.annotations.ProvidesInterface; - -/** - * Implement this plugin interface to access user event log on the device for prototype purpose. - * NOTE: plugin is for internal prototype only and is not visible in production environment. - */ -@ProvidesInterface(action = UserEventPlugin.ACTION, version = UserEventPlugin.VERSION) -public interface UserEventPlugin extends Plugin { - String ACTION = "com.android.launcher3.action.PLUGIN_USER_EVENT_LOG"; - int VERSION = 1; - - /** - * Callback to be triggered whenever an user event occurs. - */ - void onUserEvent(Object event); -} diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java index a64df62c3b..34ebbac59a 100644 --- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java +++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java @@ -26,7 +26,6 @@ import com.android.launcher3.icons.ComponentWithLabelAndIcon; import com.android.launcher3.icons.IconCache; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.pm.ShortcutConfigActivityInfo; -import com.android.launcher3.util.MultiHashMap; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.Preconditions; import com.android.launcher3.widget.WidgetItemComparator; @@ -36,11 +35,12 @@ import com.android.launcher3.widget.WidgetManagerHelper; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Widgets data model that is used by the adapters of the widget views and controllers. @@ -57,9 +57,7 @@ public class WidgetsModel { private static final boolean DEBUG = false; /* Map of widgets and shortcuts that are tracked per package. */ - private final MultiHashMap mWidgetsList = new MultiHashMap<>(); - - private AppFilter mAppFilter; + private final Map> mWidgetsList = new HashMap<>(); /** * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row @@ -74,8 +72,9 @@ public class WidgetsModel { AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context); WidgetItemComparator widgetComparator = new WidgetItemComparator(); - for (Map.Entry> entry : mWidgetsList.entrySet()) { - WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue()); + for (Map.Entry> entry : mWidgetsList.entrySet()) { + WidgetListRowEntry row = new WidgetListRowEntry( + entry.getKey(), new ArrayList<>(entry.getValue())); row.titleSectionName = (row.pkgItem.title == null) ? "" : indexer.computeSectionName(row.pkgItem.title); Collections.sort(row.widgets, widgetComparator); @@ -146,77 +145,42 @@ public class WidgetsModel { if (packageUser == null) { mWidgetsList.clear(); } else { - // Only clear the widgets for the given package/user. - PackageItemInfo packageItem = null; - for (PackageItemInfo item : mWidgetsList.keySet()) { - if (item.packageName.equals(packageUser.mPackageName)) { - packageItem = item; - break; - } - } + PackageItemInfo packageItem = mWidgetsList.keySet() + .stream() + .filter(item -> item.packageName.equals(packageUser.mPackageName)) + .findFirst() + .orElse(null); if (packageItem != null) { // We want to preserve the user that was on the packageItem previously, // so add it to tmpPackageItemInfos here to avoid creating a new entry. tmpPackageItemInfos.put(packageItem.packageName, packageItem); - Iterator widgetItemIterator = mWidgetsList.get(packageItem).iterator(); - while (widgetItemIterator.hasNext()) { - WidgetItem nextWidget = widgetItemIterator.next(); - if (nextWidget.componentName.getPackageName().equals(packageUser.mPackageName) - && nextWidget.user.equals(packageUser.mUser)) { - widgetItemIterator.remove(); - } - } + // Add the widgets for other users in the rawList as it only contains widgets for + // packageUser + List otherUserItems = mWidgetsList.remove(packageItem); + otherUserItems.removeIf(w -> w.user.equals(packageUser.mUser)); + rawWidgetsShortcuts.addAll(otherUserItems); } } - InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); UserHandle myUser = Process.myUserHandle(); // add and update. - for (WidgetItem item : rawWidgetsShortcuts) { - if (item.widgetInfo != null) { - if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) { - // Widget is hidden from picker - continue; - } - - // Ensure that all widgets we show can be added on a workspace of this size - int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX); - int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY); - if (minSpanX > idp.numColumns || minSpanY > idp.numRows) { - if (DEBUG) { - Log.d(TAG, String.format( - "Widget %s : (%d X %d) can't fit on this device", - item.componentName, minSpanX, minSpanY)); + mWidgetsList.putAll(rawWidgetsShortcuts.stream() + .filter(new WidgetValidityCheck(app)) + .collect(Collectors.groupingBy(item -> { + String packageName = item.componentName.getPackageName(); + PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); + if (pInfo == null) { + pInfo = new PackageItemInfo(packageName); + pInfo.user = item.user; + tmpPackageItemInfos.put(packageName, pInfo); + } else if (!myUser.equals(pInfo.user)) { + // Keep updating the user, until we get the primary user. + pInfo.user = item.user; } - continue; - } - } - - if (mAppFilter == null) { - mAppFilter = AppFilter.newInstance(app.getContext()); - } - if (!mAppFilter.shouldShowApp(item.componentName)) { - if (DEBUG) { - Log.d(TAG, String.format("%s is filtered and not added to the widget tray.", - item.componentName)); - } - continue; - } - - String packageName = item.componentName.getPackageName(); - PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); - if (pInfo == null) { - pInfo = new PackageItemInfo(packageName); - pInfo.user = item.user; - tmpPackageItemInfos.put(packageName, pInfo); - } else if (!myUser.equals(pInfo.user)) { - // Keep updating the user, until we get the primary user. - pInfo.user = item.user; - } - mWidgetsList.addToList(pInfo, item); - } + return pInfo; + }))); // Update each package entry IconCache iconCache = app.getIconCache(); @@ -227,9 +191,9 @@ public class WidgetsModel { public void onPackageIconsUpdated(Set packageNames, UserHandle user, LauncherAppState app) { - for (Entry> entry : mWidgetsList.entrySet()) { + for (Entry> entry : mWidgetsList.entrySet()) { if (packageNames.contains(entry.getKey().packageName)) { - ArrayList items = entry.getValue(); + List items = entry.getValue(); int count = items.size(); for (int i = 0; i < count; i++) { WidgetItem item = items.get(i); @@ -249,7 +213,7 @@ public class WidgetsModel { public WidgetItem getWidgetProviderInfoByProviderName( ComponentName providerName) { - ArrayList widgetsList = mWidgetsList.get( + List widgetsList = mWidgetsList.get( new PackageItemInfo(providerName.getPackageName())); if (widgetsList == null) { return null; @@ -262,4 +226,46 @@ public class WidgetsModel { } return null; } + + private static class WidgetValidityCheck implements Predicate { + + private final InvariantDeviceProfile mIdp; + private final AppFilter mAppFilter; + + WidgetValidityCheck(LauncherAppState app) { + mIdp = app.getInvariantDeviceProfile(); + mAppFilter = AppFilter.newInstance(app.getContext()); + } + + @Override + public boolean test(WidgetItem item) { + if (item.widgetInfo != null) { + if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) { + // Widget is hidden from picker + return false; + } + + // Ensure that all widgets we show can be added on a workspace of this size + int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX); + int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY); + if (minSpanX > mIdp.numColumns || minSpanY > mIdp.numRows) { + if (DEBUG) { + Log.d(TAG, String.format( + "Widget %s : (%d X %d) can't fit on this device", + item.componentName, minSpanX, minSpanY)); + } + return false; + } + } + if (!mAppFilter.shouldShowApp(item.componentName)) { + if (DEBUG) { + Log.d(TAG, String.format("%s is filtered and not added to the widget tray.", + item.componentName)); + } + return false; + } + + return true; + } + } } \ No newline at end of file