1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.hybridhotseat; 17 18 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 19 20 import android.app.prediction.AppTarget; 21 import android.app.prediction.AppTargetEvent; 22 import android.app.prediction.AppTargetId; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.os.Bundle; 26 27 import com.android.launcher3.LauncherAppState; 28 import com.android.launcher3.LauncherSettings; 29 import com.android.launcher3.Workspace; 30 import com.android.launcher3.model.AllAppsList; 31 import com.android.launcher3.model.BaseModelUpdateTask; 32 import com.android.launcher3.model.BgDataModel; 33 import com.android.launcher3.model.PredictionModel; 34 import com.android.launcher3.model.data.ItemInfo; 35 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 36 import com.android.launcher3.model.data.WorkspaceItemInfo; 37 import com.android.launcher3.shortcuts.ShortcutKey; 38 39 import java.util.ArrayList; 40 import java.util.Locale; 41 import java.util.function.Consumer; 42 43 /** 44 * Model helper for app predictions in workspace 45 */ 46 public class HotseatPredictionModel extends PredictionModel { 47 private static final String APP_LOCATION_HOTSEAT = "hotseat"; 48 private static final String APP_LOCATION_WORKSPACE = "workspace"; 49 50 private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events"; 51 private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items"; 52 53 HotseatPredictionModel(Context context)54 public HotseatPredictionModel(Context context) { } 55 56 /** 57 * Creates and returns bundle using workspace items and cached items 58 */ createBundle(Consumer<Bundle> cb)59 public void createBundle(Consumer<Bundle> cb) { 60 LauncherAppState appState = LauncherAppState.getInstance(mContext); 61 appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { 62 @Override 63 public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { 64 Bundle bundle = new Bundle(); 65 ArrayList<AppTargetEvent> events = new ArrayList<>(); 66 ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems); 67 workspaceItems.addAll(dataModel.appWidgets); 68 for (ItemInfo item : workspaceItems) { 69 AppTarget target = getAppTargetFromInfo(item); 70 if (target != null && !isTrackedForPrediction(item)) continue; 71 events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item)); 72 } 73 ArrayList<AppTarget> currentTargets = new ArrayList<>(); 74 for (ItemInfo itemInfo : dataModel.cachedPredictedItems) { 75 AppTarget target = getAppTargetFromInfo(itemInfo); 76 if (target != null) currentTargets.add(target); 77 } 78 bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events); 79 bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets); 80 MAIN_EXECUTOR.execute(() -> cb.accept(bundle)); 81 } 82 }); 83 } 84 85 /** 86 * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null 87 * if item is not supported prediction 88 */ getAppTargetFromInfo(ItemInfo info)89 public AppTarget getAppTargetFromInfo(ItemInfo info) { 90 if (info == null) return null; 91 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET 92 && info instanceof LauncherAppWidgetInfo 93 && ((LauncherAppWidgetInfo) info).providerName != null) { 94 ComponentName cn = ((LauncherAppWidgetInfo) info).providerName; 95 return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()), 96 cn.getPackageName(), info.user).setClassName(cn.getClassName()).build(); 97 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION 98 && info.getTargetComponent() != null) { 99 ComponentName cn = info.getTargetComponent(); 100 return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()), 101 cn.getPackageName(), info.user).setClassName(cn.getClassName()).build(); 102 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT 103 && info instanceof WorkspaceItemInfo) { 104 ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info); 105 //TODO: switch to using full shortcut info 106 return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()), 107 shortcutKey.componentName.getPackageName(), shortcutKey.user).build(); 108 } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { 109 return new AppTarget.Builder(new AppTargetId("folder:" + info.id), 110 mContext.getPackageName(), info.user).build(); 111 } 112 return null; 113 } 114 115 116 /** 117 * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item 118 * location using {@link ItemInfo} 119 */ wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info)120 public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) { 121 String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", 122 info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT 123 ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, 124 info.screenId, info.cellX, info.cellY, info.spanX, info.spanY); 125 return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build(); 126 } 127 128 /** 129 * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors 130 */ isTrackedForPrediction(ItemInfo info)131 public static boolean isTrackedForPrediction(ItemInfo info) { 132 return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || ( 133 info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP 134 && info.screenId == Workspace.FIRST_SCREEN_ID); 135 } 136 } 137