1 /* 2 * Copyright (C) 2019 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.model; 17 18 import android.content.Context; 19 import android.content.Intent; 20 import android.graphics.Bitmap; 21 import android.os.Process; 22 import android.util.Log; 23 24 import com.android.launcher3.LauncherSettings; 25 import com.android.launcher3.Utilities; 26 import com.android.launcher3.icons.BitmapInfo; 27 import com.android.launcher3.icons.LauncherIcons; 28 import com.android.launcher3.model.data.ItemInfo; 29 import com.android.launcher3.model.data.WorkspaceItemInfo; 30 import com.android.launcher3.testing.shared.TestProtocol; 31 import com.android.launcher3.util.IntArray; 32 import com.android.launcher3.util.IntSet; 33 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.List; 37 import java.util.Objects; 38 import java.util.stream.IntStream; 39 40 /** 41 * Utils class for {@link com.android.launcher3.LauncherModel}. 42 */ 43 public class ModelUtils { 44 45 private static final String TAG = "ModelUtils"; 46 47 /** 48 * Filters the set of items who are directly or indirectly (via another container) on the 49 * specified screen. 50 */ filterCurrentWorkspaceItems( final IntSet currentScreenIds, ArrayList<T> allWorkspaceItems, ArrayList<T> currentScreenItems, ArrayList<T> otherScreenItems)51 public static <T extends ItemInfo> void filterCurrentWorkspaceItems( 52 final IntSet currentScreenIds, 53 ArrayList<T> allWorkspaceItems, 54 ArrayList<T> currentScreenItems, 55 ArrayList<T> otherScreenItems) { 56 // Purge any null ItemInfos 57 allWorkspaceItems.removeIf(Objects::isNull); 58 // Order the set of items by their containers first, this allows use to walk through the 59 // list sequentially, build up a list of containers that are in the specified screen, 60 // as well as all items in those containers. 61 IntSet itemsOnScreen = new IntSet(); 62 Collections.sort(allWorkspaceItems, 63 (lhs, rhs) -> Integer.compare(lhs.container, rhs.container)); 64 for (T info : allWorkspaceItems) { 65 if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { 66 if (TestProtocol.sDebugTracing) { 67 Log.d(TestProtocol.NULL_INT_SET, "filterCurrentWorkspaceItems: " 68 + currentScreenIds); 69 } 70 if (currentScreenIds.contains(info.screenId)) { 71 currentScreenItems.add(info); 72 itemsOnScreen.add(info.id); 73 } else { 74 otherScreenItems.add(info); 75 } 76 } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) { 77 currentScreenItems.add(info); 78 itemsOnScreen.add(info.id); 79 } else { 80 if (itemsOnScreen.contains(info.container)) { 81 currentScreenItems.add(info); 82 itemsOnScreen.add(info.id); 83 } else { 84 otherScreenItems.add(info); 85 } 86 } 87 } 88 } 89 90 /** 91 * Iterates though current workspace items and returns available hotseat ranks for prediction. 92 */ getMissingHotseatRanks(List<ItemInfo> items, int len)93 public static IntArray getMissingHotseatRanks(List<ItemInfo> items, int len) { 94 IntSet seen = new IntSet(); 95 items.stream().filter( 96 info -> info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) 97 .forEach(i -> seen.add(i.screenId)); 98 IntArray result = new IntArray(len); 99 IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add); 100 return result; 101 } 102 103 104 /** 105 * Creates a workspace item info for the legacy shortcut intent 106 */ 107 @SuppressWarnings("deprecation") fromLegacyShortcutIntent(Context context, Intent data)108 public static WorkspaceItemInfo fromLegacyShortcutIntent(Context context, Intent data) { 109 if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class) 110 || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE, 111 Intent.ShortcutIconResource.class)) 112 || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) { 113 114 Log.e(TAG, "Invalid install shortcut intent"); 115 return null; 116 } 117 118 Intent launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT); 119 String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME); 120 if (launchIntent == null || label == null) { 121 Log.e(TAG, "Invalid install shortcut intent"); 122 return null; 123 } 124 125 final WorkspaceItemInfo info = new WorkspaceItemInfo(); 126 info.user = Process.myUserHandle(); 127 128 BitmapInfo iconInfo = null; 129 try (LauncherIcons li = LauncherIcons.obtain(context)) { 130 Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON); 131 if (bitmap != null) { 132 iconInfo = li.createIconBitmap(bitmap); 133 } else { 134 info.iconResource = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); 135 if (info.iconResource != null) { 136 iconInfo = li.createIconBitmap(info.iconResource); 137 } 138 } 139 } 140 141 if (iconInfo == null) { 142 Log.e(TAG, "Invalid icon by the app"); 143 return null; 144 } 145 info.bitmap = iconInfo; 146 info.contentDescription = info.title = Utilities.trim(label); 147 info.intent = launchIntent; 148 return info; 149 } 150 151 /** 152 * @return true if the extra is either null or is of type {@param type} 153 */ isValidExtraType(Intent intent, String key, Class type)154 private static boolean isValidExtraType(Intent intent, String key, Class type) { 155 Object extra = intent.getParcelableExtra(key); 156 return extra == null || type.isInstance(extra); 157 } 158 } 159