Merging from ub-launcher3-master @ build 6799015
Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-master_master_6799015.html Change-Id: Ib9d52ba333f2982d2420cab42a0935e8e8a303b3
This commit is contained in:
@@ -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 := \
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
|
||||
<string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string>
|
||||
|
||||
<string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
|
||||
|
||||
<string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ComponentKey, WorkspaceItemInfo> mShortcuts;
|
||||
private final Map<String, InstantAppItemInfo> 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<ShortcutKey> shortcutKeys, List<String> pkgNames) {
|
||||
if (!shortcutKeys.isEmpty()) {
|
||||
mWorker.removeMessages(BG_MSG_LOAD_SHORTCUTS);
|
||||
Message.obtain(mWorker, BG_MSG_LOAD_SHORTCUTS, shortcutKeys).sendToTarget();
|
||||
}
|
||||
if (!pkgNames.isEmpty()) {
|
||||
mWorker.removeMessages(BG_MSG_LOAD_INSTANT_APPS);
|
||||
Message.obtain(mWorker, BG_MSG_LOAD_INSTANT_APPS, pkgNames).sendToTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleWorkerMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case BG_MSG_LOAD_SHORTCUTS: {
|
||||
List<ShortcutKey> shortcutKeys = msg.obj != null ?
|
||||
(List<ShortcutKey>) msg.obj : Collections.EMPTY_LIST;
|
||||
Map<ShortcutKey, WorkspaceItemInfo> shortcutKeyAndInfos = new ArrayMap<>();
|
||||
for (ShortcutKey shortcutKey : shortcutKeys) {
|
||||
WorkspaceItemInfo workspaceItemInfo = loadShortcutWorker(shortcutKey);
|
||||
if (workspaceItemInfo != null) {
|
||||
shortcutKeyAndInfos.put(shortcutKey, workspaceItemInfo);
|
||||
}
|
||||
}
|
||||
Message.obtain(mUiHandler, UI_MSG_UPDATE_SHORTCUTS, shortcutKeyAndInfos)
|
||||
.sendToTarget();
|
||||
return true;
|
||||
}
|
||||
case BG_MSG_LOAD_INSTANT_APPS: {
|
||||
List<String> pkgNames = msg.obj != null ?
|
||||
(List<String>) msg.obj : Collections.EMPTY_LIST;
|
||||
List<InstantAppItemInfo> instantAppItemInfos = new ArrayList<>();
|
||||
for (String pkgName : pkgNames) {
|
||||
InstantAppItemInfo instantAppItemInfo = loadInstantApp(pkgName);
|
||||
if (instantAppItemInfo != null) {
|
||||
instantAppItemInfos.add(instantAppItemInfo);
|
||||
}
|
||||
}
|
||||
Message.obtain(mUiHandler, UI_MSG_UPDATE_INSTANT_APPS, instantAppItemInfos)
|
||||
.sendToTarget();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleUiMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case UI_MSG_UPDATE_SHORTCUTS: {
|
||||
mShortcuts.clear();
|
||||
mShortcuts.putAll((Map<ShortcutKey, WorkspaceItemInfo>) msg.obj);
|
||||
mOnUpdateCallback.run();
|
||||
return true;
|
||||
}
|
||||
case UI_MSG_UPDATE_INSTANT_APPS: {
|
||||
List<InstantAppItemInfo> instantAppItemInfos = (List<InstantAppItemInfo>) msg.obj;
|
||||
mInstantApps.clear();
|
||||
for (InstantAppItemInfo instantAppItemInfo : instantAppItemInfos) {
|
||||
mInstantApps.put(instantAppItemInfo.getTargetComponent().getPackageName(),
|
||||
instantAppItemInfo);
|
||||
}
|
||||
mOnUpdateCallback.run();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, String.format("Cache size: %d, Cache: %s",
|
||||
mInstantApps.size(), mInstantApps.toString()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private WorkspaceItemInfo loadShortcutWorker(ShortcutKey shortcutKey) {
|
||||
List<ShortcutInfo> 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<ResolveInfo> resolveInfos = pm.queryIntentActivities(
|
||||
mainIntent, MATCH_INSTANT | PackageManager.GET_META_DATA);
|
||||
String url = null;
|
||||
for (ResolveInfo resolveInfo : resolveInfos) {
|
||||
if (resolveInfo.activityInfo.metaData != null
|
||||
&& resolveInfo.activityInfo.metaData.containsKey(DEFAULT_URL)) {
|
||||
url = resolveInfo.activityInfo.metaData.getString(DEFAULT_URL);
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public InstantAppItemInfo getInstantApp(String pkgName) {
|
||||
return mInstantApps.get(pkgName);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
public WorkspaceItemInfo getShortcutInfo(ComponentKey key) {
|
||||
return mShortcuts.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* requests and caches icons for app targets
|
||||
*/
|
||||
public void updateDependencies(List<ComponentKeyMapper> componentKeyMappers,
|
||||
AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) {
|
||||
List<String> instantAppsToLoad = new ArrayList<>();
|
||||
List<ShortcutKey> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<WorkspaceItemInfo> mPredictedApps;
|
||||
private HotseatEduDialog mActiveDialog;
|
||||
|
||||
private ArrayList<ItemInfo> 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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+55
-327
@@ -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<QuickstepLauncher>,
|
||||
InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
|
||||
IconCache.ItemInfoUpdateReceiver, DragSource {
|
||||
SystemShortcut.Factory<QuickstepLauncher>, 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<ItemInfo> mPredictedItems = Collections.emptyList();
|
||||
|
||||
private List<ComponentKeyMapper> 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<PredictedAppIcon.PredictedIconOutlineDrawing> 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<WorkspaceItemInfo> 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<WorkspaceItemInfo> 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<HotseatPredictionController> 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<AppInfo> apps, IntArray ranks) {
|
||||
if (hasPredictions() && mAppPredictor != null) {
|
||||
mAppPredictor.requestPredictionUpdate();
|
||||
fillGapsWithPrediction();
|
||||
return;
|
||||
}
|
||||
int count = Math.min(ranks.size(), apps.size());
|
||||
List<WorkspaceItemInfo> 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<AppTarget> appTargets) {
|
||||
mComponentKeyMappers.clear();
|
||||
if (appTargets.isEmpty()) {
|
||||
mRestoreHelper.restoreBackup();
|
||||
}
|
||||
StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
|
||||
ArrayList<ComponentKey> 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<WorkspaceItemInfo> mapToWorkspaceItemInfo(
|
||||
List<ComponentKeyMapper> components) {
|
||||
AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore();
|
||||
if (allAppsStore.getApps().length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<WorkspaceItemInfo> 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<PredictedAppIcon> getPredictedIcons() {
|
||||
@@ -465,7 +293,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||
}
|
||||
|
||||
private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> 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<QuickstepLauncher> 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<LauncherLogProto.Target> 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<ComponentKeyMapper> 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<ComponentKeyMapper> 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
|
||||
|
||||
@@ -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<Bundle> 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<AppTargetEvent> events = new ArrayList<>();
|
||||
ArrayList<ItemInfo> 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<AppTarget> 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<AppTargetEvent> events = new ArrayList<>();
|
||||
ArrayList<ItemInfo> 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<AppTarget> 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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<AppTargetEvent> mCallback;
|
||||
private final ObjIntConsumer<AppTargetEvent> mCallback;
|
||||
|
||||
public AppEventProducer(Context context, Consumer<AppTargetEvent> callback) {
|
||||
private LauncherAtom.ItemInfo mLastDragItem;
|
||||
|
||||
public AppEventProducer(Context context, ObjIntConsumer<AppTargetEvent> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AppTarget> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SystemShortcut.Factory> getSupportedShortcuts() {
|
||||
if (mHotseatPredictionController != null) {
|
||||
@@ -222,20 +208,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPredictedItems(List<AppInfo> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.<RecentsView>getOverviewPanel().getRunningTaskView();
|
||||
if (taskView != null) {
|
||||
launcher.getUserEventDispatcher().logActionCommand(Action.Command.BACK,
|
||||
newContainerTarget(ContainerType.OVERVIEW));
|
||||
taskView.launchTask(true);
|
||||
} else {
|
||||
super.onBackPressed(launcher);
|
||||
|
||||
+3
-4
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<O
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onOverviewToggle() {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
|
||||
mOverviewCommandHelper.onOverviewToggle();
|
||||
@@ -165,7 +162,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
@BinderThread
|
||||
@Override
|
||||
public void onTip(int actionType, int viewType) {
|
||||
mOverviewCommandHelper.onTip(actionType, viewType);
|
||||
// Please delete this method from the interface
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@@ -189,18 +186,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
@BinderThread
|
||||
public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
|
||||
boolean gestureSwipeLeft) {
|
||||
if (mOverviewComponentObserver == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BaseActivityInterface activityInterface =
|
||||
mOverviewComponentObserver.getActivityInterface();
|
||||
UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
|
||||
isButton, gestureSwipeLeft, activityInterface.getContainerType());
|
||||
|
||||
if (completed && !isButton && shouldNotifyBackGesture()) {
|
||||
UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
|
||||
}
|
||||
// Remove this method from the interface
|
||||
}
|
||||
|
||||
@BinderThread
|
||||
@@ -263,10 +249,10 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||
mMainChoreographer = Choreographer.getInstance();
|
||||
mAM = ActivityManagerWrapper.getInstance();
|
||||
mDeviceState = new RecentsAnimationDeviceState(this);
|
||||
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
|
||||
mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
|
||||
mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
|
||||
mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
|
||||
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
|
||||
ProtoTracer.INSTANCE.get(this).add(this);
|
||||
|
||||
sConnected = true;
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.quickstep.logging;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
|
||||
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
|
||||
import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
|
||||
import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
|
||||
import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
|
||||
import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
|
||||
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.systemui.shared.system.MetricsLoggerCompat;
|
||||
|
||||
/**
|
||||
* This class handles AOSP MetricsLogger function calls and logging around
|
||||
* quickstep interactions.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class UserEventDispatcherExtension extends UserEventDispatcher {
|
||||
|
||||
public static final int ALL_APPS_PREDICTION_TIPS = 2;
|
||||
|
||||
private static final String TAG = "UserEventDispatcher";
|
||||
|
||||
public UserEventDispatcherExtension(Context context) { }
|
||||
|
||||
public void logStateChangeAction(int action, int dir, int downX, int downY,
|
||||
int srcChildTargetType, int srcParentContainerType,
|
||||
int dstContainerType, int pageIndex) {
|
||||
new MetricsLoggerCompat().visibility(MetricsLoggerCompat.OVERVIEW_ACTIVITY,
|
||||
dstContainerType == LauncherLogProto.ContainerType.TASKSWITCHER);
|
||||
super.logStateChangeAction(action, dir, downX, downY, srcChildTargetType,
|
||||
srcParentContainerType, dstContainerType, pageIndex);
|
||||
}
|
||||
|
||||
public void logActionTip(int actionType, int viewType) {
|
||||
LauncherLogProto.Action action = new LauncherLogProto.Action();
|
||||
LauncherLogProto.Target target = new LauncherLogProto.Target();
|
||||
switch(actionType) {
|
||||
case VISIBLE:
|
||||
action.type = LauncherLogProto.Action.Type.TIP;
|
||||
target.type = LauncherLogProto.Target.Type.CONTAINER;
|
||||
target.containerType = LauncherLogProto.ContainerType.TIP;
|
||||
break;
|
||||
case DISMISS:
|
||||
action.type = LauncherLogProto.Action.Type.TOUCH;
|
||||
action.touch = LauncherLogProto.Action.Touch.TAP;
|
||||
target.type = LauncherLogProto.Target.Type.CONTROL;
|
||||
target.controlType = CANCEL_TARGET;
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unexpected action type = " + actionType);
|
||||
}
|
||||
|
||||
switch(viewType) {
|
||||
case RECENTS_QUICK_SCRUB_ONBOARDING_TIP:
|
||||
target.tipType = LauncherLogProto.TipType.QUICK_SCRUB_TEXT;
|
||||
break;
|
||||
case RECENTS_SWIPE_UP_ONBOARDING_TIP:
|
||||
target.tipType = LauncherLogProto.TipType.SWIPE_UP_TEXT;
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unexpected viewType = " + viewType);
|
||||
}
|
||||
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
|
||||
dispatchUserEvent(event, null);
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,12 @@ import android.util.FloatProperty;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.quickstep.LauncherActivityInterface;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
@@ -54,11 +55,12 @@ public class AnimatorControllerWithResistance {
|
||||
*/
|
||||
public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f;
|
||||
|
||||
private enum RecentsParams {
|
||||
private enum RecentsResistanceParams {
|
||||
FROM_APP(0.75f, 0.5f, 1f),
|
||||
FROM_OVERVIEW(1f, 0.75f, 0.5f);
|
||||
|
||||
RecentsParams(float scaleStartResist, float scaleMaxResist, float translationFactor) {
|
||||
RecentsResistanceParams(float scaleStartResist, float scaleMaxResist,
|
||||
float translationFactor) {
|
||||
this.scaleStartResist = scaleStartResist;
|
||||
this.scaleMaxResist = scaleMaxResist;
|
||||
this.translationFactor = translationFactor;
|
||||
@@ -139,9 +141,9 @@ public class AnimatorControllerWithResistance {
|
||||
FloatProperty<SCALE> scaleProperty, TRANSLATION translationTarget,
|
||||
FloatProperty<TRANSLATION> translationProperty) {
|
||||
|
||||
PendingAnimation resistAnim = createRecentsResistanceAnim(null, context,
|
||||
recentsOrientedState, dp, scaleTarget, scaleProperty, translationTarget,
|
||||
translationProperty, RecentsParams.FROM_APP);
|
||||
RecentsParams params = new RecentsParams(context, recentsOrientedState, dp, scaleTarget,
|
||||
scaleProperty, translationTarget, translationProperty);
|
||||
PendingAnimation resistAnim = createRecentsResistanceAnim(params);
|
||||
|
||||
AnimatorPlaybackController resistanceController = resistAnim.createPlaybackController();
|
||||
return new AnimatorControllerWithResistance(normalController, resistanceController);
|
||||
@@ -152,31 +154,30 @@ public class AnimatorControllerWithResistance {
|
||||
* when starting from recents, i.e. {@link #createRecentsResistanceFromOverviewAnim}.
|
||||
*/
|
||||
public static <SCALE, TRANSLATION> PendingAnimation createRecentsResistanceAnim(
|
||||
@Nullable PendingAnimation resistAnim, Context context,
|
||||
RecentsOrientedState recentsOrientedState, DeviceProfile dp, SCALE scaleTarget,
|
||||
FloatProperty<SCALE> scaleProperty, TRANSLATION translationTarget,
|
||||
FloatProperty<TRANSLATION> translationProperty, RecentsParams params) {
|
||||
RecentsParams<SCALE, TRANSLATION> params) {
|
||||
Rect startRect = new Rect();
|
||||
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, startRect,
|
||||
recentsOrientedState.getOrientationHandler());
|
||||
PagedOrientationHandler orientationHandler = params.recentsOrientedState
|
||||
.getOrientationHandler();
|
||||
LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
|
||||
orientationHandler);
|
||||
long distanceToCover = startRect.bottom;
|
||||
boolean isTwoButtonMode = SysUINavigationMode.getMode(context) == TWO_BUTTONS;
|
||||
boolean isTwoButtonMode = SysUINavigationMode.getMode(params.context) == TWO_BUTTONS;
|
||||
if (isTwoButtonMode) {
|
||||
// We can only drag a small distance past overview, not to the top of the screen.
|
||||
distanceToCover = (long)
|
||||
((dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR);
|
||||
}
|
||||
if (resistAnim == null) {
|
||||
resistAnim = new PendingAnimation(distanceToCover * 2);
|
||||
((params.dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR);
|
||||
}
|
||||
PendingAnimation resistAnim = params.resistAnim != null
|
||||
? params.resistAnim
|
||||
: new PendingAnimation(distanceToCover * 2);
|
||||
|
||||
PointF pivot = new PointF();
|
||||
float fullscreenScale = recentsOrientedState.getFullScreenScaleAndPivot(
|
||||
startRect, dp, pivot);
|
||||
float startScale = 1;
|
||||
float prevScaleRate = (fullscreenScale - startScale) / (dp.heightPx - startRect.bottom);
|
||||
float fullscreenScale = params.recentsOrientedState.getFullScreenScaleAndPivot(
|
||||
startRect, params.dp, pivot);
|
||||
float prevScaleRate = (fullscreenScale - params.startScale)
|
||||
/ (params.dp.heightPx - startRect.bottom);
|
||||
// This is what the scale would be at the end of the drag if we didn't apply resistance.
|
||||
float endScale = startScale - prevScaleRate * distanceToCover;
|
||||
float endScale = params.startScale - prevScaleRate * distanceToCover;
|
||||
final TimeInterpolator scaleInterpolator;
|
||||
if (isTwoButtonMode) {
|
||||
// We are bounded by the distance of the drag, so we don't need to apply resistance.
|
||||
@@ -184,9 +185,10 @@ public class AnimatorControllerWithResistance {
|
||||
} else {
|
||||
// Create an interpolator that resists the scale so the scale doesn't get smaller than
|
||||
// RECENTS_SCALE_MAX_RESIST.
|
||||
float startResist = Utilities.getProgress(params.scaleStartResist , startScale,
|
||||
endScale);
|
||||
float maxResist = Utilities.getProgress(params.scaleMaxResist, startScale, endScale);
|
||||
float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist,
|
||||
params.startScale, endScale);
|
||||
float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist,
|
||||
params.startScale, endScale);
|
||||
scaleInterpolator = t -> {
|
||||
if (t < startResist) {
|
||||
return t;
|
||||
@@ -196,20 +198,22 @@ public class AnimatorControllerWithResistance {
|
||||
return startResist + resistProgress * (maxResist - startResist);
|
||||
};
|
||||
}
|
||||
resistAnim.addFloat(scaleTarget, scaleProperty, startScale, endScale,
|
||||
resistAnim.addFloat(params.scaleTarget, params.scaleProperty, params.startScale, endScale,
|
||||
scaleInterpolator);
|
||||
|
||||
if (!isTwoButtonMode) {
|
||||
// Compute where the task view would be based on the end scale, if we didn't translate.
|
||||
RectF endRectF = new RectF(startRect);
|
||||
Matrix temp = new Matrix();
|
||||
temp.setScale(params.scaleMaxResist, params.scaleMaxResist, pivot.x, pivot.y);
|
||||
temp.setScale(params.resistanceParams.scaleMaxResist,
|
||||
params.resistanceParams.scaleMaxResist, pivot.x, pivot.y);
|
||||
temp.mapRect(endRectF);
|
||||
// Translate such that the task view touches the top of the screen when drag does.
|
||||
float endTranslation = endRectF.top * recentsOrientedState.getOrientationHandler()
|
||||
.getSecondaryTranslationDirectionFactor() * params.translationFactor;
|
||||
resistAnim.addFloat(translationTarget, translationProperty, 0, endTranslation,
|
||||
RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
|
||||
float endTranslation = endRectF.top
|
||||
* orientationHandler.getSecondaryTranslationDirectionFactor()
|
||||
* params.resistanceParams.translationFactor;
|
||||
resistAnim.addFloat(params.translationTarget, params.translationProperty,
|
||||
params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
|
||||
}
|
||||
|
||||
return resistAnim;
|
||||
@@ -220,11 +224,66 @@ public class AnimatorControllerWithResistance {
|
||||
* a RecentsView interaction that started from the overview state.
|
||||
*/
|
||||
public static PendingAnimation createRecentsResistanceFromOverviewAnim(
|
||||
BaseDraggingActivity activity, @Nullable PendingAnimation resistanceAnim) {
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
return createRecentsResistanceAnim(resistanceAnim, activity,
|
||||
recentsView.getPagedViewOrientedState(), activity.getDeviceProfile(),
|
||||
recentsView, RECENTS_SCALE_PROPERTY, recentsView, TASK_SECONDARY_TRANSLATION,
|
||||
RecentsParams.FROM_OVERVIEW);
|
||||
Launcher launcher, @Nullable PendingAnimation resistanceAnim) {
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
RecentsParams params = new RecentsParams(launcher, recentsView.getPagedViewOrientedState(),
|
||||
launcher.getDeviceProfile(), recentsView, RECENTS_SCALE_PROPERTY, recentsView,
|
||||
TASK_SECONDARY_TRANSLATION)
|
||||
.setResistAnim(resistanceAnim)
|
||||
.setResistanceParams(RecentsResistanceParams.FROM_OVERVIEW)
|
||||
.setStartScale(recentsView.getScaleX());
|
||||
return createRecentsResistanceAnim(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Params to compute resistance when scaling/translating recents.
|
||||
*/
|
||||
private static class RecentsParams<SCALE, TRANSLATION> {
|
||||
// These are all required and can't have default values, hence are final.
|
||||
public final Context context;
|
||||
public final RecentsOrientedState recentsOrientedState;
|
||||
public final DeviceProfile dp;
|
||||
public final SCALE scaleTarget;
|
||||
public final FloatProperty<SCALE> scaleProperty;
|
||||
public final TRANSLATION translationTarget;
|
||||
public final FloatProperty<TRANSLATION> translationProperty;
|
||||
|
||||
// These are not required, or can have a default value that is generally correct.
|
||||
@Nullable public PendingAnimation resistAnim = null;
|
||||
public RecentsResistanceParams resistanceParams = RecentsResistanceParams.FROM_APP;
|
||||
public float startScale = 1f;
|
||||
public float startTranslation = 0f;
|
||||
|
||||
private RecentsParams(Context context, RecentsOrientedState recentsOrientedState,
|
||||
DeviceProfile dp, SCALE scaleTarget, FloatProperty<SCALE> scaleProperty,
|
||||
TRANSLATION translationTarget, FloatProperty<TRANSLATION> translationProperty) {
|
||||
this.context = context;
|
||||
this.recentsOrientedState = recentsOrientedState;
|
||||
this.dp = dp;
|
||||
this.scaleTarget = scaleTarget;
|
||||
this.scaleProperty = scaleProperty;
|
||||
this.translationTarget = translationTarget;
|
||||
this.translationProperty = translationProperty;
|
||||
}
|
||||
|
||||
private RecentsParams setResistAnim(PendingAnimation resistAnim) {
|
||||
this.resistAnim = resistAnim;
|
||||
return this;
|
||||
}
|
||||
|
||||
private RecentsParams setResistanceParams(RecentsResistanceParams resistanceParams) {
|
||||
this.resistanceParams = resistanceParams;
|
||||
return this;
|
||||
}
|
||||
|
||||
private RecentsParams setStartScale(float startScale) {
|
||||
this.startScale = startScale;
|
||||
return this;
|
||||
}
|
||||
|
||||
private RecentsParams setStartTranslation(float startTranslation) {
|
||||
this.startTranslation = startTranslation;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
|
||||
@@ -106,10 +107,16 @@ public class OverviewToHomeAnim {
|
||||
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
|
||||
? PLAY_ATOMIC_OVERVIEW_PEEK
|
||||
: ANIM_ALL_COMPONENTS;
|
||||
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, DEACCEL);
|
||||
boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
|
||||
.isLayoutNaturalToLauncher();
|
||||
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
|
||||
? DEACCEL : FINAL_FRAME);
|
||||
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
|
||||
config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
|
||||
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, INSTANT);
|
||||
if (!isLayoutNaturalToLauncher) {
|
||||
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL);
|
||||
}
|
||||
AnimatorSet stateAnim = stateManager.createAtomicAnimation(
|
||||
startState, NORMAL, config);
|
||||
stateAnim.addListener(new AnimationSuccessListener() {
|
||||
|
||||
@@ -26,13 +26,13 @@ import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.android.launcher3.BaseQuickstepLauncher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
|
||||
import com.android.launcher3.statemanager.StateManager;
|
||||
import com.android.launcher3.statemanager.StateManager.StateListener;
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher;
|
||||
import com.android.launcher3.util.OnboardingPrefs;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.views.AllAppsEduView;
|
||||
@@ -40,9 +40,9 @@ import com.android.quickstep.views.AllAppsEduView;
|
||||
/**
|
||||
* Extends {@link OnboardingPrefs} for quickstep-specific onboarding data.
|
||||
*/
|
||||
public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
|
||||
public class QuickstepOnboardingPrefs extends OnboardingPrefs<QuickstepLauncher> {
|
||||
|
||||
public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs) {
|
||||
public QuickstepOnboardingPrefs(QuickstepLauncher launcher, SharedPreferences sharedPrefs) {
|
||||
super(launcher, sharedPrefs);
|
||||
|
||||
StateManager<LauncherState> stateManager = launcher.getStateManager();
|
||||
|
||||
@@ -104,11 +104,6 @@ public class AllAppsEduView extends AbstractFloatingView {
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_ALL_APPS_EDU) != 0;
|
||||
|
||||
@@ -110,8 +110,6 @@ import com.android.launcher3.statehandlers.DepthController;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.DynamicResource;
|
||||
@@ -142,7 +140,6 @@ import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.LauncherEventUtil;
|
||||
import com.android.systemui.shared.system.PackageManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
|
||||
@@ -673,9 +670,6 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
public void onDigitalWellbeingToastShown() {
|
||||
if (!mDwbToastShown) {
|
||||
mDwbToastShown = true;
|
||||
mActivity.getUserEventDispatcher().logActionTip(
|
||||
LauncherEventUtil.VISIBLE,
|
||||
LauncherLogProto.TipType.DWB_TOAST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1461,8 +1455,6 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
if (taskView.getTask() != null) {
|
||||
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
|
||||
ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
|
||||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||
endState.logAction, Direction.UP, index, compKey);
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
|
||||
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
|
||||
}
|
||||
@@ -2174,9 +2166,6 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||
tv.launchTask(false, onLaunchResult, getHandler());
|
||||
Task task = tv.getTask();
|
||||
if (task != null) {
|
||||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||
endState.logAction, Direction.DOWN, indexOfChild(tv),
|
||||
TaskUtils.getLaunchComponentKeyForTask(task.key));
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
|
||||
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
|
||||
}
|
||||
|
||||
@@ -132,11 +132,6 @@ public class TaskMenuView extends AbstractFloatingView {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
@@ -225,10 +225,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
} else {
|
||||
launchTask(true /* animate */);
|
||||
}
|
||||
|
||||
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
|
||||
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
|
||||
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
|
||||
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
|
||||
.log(LAUNCHER_TASK_LAUNCH_TAP);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.views.SearchSettingsRowView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/TextHeadline"
|
||||
android:id="@+id/section_title"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
style="@style/TextTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
style="@style/TextTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/breadcrumbs"
|
||||
style="@style/TextTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:alpha=".7"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
|
||||
</com.android.launcher3.views.SearchSettingsRowView>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<androidx.slice.widget.SliceView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="4dp" />
|
||||
@@ -35,9 +35,6 @@
|
||||
|
||||
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
|
||||
|
||||
<color name="all_apps_section_fill">#32c0c0c0</color>
|
||||
<color name="all_apps_section_focused_item">#40c0c0c0</color>
|
||||
|
||||
<color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
|
||||
<color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
|
||||
<color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
|
||||
|
||||
@@ -69,7 +69,6 @@
|
||||
<string name="app_launch_tracker_class" translatable="false"></string>
|
||||
<string name="test_information_handler_class" translatable="false"></string>
|
||||
<string name="launcher_activity_logic_class" translatable="false"></string>
|
||||
<string name="prediction_model_class" translatable="false"></string>
|
||||
<string name="model_delegate_class" translatable="false"></string>
|
||||
|
||||
<!-- View ID to use for QSB widget -->
|
||||
|
||||
@@ -44,12 +44,6 @@
|
||||
android:defaultValue="@bool/allow_rotation"
|
||||
android:persistent="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="pref_grid_options"
|
||||
android:title="Enable grid options"
|
||||
android:defaultValue="false"
|
||||
android:persistent="true" />
|
||||
|
||||
<androidx.preference.PreferenceScreen
|
||||
android:key="pref_developer_options"
|
||||
android:persistent="false"
|
||||
|
||||
@@ -36,8 +36,6 @@ import android.widget.LinearLayout;
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
@@ -129,8 +127,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
public final void close(boolean animate) {
|
||||
animate &= areAnimatorsEnabled();
|
||||
if (mIsOpen) {
|
||||
BaseActivity.fromContext(getContext()).getUserEventDispatcher()
|
||||
.resetElapsedContainerMillis("container closed");
|
||||
// Add to WW logging
|
||||
}
|
||||
handleClose(animate);
|
||||
mIsOpen = false;
|
||||
@@ -145,12 +142,6 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
public void addHintCloseAnim(
|
||||
float distanceToMove, Interpolator interpolator, PendingAnimation target) { }
|
||||
|
||||
public abstract void logActionCommand(int command);
|
||||
|
||||
public int getLogContainerType() {
|
||||
return ContainerType.DEFAULT_CONTAINERTYPE;
|
||||
}
|
||||
|
||||
public final boolean isOpen() {
|
||||
return mIsOpen;
|
||||
}
|
||||
@@ -159,7 +150,6 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
|
||||
/** @return Whether the back is consumed. If false, Launcher will handle the back as well. */
|
||||
public boolean onBackPressed() {
|
||||
logActionCommand(Action.Command.BACK);
|
||||
close(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -536,11 +536,6 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
|
||||
mDragLayer.removeView(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
// TODO: Log this case.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
|
||||
|
||||
@@ -193,7 +193,6 @@ public abstract class BaseDraggingActivity extends BaseActivity
|
||||
getSystemService(LauncherApps.class).startMainActivity(
|
||||
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
|
||||
}
|
||||
getUserEventDispatcher().logAppLaunch(v, intent, user);
|
||||
if (item != null) {
|
||||
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
|
||||
logAppLaunch(item, instanceId);
|
||||
|
||||
@@ -79,7 +79,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
private static final int DISPLAY_FOLDER = 2;
|
||||
private static final int DISPLAY_HERO_APP = 5;
|
||||
|
||||
private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
|
||||
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
|
||||
|
||||
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
|
||||
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
|
||||
@@ -280,7 +280,10 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
applyDotState(info, false /* animate */);
|
||||
}
|
||||
|
||||
public void applyFromPackageItemInfo(PackageItemInfo info) {
|
||||
/**
|
||||
* Apply label and tag using a generic {@link ItemInfoWithIcon}
|
||||
*/
|
||||
public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
|
||||
applyIconAndLabel(info);
|
||||
// We don't need to check the info since it's not a WorkspaceItemInfo
|
||||
super.setTag(info);
|
||||
@@ -407,12 +410,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
/**
|
||||
* Draws the notification dot in the top right corner of the icon bounds.
|
||||
*
|
||||
* @param canvas The canvas to draw to.
|
||||
*/
|
||||
protected void drawDotIfNecessary(Canvas canvas) {
|
||||
if (!mForceHideDot && (hasDot() || mDotParams.scale > 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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<LauncherState> 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<LauncherState> 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<LauncherState> 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<LauncherState> implements Launche
|
||||
workspace.requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
|
||||
|
||||
/**
|
||||
* Add the views for a widget to the workspace.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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<Target> 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.
|
||||
|
||||
@@ -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<WorkspacePageIndicator>
|
||||
.showForIcon((BubbleTextView) child);
|
||||
if (popupContainer != null) {
|
||||
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
|
||||
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1706,7 +1703,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
fi.addItem(destInfo);
|
||||
fi.addItem(sourceInfo);
|
||||
}
|
||||
mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1723,7 +1719,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
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<WorkspacePageIndicator>
|
||||
return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillInLogContainerData(ItemInfo childInfo, Target child,
|
||||
ArrayList<Target> 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.
|
||||
|
||||
@@ -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<Target> parents) {
|
||||
parents.add(newContainerTarget(
|
||||
getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
mInsets.set(insets);
|
||||
|
||||
@@ -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<AllAppsGridAdapter.ViewHolder> {
|
||||
public class AllAppsGridAdapter extends
|
||||
RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> implements
|
||||
LifecycleOwner {
|
||||
|
||||
public static final String TAG = "AppsGridAdapter";
|
||||
|
||||
@@ -71,12 +83,18 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
|
||||
public static final int VIEW_TYPE_SEARCH_HERO_APP = 1 << 6;
|
||||
|
||||
public static final int DETAIL_ROW_WITH_BUTTON = 1 << 7;
|
||||
public static final int VIEW_TYPE_SEARCH_ROW_WITH_BUTTON = 1 << 7;
|
||||
|
||||
public static final int VIEW_TYPE_SEARCH_ROW = 1 << 8;
|
||||
|
||||
public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 9;
|
||||
|
||||
// Common view type masks
|
||||
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
|
||||
|
||||
private final LifecycleRegistry mLifecycleRegistry;
|
||||
|
||||
/**
|
||||
* ViewHolder for each icon.
|
||||
*/
|
||||
@@ -159,12 +177,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
boolean isCountedForAccessibility() {
|
||||
return viewType == VIEW_TYPE_ICON
|
||||
|| viewType == VIEW_TYPE_SEARCH_HERO_APP
|
||||
|| viewType == DETAIL_ROW_WITH_BUTTON;
|
||||
|| viewType == VIEW_TYPE_SEARCH_ROW_WITH_BUTTON
|
||||
|| viewType == VIEW_TYPE_SEARCH_SLICE
|
||||
|| viewType == VIEW_TYPE_SEARCH_ROW;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension of AdapterItem that contains an extra payload specific to item
|
||||
*
|
||||
* @param <T> Play load Type
|
||||
*/
|
||||
public static class AdapterItemWithPayload<T> extends AdapterItem {
|
||||
@@ -310,6 +331,12 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
mOnIconClickListener = launcher.getItemOnClickListener();
|
||||
|
||||
setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
|
||||
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
mLifecycleRegistry = new LifecycleRegistry(this);
|
||||
mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
|
||||
} else {
|
||||
mLifecycleRegistry = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAppsPerRow(int appsPerRow) {
|
||||
@@ -390,9 +417,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
case VIEW_TYPE_SEARCH_HERO_APP:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_hero_app, parent, false));
|
||||
case DETAIL_ROW_WITH_BUTTON:
|
||||
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_play_item, parent, false));
|
||||
case VIEW_TYPE_SEARCH_ROW:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_settings_row, parent, false));
|
||||
case VIEW_TYPE_SEARCH_SLICE:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_slice, parent, false));
|
||||
default:
|
||||
throw new RuntimeException("Unexpected view type");
|
||||
}
|
||||
@@ -421,9 +454,20 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
|
||||
searchView.setVisibility(View.GONE);
|
||||
}
|
||||
break;
|
||||
case VIEW_TYPE_SEARCH_SLICE:
|
||||
SliceView sliceView = (SliceView) holder.itemView;
|
||||
Uri uri = ((AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position))
|
||||
.getPayload();
|
||||
try {
|
||||
LiveData<Slice> 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<AllAppsGridAdapter.
|
||||
return item.viewType;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
return mLifecycleRegistry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.graphics.RectF;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
@@ -100,19 +101,23 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||
* Handles grouping and drawing of items in the same all apps sections.
|
||||
*/
|
||||
public static class SectionDecorationHandler {
|
||||
private static final int FILL_ALPHA = (int) (.3f * 255);
|
||||
private static final int FOCUS_ALPHA = (int) (.8f * 255);
|
||||
|
||||
protected RectF mBounds = new RectF();
|
||||
private final boolean mIsFullWidth;
|
||||
private final float mRadius;
|
||||
|
||||
private final int mFocusColor;
|
||||
private final int mFillcolor;
|
||||
protected int mFocusColor;
|
||||
protected int mFillcolor;
|
||||
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
|
||||
|
||||
public SectionDecorationHandler(Context context, boolean isFullWidth) {
|
||||
mIsFullWidth = isFullWidth;
|
||||
mFillcolor = context.getColor(R.color.all_apps_section_fill);
|
||||
mFocusColor = context.getColor(R.color.all_apps_section_focused_item);
|
||||
int endScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
|
||||
mFillcolor = ColorUtils.setAlphaComponent(endScrim, FILL_ALPHA);
|
||||
mFocusColor = ColorUtils.setAlphaComponent(endScrim, FOCUS_ALPHA);
|
||||
mRadius = Themes.getDialogCornerRadius(context);
|
||||
}
|
||||
|
||||
|
||||
@@ -115,11 +115,6 @@ public class DiscoveryBounce extends AbstractFloatingView {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) {
|
||||
// Since this is on-boarding popup, it is not a user controlled action.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_DISCOVERY_BOUNCE) != 0;
|
||||
|
||||
@@ -139,8 +139,8 @@ public class AppsSearchContainerLayout extends ExtendedEditText
|
||||
mApps = appsView.getApps();
|
||||
mAppsView = appsView;
|
||||
mSearchBarController.initialize(
|
||||
new DefaultAppSearchAlgorithm(LauncherAppState.getInstance(mLauncher)), this,
|
||||
mLauncher, this, this);
|
||||
new DefaultAppSearchAlgorithm(mLauncher, LauncherAppState.getInstance(mLauncher)),
|
||||
this, mLauncher, this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,26 +15,15 @@
|
||||
*/
|
||||
package com.android.launcher3.allapps.search;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
|
||||
import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_SEARCH_HERO_APP;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.model.AllAppsList;
|
||||
import com.android.launcher3.model.BaseModelUpdateTask;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.PopupPopulator;
|
||||
import com.android.launcher3.shortcuts.ShortcutRequest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -46,63 +35,28 @@ import java.util.function.Consumer;
|
||||
public class AppsSearchPipeline implements SearchPipeline {
|
||||
|
||||
private static final int MAX_RESULTS_COUNT = 5;
|
||||
private static final int MAX_HERO_SECTION_COUNT = 2;
|
||||
private static final int MAX_SHORTCUTS_COUNT = 2;
|
||||
|
||||
private final SearchSectionInfo mSearchSectionInfo;
|
||||
private final LauncherAppState mLauncherAppState;
|
||||
private final boolean mHeroSectionSupported;
|
||||
|
||||
public AppsSearchPipeline(LauncherAppState launcherAppState) {
|
||||
this(launcherAppState, true);
|
||||
}
|
||||
|
||||
public AppsSearchPipeline(LauncherAppState launcherAppState, boolean supportsHeroView) {
|
||||
public AppsSearchPipeline(Context context, LauncherAppState launcherAppState) {
|
||||
mLauncherAppState = launcherAppState;
|
||||
mSearchSectionInfo = new SearchSectionInfo();
|
||||
mSearchSectionInfo.setDecorationHandler(
|
||||
new SectionDecorationHandler(launcherAppState.getContext(), true));
|
||||
mHeroSectionSupported = supportsHeroView;
|
||||
new SectionDecorationHandler(context, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
@WorkerThread
|
||||
public void performSearch(String query, Consumer<ArrayList<AdapterItem>> callback) {
|
||||
mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
List<AppInfo> 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<WorkspaceItemInfo> getShortcutInfos(Context context, AppInfo appInfo) {
|
||||
List<ShortcutInfo> shortcuts = new ShortcutRequest(context, appInfo.user)
|
||||
.withContainer(appInfo.getTargetComponent())
|
||||
.query(ShortcutRequest.PUBLISHED);
|
||||
shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, null);
|
||||
IconCache cache = LauncherAppState.getInstance(context).getIconCache();
|
||||
ArrayList<WorkspaceItemInfo> 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<AdapterItem> getHeroAdapterItems(Context context, List<AppInfo> apps) {
|
||||
ArrayList<AdapterItem> adapterItems = new ArrayList<>();
|
||||
for (int i = 0; i < apps.size(); i++) {
|
||||
//hero app
|
||||
AppInfo appInfo = apps.get(i);
|
||||
ArrayList<WorkspaceItemInfo> 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<AdapterItem> getAdapterItems(List<AppInfo> matchingApps) {
|
||||
ArrayList<AdapterItem> items = new ArrayList<>();
|
||||
for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<LauncherLogProto.Target> parents) {
|
||||
parents.add(newContainerTarget(LauncherLogProto.ContainerType.PINITEM));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postCleanup() {
|
||||
super.postCleanup();
|
||||
|
||||
@@ -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<LauncherLogProto.Target> 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.
|
||||
*/
|
||||
|
||||
@@ -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<ItemInfo> 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<ShortcutInfo> 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<ItemInfo> mWorkspaceItems;
|
||||
private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
|
||||
private final ArrayList<AppInfo> mCachedPredictedItems;
|
||||
private final FixedContainerItems mHotseatPredictions;
|
||||
private final WidgetsModel mWidgetsModel;
|
||||
private final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
|
||||
|
||||
private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
|
||||
ArrayList<LauncherAppWidgetInfo> appWidgets,
|
||||
ArrayList<AppInfo> cachedPredictedItems, WidgetsModel widgetsModel,
|
||||
private WorkspaceResult(BgDataModel dataModel,
|
||||
WidgetsModel widgetsModel,
|
||||
Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
|
||||
mWorkspaceItems = workspaceItems;
|
||||
mAppWidgets = appWidgets;
|
||||
mCachedPredictedItems = cachedPredictedItems;
|
||||
mWorkspaceItems = dataModel.workspaceItems;
|
||||
mAppWidgets = dataModel.appWidgets;
|
||||
mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
|
||||
mWidgetsModel = widgetsModel;
|
||||
mWidgetProvidersMap = widgetProviderInfoMap;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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<Target> 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<Target> 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<Target> 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<Target> 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<Target> 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<Target> 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();
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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<AppInfo> 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) {
|
||||
|
||||
@@ -102,11 +102,6 @@ public class BgDataModel {
|
||||
*/
|
||||
public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
|
||||
|
||||
/**
|
||||
* List of all cached predicted items visible on home screen
|
||||
*/
|
||||
public final ArrayList<AppInfo> 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<AppInfo> appInfos, IntArray ranks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, String> mPackagesForInstaller;
|
||||
private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage;
|
||||
|
||||
public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> 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<String, String> getPackagesForInstaller(
|
||||
HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
|
||||
MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
|
||||
for (Map.Entry<PackageUserKey, SessionInfo> 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<ItemInfo> firstScreenItems) {
|
||||
for (Map.Entry<String, ArrayList<String>> 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<String> packages, List<ItemInfo> firstScreenItems) {
|
||||
Set<String> packages, List<ItemInfo> firstScreenItems) {
|
||||
Set<String> folderItems = new HashSet<>();
|
||||
Set<String> workspaceItems = new HashSet<>();
|
||||
Set<String> hotseatItems = new HashSet<>();
|
||||
|
||||
@@ -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<ShortcutInfo> 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<UserHandle, String> pendingPackages = new MultiHashMap<>();
|
||||
final Set<PackageUserKey> 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<ComponentKey> componentKeys =
|
||||
mApp.getPredictionModel().getPredictionComponentKeys();
|
||||
List<LauncherActivityInfo> 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<LauncherActivityInfo> 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());
|
||||
|
||||
@@ -71,6 +71,12 @@ public class ModelDelegate implements ResourceBasedOverride {
|
||||
@WorkerThread
|
||||
public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { }
|
||||
|
||||
/**
|
||||
* Called during loader after workspace loading is complete
|
||||
*/
|
||||
@WorkerThread
|
||||
public void workspaceLoadComplete() { }
|
||||
|
||||
/**
|
||||
* Called when the delegate is no loner needed
|
||||
*/
|
||||
|
||||
@@ -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<T> currentScreenItems,
|
||||
ArrayList<T> otherScreenItems) {
|
||||
// Purge any null ItemInfos
|
||||
Iterator<T> 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.
|
||||
|
||||
@@ -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<ComponentKey> 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<ComponentKey> getPredictionComponentKeys() {
|
||||
Preconditions.assertWorkerThread();
|
||||
ArrayList<ComponentKey> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<UserHandle, String> mPackages;
|
||||
private final Set<PackageUserKey> mPackages;
|
||||
|
||||
public SdCardAvailableReceiver(LauncherAppState app,
|
||||
MultiHashMap<UserHandle, String> packages) {
|
||||
public SdCardAvailableReceiver(LauncherAppState app, Set<PackageUserKey> 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<UserHandle, ArrayList<String>> entry : mPackages.entrySet()) {
|
||||
UserHandle user = entry.getKey();
|
||||
for (PackageUserKey puk : mPackages) {
|
||||
UserHandle user = puk.mUser;
|
||||
|
||||
final ArrayList<String> packagesRemoved = new ArrayList<>();
|
||||
final ArrayList<String> 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()) {
|
||||
|
||||
@@ -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<ShortcutKey> removedKeys = new HashSet<>();
|
||||
MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
|
||||
HashSet<String> allIds = new HashSet<>();
|
||||
ArrayList<WorkspaceItemInfo> 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<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
|
||||
if (!keyToShortcutInfo.isEmpty()) {
|
||||
if (!matchingWorkspaceItems.isEmpty()) {
|
||||
// Update the workspace to reflect the changes to updated shortcuts residing on it.
|
||||
List<String> allLauncherKnownIds = matchingWorkspaceItems.stream()
|
||||
.map(WorkspaceItemInfo::getDeepShortcutId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
List<ShortcutInfo> shortcuts = new ShortcutRequest(context, mUser)
|
||||
.forPackage(mPackageName, new ArrayList<>(allIds))
|
||||
.forPackage(mPackageName, allLauncherKnownIds)
|
||||
.query(ShortcutRequest.ALL);
|
||||
|
||||
Set<String> nonPinnedIds = new HashSet<>(allLauncherKnownIds);
|
||||
ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
|
||||
for (ShortcutInfo fullDetails : shortcuts) {
|
||||
ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
|
||||
List<WorkspaceItemInfo> 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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<T extends BaseDraggingActivity> 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<T extends BaseDraggingActivity> extends Arr
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fillInLogContainerData(ItemInfo childInfo, Target child,
|
||||
ArrayList<Target> 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.
|
||||
|
||||
@@ -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<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
|
||||
for (WidgetListRowEntry entry : mAllWidgets) {
|
||||
if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
|
||||
ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
|
||||
// Remove widgets not associated with the correct user.
|
||||
Iterator<WidgetItem> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -99,7 +99,7 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
|
||||
final List<WidgetItem> widgets =
|
||||
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
|
||||
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
|
||||
if (widgets == null) {
|
||||
if (widgets.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new Widgets(launcher, itemInfo);
|
||||
|
||||
@@ -196,9 +196,6 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
|
||||
@Override
|
||||
public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
|
||||
|
||||
@Override
|
||||
public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
|
||||
|
||||
@Override
|
||||
public void bindScreens(IntArray orderedScreenIds) { }
|
||||
|
||||
|
||||
@@ -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<ShortcutKey> keys) {
|
||||
static ItemInfoMatcher ofShortcutKeys(Set<ShortcutKey> keys) {
|
||||
return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
|
||||
keys.contains(ShortcutKey.fromItemInfo(info));
|
||||
}
|
||||
|
||||
@@ -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<K, V> extends HashMap<K, ArrayList<V>> {
|
||||
|
||||
public MultiHashMap() { }
|
||||
|
||||
public MultiHashMap(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
public void addToList(K key, V value) {
|
||||
ArrayList<V> list = get(key);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
list.add(value);
|
||||
put(key, list);
|
||||
} else {
|
||||
list.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultiHashMap<K, V> clone() {
|
||||
MultiHashMap<K, V> map = new MultiHashMap<>(size());
|
||||
for (Entry<K, ArrayList<V>> entry : entrySet()) {
|
||||
map.put(entry.getKey(), new ArrayList<V>(entry.getValue()));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,8 @@ public class OnboardingPrefs<T extends Launcher> {
|
||||
*/
|
||||
@StringDef(value = {
|
||||
HOME_BOUNCE_SEEN,
|
||||
SHELF_BOUNCE_SEEN
|
||||
SHELF_BOUNCE_SEEN,
|
||||
HOTSEAT_LONGPRESS_TIP_SEEN
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface EventBoolKey {}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<List<WorkspaceItemInfo>> {
|
||||
AllAppsSearchBarController.PayloadResultHandler<List<ItemInfoWithIcon>> {
|
||||
|
||||
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<List<WorkspaceItemInfo>> adapterItem) {
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<List<ItemInfoWithIcon>> adapterItem) {
|
||||
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
|
||||
mIconView.setBackground(mBubbleTextView.getIcon());
|
||||
mIconView.setTag(adapterItem.appInfo);
|
||||
List<WorkspaceItemInfo> shorcutInfos = adapterItem.getPayload();
|
||||
List<ItemInfoWithIcon> 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<LauncherLogProto.Target> parents) {
|
||||
|
||||
}
|
||||
|
||||
private void setWillDrawIcon(boolean willDraw) {
|
||||
mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<T extends Launcher> 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<T extends Launcher> 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;
|
||||
|
||||
@@ -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<Bundle> {
|
||||
|
||||
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<Bundle> adapterItemWithPayload) {
|
||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
||||
mIntent = bundle.getParcelable("intent");
|
||||
showIfAvailable(mTitleView, bundle.getString("title"));
|
||||
showIfAvailable(mDescriptionView, bundle.getString("description"));
|
||||
ArrayList<String> 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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Target> 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();
|
||||
}
|
||||
|
||||
@@ -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<View, String> getAccessibilityTarget() {
|
||||
return Pair.create(findViewById(R.id.title), getContext().getString(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<WidgetsRowViewHolder> {
|
||||
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<WidgetsRowViewHolder> {
|
||||
}
|
||||
}
|
||||
} 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<WidgetsRowViewHolder> {
|
||||
@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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
|
||||
|
||||
private AppFilter mAppFilter;
|
||||
private final Map<PackageItemInfo, List<WidgetItem>> 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<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
|
||||
WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
|
||||
for (Map.Entry<PackageItemInfo, List<WidgetItem>> 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<WidgetItem> 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<WidgetItem> 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<String> packageNames, UserHandle user,
|
||||
LauncherAppState app) {
|
||||
for (Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
|
||||
for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
|
||||
if (packageNames.contains(entry.getKey().packageName)) {
|
||||
ArrayList<WidgetItem> items = entry.getValue();
|
||||
List<WidgetItem> 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<WidgetItem> widgetsList = mWidgetsList.get(
|
||||
List<WidgetItem> 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<WidgetItem> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user