Merge "Send widget added and removed events to AiAi." into sc-v2-dev

This commit is contained in:
Alina Zaidi
2021-10-28 16:45:22 +00:00
committed by Android (Google) Code Review
4 changed files with 184 additions and 84 deletions
@@ -16,33 +16,25 @@
package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
import com.android.launcher3.model.BgDataModel;
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;
import com.android.launcher3.shortcuts.ShortcutKey;
import java.util.ArrayList;
import java.util.Locale;
/**
* Model helper for app predictions in workspace
*/
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";
@@ -54,15 +46,15 @@ public class HotseatPredictionModel {
ArrayList<AppTargetEvent> events = new ArrayList<>();
ArrayList<ItemInfo> workspaceItems = dataModel.getAllWorkspaceItems();
for (ItemInfo item : workspaceItems) {
AppTarget target = getAppTargetFromInfo(context, item);
if (target != null && !isTrackedForPrediction(item)) continue;
events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
AppTarget target = getAppTargetFromItemInfo(context, item);
if (target != null && !isTrackedForHotseatPrediction(item)) continue;
events.add(wrapAppTargetWithItemLocation(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);
AppTarget target = getAppTargetFromItemInfo(context, itemInfo);
if (target != null) currentTargets.add(target);
}
}
@@ -70,56 +62,4 @@ public class HotseatPredictionModel {
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 static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) {
if (info == null) return null;
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
&& info instanceof LauncherAppWidgetInfo
&& ((LauncherAppWidgetInfo) info).providerName != null) {
ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& info.getTargetComponent() != null) {
ComponentName cn = info.getTargetComponent();
return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& info instanceof WorkspaceItemInfo) {
ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
//TODO: switch to using full shortcut info
return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
context.getPackageName(), info.user).build();
}
return null;
}
/**
* Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
* location using {@link ItemInfo}
*/
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,
info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
}
/**
* Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
*/
public static boolean isTrackedForPrediction(ItemInfo info) {
return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
&& info.screenId == Workspace.FIRST_SCREEN_ID);
}
}
@@ -22,6 +22,7 @@ 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.LauncherSettings.Favorites.CONTAINER_WIDGETS_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;
@@ -35,6 +36,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.annotation.TargetApi;
@@ -62,7 +65,6 @@ 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.launcher3.shortcuts.ShortcutRequest;
import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
@@ -141,6 +143,9 @@ public class AppEventProducer implements StatsLogConsumer {
if (isTrackedForHotseatPrediction(mLastDragItem)) {
sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
}
if (isTrackedForWidgetPrediction(atomInfo)) {
sendEvent(atomInfo, ACTION_PIN, CONTAINER_WIDGETS_PREDICTION);
}
mLastDragItem = null;
} else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) {
if (isTrackedForHotseatPrediction(atomInfo)) {
@@ -158,6 +163,9 @@ public class AppEventProducer implements StatsLogConsumer {
if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) {
sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
}
if (mLastDragItem != null && isTrackedForWidgetPrediction(mLastDragItem)) {
sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_WIDGETS_PREDICTION);
}
} else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) {
if (isTrackedForHotseatPrediction(atomInfo)) {
sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
@@ -302,19 +310,4 @@ public class AppEventProducer implements StatsLogConsumer {
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;
}
}
}
@@ -0,0 +1,130 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.model;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.WORKSPACE;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.content.Context;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import java.util.Locale;
/** Helper class with methods for converting launcher items to form usable by predictors */
public final class PredictionHelper {
private static final String APP_LOCATION_HOTSEAT = "hotseat";
private static final String APP_LOCATION_WORKSPACE = "workspace";
/**
* Creates and returns an {@link AppTarget} object for an {@link ItemInfo}. Returns null
* if item type is not supported in predictions
*/
@Nullable
public static AppTarget getAppTargetFromItemInfo(Context context, ItemInfo info) {
if (info == null) return null;
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
&& info instanceof LauncherAppWidgetInfo
&& ((LauncherAppWidgetInfo) info).providerName != null) {
ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& info.getTargetComponent() != null) {
ComponentName cn = info.getTargetComponent();
return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& info instanceof WorkspaceItemInfo) {
ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
//TODO: switch to using full shortcut info
return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
context.getPackageName(), info.user).build();
}
return null;
}
/**
* Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
* location using {@link ItemInfo}
*/
public static AppTargetEvent wrapAppTargetWithItemLocation(
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,
info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
}
/**
* Helper method to determine if {@link ItemInfo} should be tracked and reported to hotseat
* predictors
*/
public static boolean isTrackedForHotseatPrediction(ItemInfo info) {
return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
&& info.screenId == Workspace.FIRST_SCREEN_ID);
}
/**
* Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported to
* hotseat predictors
*/
public static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
LauncherAtom.ContainerInfo ci = info.getContainerInfo();
switch (ci.getContainerCase()) {
case HOTSEAT:
return true;
case WORKSPACE:
return ci.getWorkspace().getPageIndex() == 0;
default:
return false;
}
}
/**
* Helper method to determine if {@link ItemInfo} should be tracked and reported to widget
* predictors
*/
public static boolean isTrackedForWidgetPrediction(ItemInfo info) {
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
&& info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP;
}
/**
* Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported
* to widget predictors
*/
public static boolean isTrackedForWidgetPrediction(LauncherAtom.ItemInfo info) {
return info.getItemCase() == LauncherAtom.ItemInfo.ItemCase.WIDGET
&& info.getContainerInfo().getContainerCase() == WORKSPACE;
}
}
@@ -25,8 +25,12 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICA
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle;
import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static java.util.stream.Collectors.toCollection;
import android.app.StatsManager;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
@@ -39,6 +43,7 @@ import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import android.util.StatsEvent;
@@ -62,6 +67,7 @@ import com.android.launcher3.util.PersistedItemArray;
import com.android.quickstep.logging.StatsLogCompatManager;
import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -75,6 +81,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
private static final boolean IS_DEBUG = false;
@@ -272,6 +279,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
registerWidgetsPredictor(apm.createAppPredictionSession(
new AppPredictionContext.Builder(context)
.setUiSurface("widgets")
.setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel))
.setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
.build()));
}
@@ -306,12 +314,41 @@ public class QuickstepModelDelegate extends ModelDelegate {
}
private void onAppTargetEvent(AppTargetEvent event, int client) {
PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState;
PredictorState state;
switch(client) {
case CONTAINER_PREDICTION:
state = mAllAppsState;
break;
case CONTAINER_WIDGETS_PREDICTION:
state = mWidgetsRecommendationState;
break;
case CONTAINER_HOTSEAT_PREDICTION:
default:
state = mHotseatState;
break;
}
if (state.predictor != null) {
state.predictor.notifyAppTargetEvent(event);
}
}
private Bundle getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel) {
Bundle bundle = new Bundle();
ArrayList<AppTargetEvent> widgetEvents =
dataModel.getAllWorkspaceItems().stream()
.filter(PredictionHelper::isTrackedForWidgetPrediction)
.map(item -> {
AppTarget target = getAppTargetFromItemInfo(context, item);
if (target == null) return null;
return wrapAppTargetWithItemLocation(
target, AppTargetEvent.ACTION_PIN, item);
})
.filter(Objects::nonNull)
.collect(toCollection(ArrayList::new));
bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, widgetEvents);
return bundle;
}
static class PredictorState {
public final FixedContainerItems items;