Merge "Add READ_DEVICE_CONFIG permission Bug: 138964490" into ub-launcher3-qt-qpr1-dev
am: 74aff3b206
Change-Id: I167b288775f16542791b24ad04b837cf22f66d17
diff --git a/Android.mk b/Android.mk
index 9d113d9..78ea02a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,12 +23,7 @@
LOCAL_USE_AAPT2 := true
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-
-ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
-else
- LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore
-endif
+LOCAL_STATIC_JAVA_LIBRARIES:= PluginCoreLib
LOCAL_SRC_FILES := \
$(call all-java-files-under, src_plugins)
@@ -151,11 +146,10 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_PRIVATE_PLATFORM_APIS := true
else
- LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
LOCAL_SDK_VERSION := system_current
LOCAL_MIN_SDK_VERSION := 26
endif
@@ -224,11 +218,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_PRIVATE_PLATFORM_APIS := true
else
- LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
LOCAL_SDK_VERSION := system_current
LOCAL_MIN_SDK_VERSION := 26
endif
@@ -271,11 +264,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
ifneq (,$(wildcard frameworks/base))
- LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
LOCAL_PRIVATE_PLATFORM_APIS := true
else
- LOCAL_STATIC_JAVA_LIBRARIES := libSharedSystemUI libLauncherProtos
LOCAL_SDK_VERSION := system_current
LOCAL_MIN_SDK_VERSION := 26
endif
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..f3db20e
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,2 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java
index b82f362..26c3313 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LoaderResults.java
@@ -16,9 +16,8 @@
package com.android.launcher3.model;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import java.lang.ref.WeakReference;
diff --git a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index ee113df..42b1194 100644
--- a/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/go/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -27,34 +27,23 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.notification.NotificationKeyData;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
/**
* Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
*/
public class DeepShortcutManager {
- private static DeepShortcutManager sInstance;
- private static final Object sInstanceLock = new Object();
+
+ private static final DeepShortcutManager sInstance = new DeepShortcutManager();
public static DeepShortcutManager getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new DeepShortcutManager(context.getApplicationContext());
- }
- return sInstance;
- }
+ return sInstance;
}
- private DeepShortcutManager(Context context) {
- }
+ private final QueryResult mFailure = new QueryResult();
- public boolean wasLastCallSuccess() {
- return false;
- }
-
- public void onShortcutsChanged(List<ShortcutInfo> shortcuts) {
- }
+ private DeepShortcutManager() { }
/**
* Queries for the shortcuts with the package name and provided ids.
@@ -62,18 +51,18 @@
* This method is intended to get the full details for shortcuts when they are added or updated,
* because we only get "key" fields in onShortcutsChanged().
*/
- public List<ShortcutInfo> queryForFullDetails(String packageName,
+ public QueryResult queryForFullDetails(String packageName,
List<String> shortcutIds, UserHandle user) {
- return Collections.emptyList();
+ return mFailure;
}
/**
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+ public QueryResult queryForShortcutsContainer(ComponentName activity,
UserHandle user) {
- return Collections.emptyList();
+ return mFailure;
}
/**
@@ -103,20 +92,28 @@
*
* If packageName is null, returns all pinned shortcuts regardless of package.
*/
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
- return Collections.emptyList();
+ public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) {
+ return mFailure;
}
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
+ public QueryResult queryForPinnedShortcuts(String packageName,
List<String> shortcutIds, UserHandle user) {
- return Collections.emptyList();
+ return mFailure;
}
- public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
- return Collections.emptyList();
+ public QueryResult queryForAllShortcuts(UserHandle user) {
+ return mFailure;
}
public boolean hasHostPermission() {
return false;
}
+
+
+ public static class QueryResult extends ArrayList<ShortcutInfo> {
+
+ public boolean wasSuccess() {
+ return true;
+ }
+ }
}
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 49fd436..055ade5 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -118,6 +118,7 @@
APP_USAGE_SETTINGS = 18;
BACK_GESTURE = 19;
UNDO = 20;
+ DISMISS_PREDICTION = 21;
}
enum TipType {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
index 24fc61b..7d8fab1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.appprediction;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
-
import android.annotation.TargetApi;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
@@ -34,13 +32,15 @@
import android.util.Log;
import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.util.UiThreadHelper;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
+import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
/**
* Subclass of app tracker which publishes the data to the prediction engine and gets back results.
@@ -174,10 +174,11 @@
public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
String container) {
// TODO: Use the full shortcut info
- AppTarget target = new AppTarget
- .Builder(new AppTargetId("shortcut:" + shortcutId), packageName, user)
- .setClassName(shortcutId)
- .build();
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("shortcut:" + shortcutId), packageName, user)
+ .setClassName(shortcutId)
+ .build();
+
sendLaunch(target, container);
}
@@ -185,19 +186,40 @@
@UiThread
public void onStartApp(ComponentName cn, UserHandle user, String container) {
if (cn != null) {
- AppTarget target = new AppTarget
- .Builder(new AppTargetId("app:" + cn), cn.getPackageName(), user)
- .setClassName(cn.getClassName())
- .build();
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("app:" + cn), cn.getPackageName(), user)
+ .setClassName(cn.getClassName())
+ .build();
sendLaunch(target, container);
}
}
+ @Override
@UiThread
- private void sendLaunch(AppTarget target, String container) {
- AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH)
+ public void onDismissApp(ComponentName cn, UserHandle user, String container) {
+ if (cn == null) return;
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("app: " + cn), cn.getPackageName(), user)
+ .setClassName(cn.getClassName())
+ .build();
+ sendDismiss(target, container);
+ }
+
+ @UiThread
+ private void sendEvent(AppTarget target, String container, int eventId) {
+ AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
.setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
.build();
Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
}
+
+ @UiThread
+ private void sendLaunch(AppTarget target, String container) {
+ sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH);
+ }
+
+ @UiThread
+ private void sendDismiss(AppTarget target, String container) {
+ sendEvent(target, container, AppTargetEvent.ACTION_DISMISS);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index 0c7ba9c..95f63ce 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
@@ -281,7 +282,7 @@
}
private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) {
- if (getAppsStore().getApps().isEmpty()) {
+ if (getAppsStore().getApps().length == 0) {
// Apps have not been bound yet.
return Collections.emptyList();
}
@@ -290,7 +291,9 @@
for (ComponentKeyMapper mapper : components) {
ItemInfoWithIcon info = mapper.getApp(getAppsStore());
if (info != null) {
- predictedApps.add(info);
+ ItemInfoWithIcon predictedApp = info.clone();
+ predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
+ predictedApps.add(predictedApp);
} else {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index f08ae4a..9bdc98b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -136,6 +136,12 @@
}
@Override
+ public void returnToHomescreen() {
+ super.returnToHomescreen();
+ // TODO(b/137318995) This should go home, but doing so removes freeform windows
+ }
+
+ @Override
public ActivityOptions getActivityLaunchOptions(final View v) {
if (!(v instanceof TaskView)) {
return null;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
index fd45923..1af0db0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
@@ -36,8 +36,6 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
@@ -267,12 +265,16 @@
@Override
protected ActivityOptions makeLaunchOptions(Activity activity) {
- return ActivityOptionsCompat.makeFreeformOptions();
+ ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
+ // Arbitrary bounds only because freeform is in dev mode right now
+ Rect r = new Rect(50, 50, 200, 200);
+ activityOptions.setLaunchBounds(r);
+ return activityOptions;
}
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
- Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL);
+ activity.returnToHomescreen();
return true;
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index e41dba9..71ce32b 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,10 +20,12 @@
import android.annotation.TargetApi;
import android.app.ActivityManager;
-import android.content.Context;
import android.os.Build;
import android.os.Process;
import android.util.SparseBooleanArray;
+
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.MainThreadExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -42,6 +44,7 @@
private final KeyguardManagerCompat mKeyguardManager;
private final MainThreadExecutor mMainThreadExecutor;
+ private final ActivityManagerWrapper mActivityManagerWrapper;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -52,11 +55,14 @@
ArrayList<Task> mTasks = new ArrayList<>();
- public RecentTasksList(Context context) {
- mMainThreadExecutor = new MainThreadExecutor();
- mKeyguardManager = new KeyguardManagerCompat(context);
+
+ public RecentTasksList(MainThreadExecutor mainThreadExecutor,
+ KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) {
+ mMainThreadExecutor = mainThreadExecutor;
+ mKeyguardManager = keyguardManager;
mChangeId = 1;
- ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+ mActivityManagerWrapper = activityManagerWrapper;
+ mActivityManagerWrapper.registerTaskStackListener(this);
}
/**
@@ -136,12 +142,13 @@
/**
* Loads and creates a list of all the recent tasks.
*/
- private ArrayList<Task> loadTasksInBackground(int numTasks,
+ @VisibleForTesting
+ ArrayList<Task> loadTasksInBackground(int numTasks,
boolean loadKeysOnly) {
int currentUserId = Process.myUserHandle().getIdentifier();
ArrayList<Task> allTasks = new ArrayList<>();
List<ActivityManager.RecentTaskInfo> rawTasks =
- ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId);
+ mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
// The raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(rawTasks);
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index dfab434..d9ecdcd 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -25,13 +25,18 @@
import android.os.HandlerThread;
import android.os.Process;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.KeyguardManagerCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
@@ -64,10 +69,12 @@
HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
Process.THREAD_PRIORITY_BACKGROUND);
loaderThread.start();
- mTaskList = new RecentTasksList(context);
+ mTaskList = new RecentTasksList(new MainThreadExecutor(),
+ new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
mIconCache = new TaskIconCache(context, loaderThread.getLooper());
mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper());
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+ setupPackageListener();
}
public TaskIconCache getIconCache() {
@@ -166,6 +173,7 @@
public void onTaskRemoved(int taskId) {
Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
mThumbnailCache.remove(dummyKey);
+ mIconCache.onTaskRemoved(dummyKey);
}
public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
@@ -200,6 +208,21 @@
}
}
+ private void setupPackageListener() {
+ LauncherAppsCompat.getInstance(mContext)
+ .addOnAppsChangedCallback(new OnAppsChangedCallbackCompat() {
+ @Override
+ public void onPackageRemoved(String packageName, UserHandle user) {
+ mIconCache.invalidatePackage(packageName);
+ }
+
+ @Override
+ public void onPackageChanged(String packageName, UserHandle user) {
+ mIconCache.invalidatePackage(packageName);
+ }
+ });
+ }
+
public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
mThumbnailChangeListeners.add(listener);
}
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 07af9b3..5e01958 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -31,17 +31,18 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.cache.HandlerRunnable;
-import com.android.launcher3.uioverrides.RecentsUiFactory;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import java.util.Map;
import java.util.function.Consumer;
/**
* Manages the caching of task icons and related data.
- * TODO: This class should later be merged into IconCache.
+ * TODO(b/138944598): This class should later be merged into IconCache.
*/
public class TaskIconCache {
@@ -149,6 +150,21 @@
return label;
}
+
+ void onTaskRemoved(TaskKey taskKey) {
+ mIconCache.remove(taskKey);
+ }
+
+ void invalidatePackage(String packageName) {
+ // TODO(b/138944598): Merge this class into IconCache so we can do this at the base level
+ Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot();
+ for (ComponentName cn : activityInfoCache.keySet()) {
+ if (cn.getPackageName().equals(packageName)) {
+ mActivityInfoCache.remove(cn);
+ }
+ }
+ }
+
public static abstract class IconLoadRequest extends HandlerRunnable {
IconLoadRequest(Handler handler) {
super(handler, null);
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
index d0956d1..7801775 100644
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
@@ -150,10 +150,10 @@
List<AppTarget> targets = new ArrayList<>(activities.length);
for (LauncherActivityInfo info : activities) {
ComponentName cn = info.getComponentName();
- AppTarget target =
- new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser())
- .setClassName(cn.getClassName())
- .build();
+ AppTarget target = new AppTarget.Builder(
+ new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser())
+ .setClassName(cn.getClassName())
+ .build();
targets.add(target);
}
mCallback.onTargetsAvailable(targets);
diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
new file mode 100644
index 0000000..5fb27bc
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.quickstep;
+
+import android.app.ActivityManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.KeyguardManagerCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static junit.framework.TestCase.assertNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@SmallTest
+public class RecentTasksListTest {
+
+ private ActivityManagerWrapper mockActivityManagerWrapper;
+
+ // Class under test
+ private RecentTasksList mRecentTasksList;
+
+ @Before
+ public void setup() {
+ MainThreadExecutor mockMainThreadExecutor = mock(MainThreadExecutor.class);
+ KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class);
+ mockActivityManagerWrapper = mock(ActivityManagerWrapper.class);
+ mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat,
+ mockActivityManagerWrapper);
+ }
+
+ @Test
+ public void onTaskRemoved_reloadsAllTasks() {
+ mRecentTasksList.onTaskRemoved(0);
+ verify(mockActivityManagerWrapper, times(1))
+ .getRecentTasks(anyInt(), anyInt());
+ }
+
+ @Test
+ public void onTaskStackChanged_doesNotFetchTasks() {
+ mRecentTasksList.onTaskStackChanged();
+ verify(mockActivityManagerWrapper, times(0))
+ .getRecentTasks(anyInt(), anyInt());
+ }
+
+ @Test
+ public void loadTasksInBackground_onlyKeys_noValidTaskDescription() {
+ ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
+ when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(recentTaskInfo));
+
+ List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true);
+
+ assertEquals(1, taskList.size());
+ assertNull(taskList.get(0).taskDescription.getLabel());
+ }
+
+ @Test
+ public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() {
+ String taskDescription = "Wheeee!";
+ ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
+ recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription);
+ when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt()))
+ .thenReturn(Collections.singletonList(recentTaskInfo));
+
+ List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false);
+
+ assertEquals(1, taskList.size());
+ assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel());
+ }
+}
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 13e096c..9d9c2e8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -23,8 +23,6 @@
<!-- Application name -->
<string name="app_name">Launcher3</string>
- <!-- Default folder name -->
- <string name="folder_name"></string>
<!-- Work folder name -->
<string name="work_folder_name">Work</string>
<!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
@@ -105,6 +103,9 @@
<!-- Label for install drop target. [CHAR_LIMIT=20] -->
<string name="install_drop_target_label">Install</string>
+ <!-- Label for install dismiss prediction. -->
+ <string translatable="false" name="dismiss_prediction_label">Dismiss prediction</string>
+
<!-- Permissions: -->
<skip />
<!-- Permission short label -->
diff --git a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index ab39274..bc936b7 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -15,14 +15,13 @@
import android.os.Process;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppFilter;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.icons.IconCache;
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
new file mode 100644
index 0000000..c08e198
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/util/IntArrayTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/**
+ * Robolectric unit tests for {@link IntArray}
+ */
+@RunWith(RobolectricTestRunner.class)
+public class IntArrayTest {
+
+ @Test
+ public void concatAndParseString() {
+ int[] array = new int[] {0, 2, 3, 9};
+ String concat = IntArray.wrap(array).toConcatString();
+
+ int[] parsed = IntArray.fromConcatString(concat).toArray();
+ assertThat(array).isEqualTo(parsed);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
index f846de5..8513353 100644
--- a/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
+++ b/robolectric_tests/src/com/android/launcher3/util/IntSetTest.java
@@ -21,7 +21,6 @@
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 65f9d6b..8fddf3c 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -86,7 +86,7 @@
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
- | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
+ | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
// Usually we show the back button when a floating view is open. Instead, hide for these types.
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index d884049..c8e7619 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -29,11 +29,19 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
+import java.util.Comparator;
+
/**
* Represents an app in AllAppsView.
*/
public class AppInfo extends ItemInfoWithIcon {
+ public static AppInfo[] EMPTY_ARRAY = new AppInfo[0];
+ public static Comparator<AppInfo> COMPONENT_KEY_COMPARATOR = (a, b) -> {
+ int uc = a.user.hashCode() - b.user.hashCode();
+ return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
+ };
+
/**
* The intent used to start the application.
*/
@@ -41,6 +49,9 @@
public ComponentName componentName;
+ // Section name used for indexing.
+ public String sectionName = "";
+
public AppInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
}
@@ -74,6 +85,8 @@
componentName = info.componentName;
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
+ user = info.user;
+ runtimeStatusFlags = info.runtimeStatusFlags;
}
@Override
@@ -116,4 +129,9 @@
info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
}
}
+
+ @Override
+ public AppInfo clone() {
+ return new AppInfo(this);
+ }
}
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 9724869..8bf1a37 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -43,6 +43,7 @@
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -72,7 +73,7 @@
static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback) {
- Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk(
+ Pair<String, Resources> customizationApkInfo = PackageManagerHelper.findSystemApk(
ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
if (customizationApkInfo == null) {
return null;
@@ -548,7 +549,7 @@
if (titleResId != 0) {
title = mSourceRes.getString(titleResId);
} else {
- title = mContext.getResources().getString(R.string.folder_name);
+ title = "";
}
mValues.put(Favorites.TITLE, title);
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f0b3afd..f61051f 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -34,9 +34,9 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import androidx.annotation.Nullable;
@@ -120,6 +120,10 @@
public abstract View getRootView();
+ public void returnToHomescreen() {
+ // no-op
+ }
+
public Rect getViewBounds(View v) {
int[] pos = new int[2];
v.getLocationOnScreen(pos);
@@ -135,7 +139,7 @@
public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
@Nullable String sourceContainer) {
- if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+ if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 09fb244..976ccd5 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2702,6 +2702,14 @@
}
}
+ /**
+ * Sets the position to the provided point
+ */
+ public void setXY(Point point) {
+ cellX = point.x;
+ cellY = point.y;
+ }
+
public String toString() {
return "(" + this.cellX + ", " + this.cellY + ")";
}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index b747d62..67d5ab0 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -50,9 +50,9 @@
/**
* The apps and shortcuts
*/
- public ArrayList<WorkspaceItemInfo> contents = new ArrayList<WorkspaceItemInfo>();
+ public ArrayList<WorkspaceItemInfo> contents = new ArrayList<>();
- ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
+ private ArrayList<FolderListener> mListeners = new ArrayList<>();
public FolderInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
@@ -72,10 +72,10 @@
* Add an app or shortcut for a specified rank.
*/
public void add(WorkspaceItemInfo item, int rank, boolean animate) {
- rank = Utilities.boundToRange(rank, 0, contents.size());
+ rank = Utilities.boundToRange(rank, 0, contents.size() + 1);
contents.add(rank, item);
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onAdd(item, rank);
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onAdd(item, rank);
}
itemsChanged(animate);
}
@@ -87,16 +87,16 @@
*/
public void remove(WorkspaceItemInfo item, boolean animate) {
contents.remove(item);
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onRemove(item);
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onRemove(item);
}
itemsChanged(animate);
}
public void setTitle(CharSequence title) {
this.title = title;
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onTitleChanged(title);
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onTitleChanged(title);
}
}
@@ -105,26 +105,25 @@
super.onAddToDatabase(writer);
writer.put(LauncherSettings.Favorites.TITLE, title)
.put(LauncherSettings.Favorites.OPTIONS, options);
-
}
public void addListener(FolderListener listener) {
- listeners.add(listener);
+ mListeners.add(listener);
}
public void removeListener(FolderListener listener) {
- listeners.remove(listener);
+ mListeners.remove(listener);
}
public void itemsChanged(boolean animate) {
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onItemsChanged(animate);
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).onItemsChanged(animate);
}
}
public void prepareAutoUpdate() {
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).prepareAutoUpdate();
+ for (int i = 0; i < mListeners.size(); i++) {
+ mListeners.get(i).prepareAutoUpdate();
}
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index e9b932a..a6b53b9 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -612,7 +612,7 @@
// Already an activity target
return original;
}
- if (!Utilities.isLauncherAppTarget(original.launchIntent)) {
+ if (!PackageManagerHelper.isLauncherAppTarget(original.launchIntent)) {
return original;
}
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index e29f927..1550bb0 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -27,6 +27,8 @@
*/
public abstract class ItemInfoWithIcon extends ItemInfo {
+ public static final String TAG = "ItemInfoDebug";
+
/**
* A bitmap version of the application icon.
*/
@@ -126,4 +128,8 @@
iconColor = info.color;
}
+ /**
+ * @return a copy of this
+ */
+ public abstract ItemInfoWithIcon clone();
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d79230f..cdb16d2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,10 +16,11 @@
package com.android.launcher3;
-import static android.content.pm.ActivityInfo.CONFIG_LOCALE;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
+import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -77,6 +78,8 @@
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -91,9 +94,8 @@
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.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -103,14 +105,13 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -156,13 +157,11 @@
import java.util.List;
import java.util.function.Predicate;
-import androidx.annotation.Nullable;
-
/**
* Default launcher application.
*/
public class Launcher extends BaseDraggingActivity implements LauncherExterns,
- LauncherModel.Callbacks, LauncherProviderChangeListener, UserEventDelegate,
+ Callbacks, LauncherProviderChangeListener, UserEventDelegate,
InvariantDeviceProfile.OnIDPChangeListener {
public static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -418,10 +417,6 @@
public void onConfigurationChanged(Configuration newConfig) {
int diff = newConfig.diff(mOldConfig);
- if ((diff & CONFIG_LOCALE) != 0) {
- Folder.setLocaleDependentFields(getResources(), true /* force */);
- }
-
if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
onIdpChanged(mDeviceProfile.inv);
}
@@ -603,10 +598,9 @@
if (info.container >= 0) {
View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
if (folderIcon instanceof FolderIcon && folderIcon.getTag() instanceof FolderInfo) {
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(getDeviceProfile().inv);
- verifier.setFolderInfo((FolderInfo) folderIcon.getTag());
- if (verifier.isItemInPreview(info.rank)) {
+ if (new FolderGridOrganizer(getDeviceProfile().inv)
+ .setFolderInfo((FolderInfo) folderIcon.getTag())
+ .isItemInPreview(info.rank)) {
folderIcon.invalidate();
}
}
@@ -1435,9 +1429,10 @@
outState.remove(RUNTIME_STATE_WIDGET_PANEL);
}
- // We close any open folders and shortcut containers since they will not be re-opened,
+ // We close any open folders and shortcut containers that are not safe for rebind,
// and we need to make sure this state is reflected.
- AbstractFloatingView.closeAllOpenViews(this, false);
+ AbstractFloatingView.closeOpenViews(this, false, TYPE_ALL & ~TYPE_REBIND_SAFE);
+ finishAutoCancelActionMode();
if (mPendingRequestArgs != null) {
outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
@@ -1668,7 +1663,7 @@
FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
int cellY) {
final FolderInfo folderInfo = new FolderInfo();
- folderInfo.title = getText(R.string.folder_name);
+ folderInfo.title = "";
// Update the model
getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
@@ -1722,8 +1717,6 @@
return true;
}
-
-
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
@@ -1922,8 +1915,7 @@
// Floating panels (except the full widget sheet) are associated with individual icons. If
// we are starting a fresh bind, close all such panels as all the icons are about
// to go away.
- AbstractFloatingView.closeOpenViews(this, true,
- AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+ AbstractFloatingView.closeOpenViews(this, true, TYPE_ALL & ~TYPE_REBIND_SAFE);
setWorkspaceLoading(true);
@@ -2344,7 +2336,7 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAllApplications(ArrayList<AppInfo> apps) {
+ public void bindAllApplications(AppInfo[] apps) {
mAppsView.getAppsStore().setApps(apps);
}
@@ -2357,16 +2349,6 @@
mPopupDataProvider.setDeepShortcutMap(deepShortcutMapCopy);
}
- /**
- * A package was updated.
- *
- * Implementation of the method from LauncherModel.Callbacks.
- */
- @Override
- public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
- mAppsView.getAppsStore().addOrUpdateApps(apps);
- }
-
@Override
public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
mAppsView.getAppsStore().updatePromiseAppProgress(app);
@@ -2414,11 +2396,6 @@
}
@Override
- public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
- mAppsView.getAppsStore().removeApps(appInfos);
- }
-
- @Override
public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
mPopupDataProvider.setAllWidgets(allWidgets);
}
@@ -2577,6 +2554,12 @@
return (Launcher) fromContext(context);
}
+ @Override
+ public void returnToHomescreen() {
+ super.returnToHomescreen();
+ getStateManager().goToState(LauncherState.NORMAL);
+ }
+
/**
* Just a wrapper around the type cast to allow easier tracking of calls.
*/
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index d79f5d5..eed2377 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -38,8 +38,10 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
+import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.CacheDataUpdatedTask;
import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.model.LoaderTask;
@@ -49,20 +51,14 @@
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.CancellationException;
@@ -133,33 +129,6 @@
}
};
- public interface Callbacks {
- public void rebindModel();
-
- public int getCurrentWorkspaceScreen();
- public void clearPendingBinds();
- public void startBinding();
- public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
- public void bindScreens(IntArray orderedScreenIds);
- public void finishFirstPageBind(ViewOnDrawExecutor executor);
- public void finishBindingItems(int pageBoundFirst);
- public void bindAllApplications(ArrayList<AppInfo> apps);
- public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps);
- public void preAddApps();
- public void bindAppsAdded(IntArray newScreens,
- ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
- public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
- public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
- public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
- public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
- public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
- public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
- public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
- public void onPageBoundSynchronously(int page);
- public void executeOnNextDraw(ViewOnDrawExecutor executor);
- public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
- }
-
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
@@ -411,16 +380,7 @@
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
apps.addPromiseApp(app.getContext(), sessionInfo);
- if (!apps.added.isEmpty()) {
- final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added);
- apps.added.clear();
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppsAddedOrUpdated(arrayList);
- }
- });
- }
+ bindApplicationsIfNeeded();
}
});
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 6ad5c36..6081300 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -33,7 +33,6 @@
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
import android.database.Cursor;
@@ -50,7 +49,6 @@
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.BaseColumns;
import android.provider.Settings;
import android.text.TextUtils;
@@ -70,6 +68,7 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NoLocaleSQLiteHelper;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
@@ -77,7 +76,6 @@
import java.io.File;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
@@ -873,7 +871,7 @@
continue;
}
- if (!Utilities.isLauncherAppTarget(intent)) {
+ if (!PackageManagerHelper.isLauncherAppTarget(intent)) {
continue;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index e248ba0..242e099 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -122,11 +122,13 @@
*/
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;
+ public static final int CONTAINER_PREDICTION = -102;
static final String containerToString(int container) {
switch (container) {
case CONTAINER_DESKTOP: return "desktop";
case CONTAINER_HOTSEAT: return "hotseat";
+ case CONTAINER_PREDICTION: return "prediction";
default: return String.valueOf(container);
}
}
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index 380078b..af5402a 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.util.PackageManagerHelper.findSystemApk;
+
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.util.DisplayMetrics;
@@ -59,7 +61,7 @@
*/
public static synchronized Partner get(PackageManager pm) {
if (!sSearched) {
- Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
+ Pair<String, Resources> apkInfo = findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
if (apkInfo != null) {
sPartner = new Partner(apkInfo.first, apkInfo.second);
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 3bef598..05336f2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -24,15 +24,9 @@
import android.app.Person;
import android.app.WallpaperManager;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
@@ -47,7 +41,6 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.os.Build;
-import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Message;
@@ -60,7 +53,6 @@
import android.text.style.TtsSpan;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Pair;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
@@ -69,7 +61,6 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.TintedDrawableSpan;
@@ -81,13 +72,10 @@
import com.android.launcher3.views.Transposable;
import com.android.launcher3.widget.PendingAddShortcutInfo;
-import java.io.Closeable;
-import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
-import java.util.StringTokenizer;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
@@ -247,7 +235,6 @@
return scale;
}
-
/**
* Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, float[], boolean)}.
*/
@@ -383,53 +370,6 @@
return min + (value * (max - min));
}
- public static boolean isSystemApp(Context context, Intent intent) {
- PackageManager pm = context.getPackageManager();
- ComponentName cn = intent.getComponent();
- String packageName = null;
- if (cn == null) {
- ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if ((info != null) && (info.activityInfo != null)) {
- packageName = info.activityInfo.packageName;
- }
- } else {
- packageName = cn.getPackageName();
- }
- if (packageName != null) {
- try {
- PackageInfo info = pm.getPackageInfo(packageName, 0);
- return (info != null) && (info.applicationInfo != null) &&
- ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
- } catch (NameNotFoundException e) {
- return false;
- }
- } else {
- return false;
- }
- }
-
- /*
- * Finds a system apk which had a broadcast receiver listening to a particular action.
- * @param action intent action used to find the apk
- * @return a pair of apk package name and the resources.
- */
- static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
- final Intent intent = new Intent(action);
- for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
- if (info.activityInfo != null &&
- (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- final String packageName = info.activityInfo.packageName;
- try {
- final Resources res = pm.getResourcesForApplication(packageName);
- return Pair.create(packageName, res);
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Failed to find resources for " + packageName);
- }
- }
- }
- return null;
- }
-
/**
* Trims the string, removing all whitespace at the beginning and end of the string.
* Non-breaking whitespaces are also removed.
@@ -454,51 +394,10 @@
return (int) Math.ceil(fm.bottom - fm.top);
}
- /**
- * Convenience println with multiple args.
- */
- public static void println(String key, Object... args) {
- StringBuilder b = new StringBuilder();
- b.append(key);
- b.append(": ");
- boolean isFirstArgument = true;
- for (Object arg : args) {
- if (isFirstArgument) {
- isFirstArgument = false;
- } else {
- b.append(", ");
- }
- b.append(arg);
- }
- System.out.println(b.toString());
- }
-
public static boolean isRtl(Resources res) {
return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
}
- /**
- * Returns true if the intent is a valid launch intent for a launcher activity of an app.
- * This is used to identify shortcuts which are different from the ones exposed by the
- * applications' manifest file.
- *
- * @param launchIntent The intent that will be launched when the shortcut is clicked.
- */
- public static boolean isLauncherAppTarget(Intent launchIntent) {
- if (launchIntent != null
- && Intent.ACTION_MAIN.equals(launchIntent.getAction())
- && launchIntent.getComponent() != null
- && launchIntent.getCategories() != null
- && launchIntent.getCategories().size() == 1
- && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
- && TextUtils.isEmpty(launchIntent.getDataString())) {
- // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
- Bundle extras = launchIntent.getExtras();
- return extras == null || extras.keySet().isEmpty();
- }
- return false;
- }
-
public static float dpiFromPx(int size, DisplayMetrics metrics){
float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
return (size / densityRatio);
@@ -601,18 +500,6 @@
return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed();
}
- public static void closeSilently(Closeable c) {
- if (c != null) {
- try {
- c.close();
- } catch (IOException e) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- Log.d(TAG, "Error closing", e);
- }
- }
- }
- }
-
public static boolean isBinderSizeError(Exception e) {
return e.getCause() instanceof TransactionTooLargeException
|| e.getCause() instanceof DeadObjectException;
@@ -727,25 +614,6 @@
}
}
- public static int[] getIntArrayFromString(String tokenized) {
- StringTokenizer tokenizer = new StringTokenizer(tokenized, ",");
- int[] array = new int[tokenizer.countTokens()];
- int count = 0;
- while (tokenizer.hasMoreTokens()) {
- array[count] = Integer.parseInt(tokenizer.nextToken());
- count++;
- }
- return array;
- }
-
- public static String getStringFromIntArray(int[] array) {
- StringBuilder str = new StringBuilder();
- for (int value : array) {
- str.append(value).append(",");
- }
- return str.toString();
- }
-
public static float squaredHypot(float x, float y) {
return x * x + y * y;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3be91d4..812f444 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,9 +67,9 @@
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -3102,7 +3102,7 @@
ItemInfo info = (ItemInfo) item.getTag();
if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
FolderIcon folder = (FolderIcon) item;
- ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+ ArrayList<View> folderChildren = folder.getFolder().getIconsInReadingOrder();
// map over all the children in the folder
final int childCount = folderChildren.size();
for (int childIdx = 0; childIdx < childCount; childIdx++) {
diff --git a/src/com/android/launcher3/WorkspaceItemInfo.java b/src/com/android/launcher3/WorkspaceItemInfo.java
index b72866c..9a9aa97 100644
--- a/src/com/android/launcher3/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/WorkspaceItemInfo.java
@@ -219,4 +219,9 @@
}
return cn;
}
+
+ @Override
+ public ItemInfoWithIcon clone() {
+ return new WorkspaceItemInfo(this);
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 267363f..a505240 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
+
import android.view.View;
import android.view.ViewGroup;
@@ -26,8 +29,7 @@
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
+import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -45,27 +47,33 @@
public static final int DEFER_UPDATES_TEST = 1 << 2;
private PackageUserKey mTempKey = new PackageUserKey(null, null);
- private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
+ private AppInfo mTempInfo = new AppInfo();
+
+ private AppInfo[] mApps = EMPTY_ARRAY;
+
private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
private int mDeferUpdatesFlags = 0;
private boolean mUpdatePending = false;
- public Collection<AppInfo> getApps() {
- return mComponentToAppMap.values();
+ public AppInfo[] getApps() {
+ return mApps;
}
/**
* Sets the current set of apps.
*/
- public void setApps(List<AppInfo> apps) {
- mComponentToAppMap.clear();
- addOrUpdateApps(apps);
+ public void setApps(AppInfo[] apps) {
+ mApps = apps;
+ notifyUpdate();
}
public AppInfo getApp(ComponentKey key) {
- return mComponentToAppMap.get(key);
+ mTempInfo.componentName = key.componentName;
+ mTempInfo.user = key.user;
+ int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR);
+ return index < 0 ? null : mApps[index];
}
public void enableDeferUpdates(int flag) {
@@ -88,27 +96,6 @@
return mDeferUpdatesFlags;
}
- /**
- * Adds or updates existing apps in the list
- */
- public void addOrUpdateApps(List<AppInfo> apps) {
- for (AppInfo app : apps) {
- mComponentToAppMap.put(app.toComponentKey(), app);
- }
- notifyUpdate();
- }
-
- /**
- * Removes some apps from the list.
- */
- public void removeApps(List<AppInfo> apps) {
- for (AppInfo app : apps) {
- mComponentToAppMap.remove(app.toComponentKey());
- }
- notifyUpdate();
- }
-
-
private void notifyUpdate() {
if (mDeferUpdatesFlags != 0) {
mUpdatePending = true;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 1369441..0c4be62 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -21,16 +21,13 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -145,9 +142,7 @@
// The of ordered component names as a result of a search query
private ArrayList<ComponentKey> mSearchResults;
- private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
private AllAppsGridAdapter mAdapter;
- private AlphabeticIndexCompat mIndexer;
private AppInfoComparator mAppNameComparator;
private final int mNumAppsPerRow;
private int mNumAppRowsInAdapter;
@@ -156,7 +151,6 @@
public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
mAllAppsStore = appsStore;
mLauncher = Launcher.getLauncher(context);
- mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
@@ -263,7 +257,7 @@
TreeMap<String, ArrayList<AppInfo>> sectionMap = new TreeMap<>(new LabelComparator());
for (AppInfo info : mApps) {
// Add the section to the cache
- String sectionName = getAndUpdateCachedSectionName(info.title);
+ String sectionName = info.sectionName;
// Add it to the mapping
ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
@@ -279,12 +273,6 @@
for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
mApps.addAll(entry.getValue());
}
- } else {
- // Just compute the section headers for use below
- for (AppInfo info : mApps) {
- // Add the section to the cache
- getAndUpdateCachedSectionName(info.title);
- }
}
// Recompose the set of adapter items from the current set of apps
@@ -320,7 +308,7 @@
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
for (AppInfo info : getFiltersAppInfos()) {
- String sectionName = getAndUpdateCachedSectionName(info.title);
+ String sectionName = info.sectionName;
// Create a new section if the section names do not match
if (!sectionName.equals(lastSectionName)) {
@@ -428,18 +416,4 @@
}
return result;
}
-
- /**
- * Returns the cached section name for the given title, recomputing and updating the cache if
- * the title has no cached section name.
- */
- private String getAndUpdateCachedSectionName(CharSequence title) {
- String sectionName = mCachedSectionNames.get(title);
- if (sectionName == null) {
- sectionName = mIndexer.computeSectionName(title);
- mCachedSectionNames.put(title, sectionName);
- }
- return sectionName;
- }
-
}
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index dfdcc70..46c9006 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -3,7 +3,6 @@
import android.content.Context;
import android.icu.text.AlphabeticIndex;
import android.os.LocaleList;
-import android.util.Log;
import com.android.launcher3.Utilities;
@@ -12,28 +11,32 @@
import androidx.annotation.NonNull;
public class AlphabeticIndexCompat {
- private static final String TAG = "AlphabeticIndexCompat";
private static final String MID_DOT = "\u2219";
- private final BaseIndex mBaseIndex;
private final String mDefaultMiscLabel;
+ private final AlphabeticIndex.ImmutableIndex mBaseIndex;
+
public AlphabeticIndexCompat(Context context) {
- BaseIndex index = null;
+ this(context.getResources().getConfiguration().getLocales());
+ }
- try {
- index = new AlphabeticIndexVN(context);
- } catch (Exception e) {
- Log.d(TAG, "Unable to load the system index", e);
+ public AlphabeticIndexCompat(LocaleList locales) {
+ int localeCount = locales.size();
+
+ Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0);
+ AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale);
+ for (int i = 1; i < localeCount; i++) {
+ indexBuilder.addLabels(locales.get(i));
}
+ indexBuilder.addLabels(Locale.ENGLISH);
+ mBaseIndex = indexBuilder.buildImmutableIndex();
- mBaseIndex = index == null ? new BaseIndex() : index;
-
- if (context.getResources().getConfiguration().locale
- .getLanguage().equals(Locale.JAPANESE.getLanguage())) {
+ if (primaryLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) {
// Japanese character ä»– ("misc")
mDefaultMiscLabel = "\u4ed6";
- // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
+ // TODO(winsonc, omakoto): We need to handle Japanese sections better,
+ // especially the kanji
} else {
// Dot
mDefaultMiscLabel = MID_DOT;
@@ -45,7 +48,7 @@
*/
public String computeSectionName(@NonNull CharSequence cs) {
String s = Utilities.trim(cs);
- String sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s));
+ String sectionName = mBaseIndex.getBucket(mBaseIndex.getBucketIndex(s)).getLabel();
if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
int c = s.codePointAt(0);
boolean startsWithDigit = Character.isDigit(c);
@@ -66,71 +69,4 @@
}
return sectionName;
}
-
- /**
- * Base class to support Alphabetic indexing if not supported by the framework.
- * TODO(winsonc): disable for non-english locales
- */
- private static class BaseIndex {
-
- private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-";
- private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1;
-
- /**
- * Returns the index of the bucket in which the given string should appear.
- */
- protected int getBucketIndex(@NonNull String s) {
- if (s.isEmpty()) {
- return UNKNOWN_BUCKET_INDEX;
- }
- int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase());
- if (index != -1) {
- return index;
- }
- return UNKNOWN_BUCKET_INDEX;
- }
-
- /**
- * Returns the label for the bucket at the given index (as returned by getBucketIndex).
- */
- protected String getBucketLabel(int index) {
- return BUCKETS.substring(index, index + 1);
- }
- }
-
- /**
- * Implementation based on {@link AlphabeticIndex}.
- */
- private static class AlphabeticIndexVN extends BaseIndex {
-
- private final AlphabeticIndex.ImmutableIndex mAlphabeticIndex;
-
- public AlphabeticIndexVN(Context context) {
- LocaleList locales = context.getResources().getConfiguration().getLocales();
- int localeCount = locales.size();
-
- Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0);
- AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale);
- for (int i = 1; i < localeCount; i++) {
- indexBuilder.addLabels(locales.get(i));
- }
- indexBuilder.addLabels(Locale.ENGLISH);
-
- mAlphabeticIndex = indexBuilder.buildImmutableIndex();
- }
-
- /**
- * Returns the index of the bucket in which {@param s} should appear.
- */
- protected int getBucketIndex(String s) {
- return mAlphabeticIndex.getBucketIndex(s);
- }
-
- /**
- * Returns the label for the bucket at the given index
- */
- protected String getBucketLabel(int index) {
- return mAlphabeticIndex.getBucket(index).getLabel();
- }
- }
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 58fc73d..cdb5c4d 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -27,25 +27,27 @@
import android.os.Bundle;
import android.os.UserHandle;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Utilities;
import com.android.launcher3.util.PackageUserKey;
import java.util.List;
-import androidx.annotation.Nullable;
-
public abstract class LauncherAppsCompat {
public interface OnAppsChangedCallbackCompat {
- void onPackageRemoved(String packageName, UserHandle user);
- void onPackageAdded(String packageName, UserHandle user);
- void onPackageChanged(String packageName, UserHandle user);
- void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing);
- void onPackagesUnavailable(String[] packageNames, UserHandle user, boolean replacing);
- void onPackagesSuspended(String[] packageNames, UserHandle user);
- void onPackagesUnsuspended(String[] packageNames, UserHandle user);
- void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user);
+ default void onPackageRemoved(String packageName, UserHandle user) { }
+ default void onPackageAdded(String packageName, UserHandle user) { }
+ default void onPackageChanged(String packageName, UserHandle user) { }
+ default void onPackagesAvailable(String[] packageNames, UserHandle user,
+ boolean replacing) { }
+ default void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing) { }
+ default void onPackagesSuspended(String[] packageNames, UserHandle user) { }
+ default void onPackagesUnsuspended(String[] packageNames, UserHandle user) { }
+ default void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
+ UserHandle user) { }
}
protected LauncherAppsCompat() {
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 23edcd2..7df9829 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -110,6 +110,10 @@
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
+ public static final TogglableFlag ENABLE_PREDICTION_DISMISS = new TogglableFlag(
+ "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
+
+
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2ef6d70..28c25cf 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -26,12 +26,12 @@
import android.annotation.SuppressLint;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Rect;
import android.text.InputType;
import android.text.Selection;
+import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
@@ -126,9 +126,6 @@
private static final Rect sTempRect = new Rect();
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
- private static String sDefaultFolderName;
- private static String sHintText;
-
private final Alarm mReorderAlarm = new Alarm();
private final Alarm mOnExitAlarm = new Alarm();
private final Alarm mOnScrollHintAlarm = new Alarm();
@@ -196,8 +193,6 @@
super(context, attrs);
setAlwaysDrawnWithCacheEnabled(false);
- setLocaleDependentFields(getResources(), false /* force */);
-
mLauncher = Launcher.getLauncher(context);
// We need this view to be focusable in touch mode so that when text editing of the folder
// name is complete, we have something to focus on, thus hiding the cursor and giving
@@ -318,7 +313,11 @@
mInfo.setTitle(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
- mFolderName.setHint(sDefaultFolderName.contentEquals(newTitle) ? sHintText : null);
+ if (TextUtils.isEmpty(mInfo.title)) {
+ mFolderName.setHint(R.string.folder_hint_text);
+ } else {
+ mFolderName.setHint(null);
+ }
sendCustomAccessibilityEvent(
this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -385,6 +384,7 @@
mInfo = info;
ArrayList<WorkspaceItemInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
+ updateItemLocationsInDatabaseBatch();
mContent.bindItems(children);
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
@@ -399,12 +399,12 @@
updateTextViewFocus();
mInfo.addListener(this);
- if (!sDefaultFolderName.contentEquals(mInfo.title)) {
+ if (TextUtils.isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
- mFolderName.setHint(sHintText);
+ mFolderName.setHint(R.string.folder_hint_text);
}
// In case any children didn't come across during loading, clean up the folder accordingly
@@ -822,9 +822,9 @@
}
}
+ @Override
public void onDropCompleted(final View target, final DragObject d,
final boolean success) {
-
if (success) {
if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
replaceFolderWithFinalItem();
@@ -834,9 +834,9 @@
WorkspaceItemInfo info = (WorkspaceItemInfo) d.dragInfo;
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
- ArrayList<View> views = getItemsInReadingOrder();
+ ArrayList<View> views = getIconsInReadingOrder();
views.add(info.rank, icon);
- mContent.arrangeChildren(views, views.size());
+ mContent.arrangeChildren(views);
mItemsInvalidated = true;
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
@@ -874,16 +874,21 @@
}
private void updateItemLocationsInDatabaseBatch() {
- ArrayList<View> list = getItemsInReadingOrder();
- ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
- for (int i = 0; i < list.size(); i++) {
- View v = list.get(i);
- ItemInfo info = (ItemInfo) v.getTag();
- info.rank = i;
- items.add(info);
+ FolderGridOrganizer verifier = new FolderGridOrganizer(
+ mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+
+ ArrayList<ItemInfo> items = new ArrayList<>();
+ int total = mInfo.contents.size();
+ for (int i = 0; i < total; i++) {
+ WorkspaceItemInfo itemInfo = mInfo.contents.get(i);
+ if (verifier.updateRankAndPos(itemInfo, i)) {
+ items.add(itemInfo);
+ }
}
- mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+ if (!items.isEmpty()) {
+ mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
+ }
}
public void notifyDrop() {
@@ -1021,17 +1026,7 @@
* Rearranges the children based on their rank.
*/
public void rearrangeChildren() {
- rearrangeChildren(-1);
- }
-
- /**
- * Rearranges the children based on their rank.
- * @param itemCount if greater than the total children count, empty spaces are left at the end,
- * otherwise it is ignored.
- */
- public void rearrangeChildren(int itemCount) {
- ArrayList<View> views = getItemsInReadingOrder();
- mContent.arrangeChildren(views, Math.max(itemCount, views.size()));
+ mContent.arrangeChildren(getIconsInReadingOrder());
mItemsInvalidated = true;
}
@@ -1124,6 +1119,7 @@
}
}
+ @Override
public void onDrop(DragObject d, DragOptions options) {
// If the icon was dropped while the page was being scrolled, we need to compute
// the target location again such that the icon is placed of the final page.
@@ -1171,12 +1167,6 @@
// before creating the view, so that WorkspaceItemInfo is updated appropriately.
mLauncher.getModelWriter().addOrMoveItemInDatabase(
si, mInfo.id, 0, si.cellX, si.cellY);
-
- // We only need to update the locations if it doesn't get handled in
- // #onDropCompleted.
- if (d.dragSource != this) {
- updateItemLocationsInDatabaseBatch();
- }
mIsExternalDrag = false;
} else {
currentDragView = mCurrentDragView;
@@ -1203,7 +1193,13 @@
// Temporarily suppress the listener, as we did all the work already here.
try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, false);
+ mInfo.add(si, mEmptyCellRank, false);
+ }
+
+ // We only need to update the locations if it doesn't get handled in
+ // #onDropCompleted.
+ if (d.dragSource != this) {
+ updateItemLocationsInDatabaseBatch();
}
}
@@ -1226,22 +1222,29 @@
// to correspond to the animation of the icon back into the folder. This is
public void hideItem(WorkspaceItemInfo info) {
View v = getViewForInfo(info);
- v.setVisibility(INVISIBLE);
+ if (v != null) {
+ v.setVisibility(INVISIBLE);
+ }
}
public void showItem(WorkspaceItemInfo info) {
View v = getViewForInfo(info);
- v.setVisibility(VISIBLE);
+ if (v != null) {
+ v.setVisibility(VISIBLE);
+ }
}
@Override
public void onAdd(WorkspaceItemInfo item, int rank) {
- View view = mContent.createAndAddViewForRank(item, rank);
+ FolderGridOrganizer verifier = new FolderGridOrganizer(
+ mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+ verifier.updateRankAndPos(item, rank);
mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
item.cellY);
+ updateItemLocationsInDatabaseBatch();
- ArrayList<View> items = new ArrayList<>(getItemsInReadingOrder());
- items.add(rank, view);
- mContent.arrangeChildren(items, items.size());
+ ArrayList<View> items = new ArrayList<>(getIconsInReadingOrder());
+ items.add(rank, mContent.createAndAddViewForRank(item, rank));
+ mContent.arrangeChildren(items);
mItemsInvalidated = true;
}
@@ -1264,13 +1267,7 @@
}
private View getViewForInfo(final WorkspaceItemInfo item) {
- return mContent.iterateOverItems(new ItemOperator() {
-
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- return info == item;
- }
- });
+ return mContent.iterateOverItems((info, view) -> info == item);
}
@Override
@@ -1286,7 +1283,10 @@
public void onTitleChanged(CharSequence title) {
}
- public ArrayList<View> getItemsInReadingOrder() {
+ /**
+ * Returns the sorted list of all the icons in the folder
+ */
+ public ArrayList<View> getIconsInReadingOrder() {
if (mItemsInvalidated) {
mItemsInReadingOrder.clear();
mContent.iterateOverItems(new ItemOperator() {
@@ -1303,7 +1303,7 @@
}
public List<BubbleTextView> getItemsOnPage(int page) {
- ArrayList<View> allItems = getItemsInReadingOrder();
+ ArrayList<View> allItems = getIconsInReadingOrder();
int lastPage = mContent.getPageCount() - 1;
int totalItemsInFolder = allItems.size();
int itemsPerPage = mContent.itemsPerPage();
@@ -1482,15 +1482,6 @@
return false;
}
- public static void setLocaleDependentFields(Resources res, boolean force) {
- if (sDefaultFolderName == null || force) {
- sDefaultFolderName = res.getString(R.string.folder_name);
- }
- if (sHintText == null || force) {
- sHintText = res.getString(R.string.folder_hint_text);
- }
- }
-
/**
* Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
* rounded rect.
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 962f215..9eb0693 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -79,7 +79,7 @@
private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-
+ private final FolderGridOrganizer mPreviewVerifier;
public FolderAnimationManager(Folder folder, boolean isOpening) {
mFolder = folder;
@@ -91,6 +91,7 @@
mContext = folder.getContext();
mLauncher = folder.mLauncher;
+ mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
mIsOpening = isOpening;
@@ -113,7 +114,7 @@
public AnimatorSet getAnimator() {
final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
- final List<BubbleTextView> itemsInPreview = mFolderIcon.getPreviewItems();
+ final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
// Match position of the FolderIcon
final Rect folderIconPos = new Rect();
@@ -226,15 +227,22 @@
}
/**
+ * Returns the list of "preview items" on {@param page}.
+ */
+ private List<BubbleTextView> getPreviewIconsOnPage(int page) {
+ return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
+ .previewItemsForPage(page, mFolder.getIconsInReadingOrder());
+ }
+
+ /**
* Animate the items on the current page.
*/
private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
int previewItemOffsetX, int previewItemOffsetY) {
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
boolean isOnFirstPage = mFolder.mContent.getCurrentPage() == 0;
- final List<BubbleTextView> itemsInPreview = isOnFirstPage
- ? mFolderIcon.getPreviewItems()
- : mFolderIcon.getPreviewItemsOnPage(mFolder.mContent.getCurrentPage());
+ final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(
+ isOnFirstPage ? 0 : mFolder.mContent.getCurrentPage());
final int numItemsInPreview = itemsInPreview.size();
final int numItemsInFirstPagePreview = isOnFirstPage
? numItemsInPreview : MAX_NUM_ITEMS_IN_PREVIEW;
diff --git a/src/com/android/launcher3/folder/FolderGridOrganizer.java b/src/com/android/launcher3/folder/FolderGridOrganizer.java
new file mode 100644
index 0000000..9d14a5f
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderGridOrganizer.java
@@ -0,0 +1,196 @@
+/*
+ * 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.folder;
+
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
+import android.graphics.Point;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility class for managing item positions in a folder based on rank
+ */
+public class FolderGridOrganizer {
+
+ private final Point mPoint = new Point();
+ private final int mMaxCountX;
+ private final int mMaxCountY;
+ private final int mMaxItemsPerPage;
+
+ private int mNumItemsInFolder;
+ private int mCountX;
+ private int mCountY;
+ private boolean mDisplayingUpperLeftQuadrant = false;
+
+ /**
+ * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
+ */
+ public FolderGridOrganizer(InvariantDeviceProfile profile) {
+ mMaxCountX = profile.numFolderColumns;
+ mMaxCountY = profile.numFolderRows;
+ mMaxItemsPerPage = mMaxCountX * mMaxCountY;
+ }
+
+ /**
+ * Updates the organizer with the provided folder info
+ */
+ public FolderGridOrganizer setFolderInfo(FolderInfo info) {
+ return setContentSize(info.contents.size());
+ }
+
+ /**
+ * Updates the organizer to reflect the content size
+ */
+ public FolderGridOrganizer setContentSize(int contentSize) {
+ if (contentSize != mNumItemsInFolder) {
+ calculateGridSize(contentSize);
+
+ mDisplayingUpperLeftQuadrant = contentSize > MAX_NUM_ITEMS_IN_PREVIEW;
+ mNumItemsInFolder = contentSize;
+ }
+ return this;
+ }
+
+ public int getCountX() {
+ return mCountX;
+ }
+
+ public int getCountY() {
+ return mCountY;
+ }
+
+ public int getMaxItemsPerPage() {
+ return mMaxItemsPerPage;
+ }
+
+ /**
+ * Calculates the grid size such that {@param count} items can fit in the grid.
+ * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
+ * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
+ */
+ private void calculateGridSize(int count) {
+ boolean done;
+ int gridCountX = mCountX;
+ int gridCountY = mCountY;
+
+ if (count >= mMaxItemsPerPage) {
+ gridCountX = mMaxCountX;
+ gridCountY = mMaxCountY;
+ done = true;
+ } else {
+ done = false;
+ }
+
+ while (!done) {
+ int oldCountX = gridCountX;
+ int oldCountY = gridCountY;
+ if (gridCountX * gridCountY < count) {
+ // Current grid is too small, expand it
+ if ((gridCountX <= gridCountY || gridCountY == mMaxCountY)
+ && gridCountX < mMaxCountX) {
+ gridCountX++;
+ } else if (gridCountY < mMaxCountY) {
+ gridCountY++;
+ }
+ if (gridCountY == 0) gridCountY++;
+ } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
+ gridCountY = Math.max(0, gridCountY - 1);
+ } else if ((gridCountX - 1) * gridCountY >= count) {
+ gridCountX = Math.max(0, gridCountX - 1);
+ }
+ done = gridCountX == oldCountX && gridCountY == oldCountY;
+ }
+
+ mCountX = gridCountX;
+ mCountY = gridCountY;
+ }
+
+ /**
+ * Updates the item's cellX, cellY and rank corresponding to the provided rank.
+ * @return true if there was any change
+ */
+ public boolean updateRankAndPos(ItemInfo item, int rank) {
+ Point pos = getPosForRank(rank);
+ if (!pos.equals(item.cellX, item.cellY) || rank != item.rank) {
+ item.rank = rank;
+ item.cellX = pos.x;
+ item.cellY = pos.y;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the position of the item in the grid
+ */
+ public Point getPosForRank(int rank) {
+ int pagePos = rank % mMaxItemsPerPage;
+ mPoint.x = pagePos % mCountX;
+ mPoint.y = pagePos / mCountX;
+ return mPoint;
+ }
+
+ /**
+ * Returns the preview items for the provided pageNo using the full list of contents
+ */
+ public <T, R extends T> ArrayList<R> previewItemsForPage(int page, List<T> contents) {
+ ArrayList<R> result = new ArrayList<>();
+ int itemsPerPage = mCountX * mCountY;
+ int start = itemsPerPage * page;
+ int end = Math.min(start + itemsPerPage, contents.size());
+
+ for (int i = start, rank = 0; i < end; i++, rank++) {
+ if (isItemInPreview(page, rank)) {
+ result.add((R) contents.get(i));
+ }
+
+ if (result.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns whether the item with rank is in the default Folder icon preview.
+ */
+ public boolean isItemInPreview(int rank) {
+ return isItemInPreview(0, rank);
+ }
+
+ /**
+ * @param page The page the item is on.
+ * @param rank The rank of the item.
+ * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
+ */
+ public boolean isItemInPreview(int page, int rank) {
+ // First page items are laid out such that the first 4 items are always in the upper
+ // left quadrant. For all other pages, we need to check the row and col.
+ if (page > 0 || mDisplayingUpperLeftQuadrant) {
+ int col = rank % mCountX;
+ int row = rank / mCountX;
+ return col < 2 && row < 2;
+ }
+ return rank < MAX_NUM_ITEMS_IN_PREVIEW;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 250169c..686684d 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -95,7 +95,7 @@
PreviewBackground mBackground = new PreviewBackground();
private boolean mBackgroundIsVisible = true;
- FolderIconPreviewVerifier mPreviewVerifier;
+ FolderGridOrganizer mPreviewVerifier;
ClippedFolderIconLayoutRule mPreviewLayoutRule;
private PreviewItemManager mPreviewItemManager;
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
@@ -212,7 +212,7 @@
private void setFolder(Folder folder) {
mFolder = folder;
- mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+ mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
mPreviewVerifier.setFolderInfo(mFolder.getInfo());
updatePreviewItems(false);
}
@@ -230,11 +230,7 @@
}
public void addItem(WorkspaceItemInfo item) {
- addItem(item, true);
- }
-
- public void addItem(WorkspaceItemInfo item, boolean animate) {
- mInfo.add(item, animate);
+ mInfo.add(item, true);
}
public void removeItem(WorkspaceItemInfo item, boolean animate) {
@@ -294,8 +290,7 @@
}
private void onDrop(final WorkspaceItemInfo item, DragView animateView, Rect finalRect,
- float scaleRelativeToDragLayer, int index,
- boolean itemReturnedOnFailedDrop) {
+ float scaleRelativeToDragLayer, int index, boolean itemReturnedOnFailedDrop) {
item.cellX = -1;
item.cellY = -1;
@@ -327,9 +322,9 @@
boolean itemAdded = false;
if (itemReturnedOnFailedDrop || index >= MAX_NUM_ITEMS_IN_PREVIEW) {
List<BubbleTextView> oldPreviewItems = new ArrayList<>(mCurrentPreviewItems);
- addItem(item, false);
+ mInfo.add(item, index, false);
mCurrentPreviewItems.clear();
- mCurrentPreviewItems.addAll(getPreviewItems());
+ mCurrentPreviewItems.addAll(getPreviewIconsOnPage(0));
if (!oldPreviewItems.equals(mCurrentPreviewItems)) {
for (int i = 0; i < mCurrentPreviewItems.size(); ++i) {
@@ -349,13 +344,13 @@
}
if (!itemAdded) {
- addItem(item);
+ mInfo.add(item, index, true);
}
int[] center = new int[2];
float scale = getLocalCenterForIndex(index, numItemsInPreview, center);
- center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
- center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
+ center[0] = Math.round(scaleRelativeToDragLayer * center[0]);
+ center[1] = Math.round(scaleRelativeToDragLayer * center[1]);
to.offset(center[0] - animateView.getMeasuredWidth() / 2,
center[1] - animateView.getMeasuredHeight() / 2);
@@ -396,7 +391,8 @@
item = (WorkspaceItemInfo) d.dragInfo;
}
mFolder.notifyDrop();
- onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(),
+ onDrop(item, d.dragView, null, 1.0f,
+ itemReturnedOnFailedDrop ? item.rank : mInfo.contents.size(),
itemReturnedOnFailedDrop);
}
@@ -493,8 +489,7 @@
mBackground.drawBackground(canvas);
}
- if (mFolder == null) return;
- if (mFolder.getItemCount() == 0 && !mAnimating) return;
+ if (mCurrentPreviewItems.isEmpty() && !mAnimating) return;
final int saveCount = canvas.save();
canvas.clipPath(mBackground.getClipPath());
@@ -536,31 +531,11 @@
}
/**
- * Returns the list of preview items displayed in the icon.
- */
- public List<BubbleTextView> getPreviewItems() {
- return getPreviewItemsOnPage(0);
- }
-
- /**
* Returns the list of "preview items" on {@param page}.
*/
- public List<BubbleTextView> getPreviewItemsOnPage(int page) {
- mPreviewVerifier.setFolderInfo(mFolder.getInfo());
-
- List<BubbleTextView> itemsToDisplay = new ArrayList<>();
- List<BubbleTextView> itemsOnPage = mFolder.getItemsOnPage(page);
- int numItems = itemsOnPage.size();
- for (int rank = 0; rank < numItems; ++rank) {
- if (mPreviewVerifier.isItemInPreview(page, rank)) {
- itemsToDisplay.add(itemsOnPage.get(rank));
- }
-
- if (itemsToDisplay.size() == MAX_NUM_ITEMS_IN_PREVIEW) {
- break;
- }
- }
- return itemsToDisplay;
+ public List<BubbleTextView> getPreviewIconsOnPage(int page) {
+ return mPreviewVerifier.setFolderInfo(mFolder.mInfo)
+ .previewItemsForPage(page, mFolder.getIconsInReadingOrder());
}
@Override
@@ -578,7 +553,7 @@
private void updatePreviewItems(boolean animate) {
mPreviewItemManager.updatePreviewItems(animate);
mCurrentPreviewItems.clear();
- mCurrentPreviewItems.addAll(getPreviewItems());
+ mCurrentPreviewItems.addAll(getPreviewIconsOnPage(0));
}
@Override
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
deleted file mode 100644
index 4c84e35..0000000
--- a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2017 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.folder;
-
-import android.util.Log;
-
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InvariantDeviceProfile;
-
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-
-/**
- * Verifies whether an item in a Folder is displayed in the FolderIcon preview.
- */
-public class FolderIconPreviewVerifier {
-
- private static final String TAG = "FolderPreviewVerifier";
-
- private final int mMaxGridCountX;
- private final int mMaxGridCountY;
- private final int mMaxItemsPerPage;
- private final int[] mGridSize = new int[] { 1, 1 };
-
- private int mNumItemsInFolder;
- private int mGridCountX;
- private boolean mDisplayingUpperLeftQuadrant = false;
-
- /**
- * Note: must call {@link #setFolderInfo(FolderInfo)} manually for verifier to work.
- */
- public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
- mMaxGridCountX = profile.numFolderColumns;
- mMaxGridCountY = profile.numFolderRows;
- mMaxItemsPerPage = mMaxGridCountX * mMaxGridCountY;
- }
-
- public void setFolderInfo(FolderInfo info) {
- int numItemsInFolder = info.contents.size();
- if (numItemsInFolder != mNumItemsInFolder) {
- FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, mMaxGridCountX,
- mMaxGridCountY, mMaxItemsPerPage, mGridSize);
- mGridCountX = mGridSize[0];
-
- mDisplayingUpperLeftQuadrant = numItemsInFolder > MAX_NUM_ITEMS_IN_PREVIEW;
- mNumItemsInFolder = numItemsInFolder;
- }
- }
-
- /**
- * Returns whether the item with {@param rank} is in the default Folder icon preview.
- */
- public boolean isItemInPreview(int rank) {
- return isItemInPreview(0, rank);
- }
-
- /**
- * @param page The page the item is on.
- * @param rank The rank of the item.
- * @return True iff the icon is in the 2x2 upper left quadrant of the Folder.
- */
- public boolean isItemInPreview(int page, int rank) {
- if (mGridSize[0] == 1) {
- Log.w(TAG, "setFolderInfo not called before checking if item is in preview.");
- }
-
- // First page items are laid out such that the first 4 items are always in the upper
- // left quadrant. For all other pages, we need to check the row and col.
- if (page > 0 || mDisplayingUpperLeftQuadrant) {
- int col = rank % mGridCountX;
- int row = rank / mGridCountX;
- return col < 2 && row < 2;
- }
- return rank < MAX_NUM_ITEMS_IN_PREVIEW;
- }
-}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 57105e7..3e00cae 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -38,9 +38,9 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.pageindicators.PageIndicatorDots;
@@ -50,6 +50,7 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
+import java.util.function.ToIntFunction;
public class FolderPagedView extends PagedView<PageIndicatorDots> {
@@ -73,12 +74,7 @@
@Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>();
- @ViewDebug.ExportedProperty(category = "launcher")
- private final int mMaxCountX;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final int mMaxCountY;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final int mMaxItemsPerPage;
+ private final FolderGridOrganizer mOrganizer;
private int mAllocatedContentSize;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -91,10 +87,7 @@
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
- mMaxCountX = profile.numFolderColumns;
- mMaxCountY = profile.numFolderRows;
-
- mMaxItemsPerPage = mMaxCountX * mMaxCountY;
+ mOrganizer = new FolderGridOrganizer(profile);
mInflater = LayoutInflater.from(context);
@@ -111,57 +104,13 @@
}
/**
- * Calculates the grid size such that {@param count} items can fit in the grid.
- * The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
- * maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
- */
- public static void calculateGridSize(int count, int countX, int countY, int maxCountX,
- int maxCountY, int maxItemsPerPage, int[] out) {
- boolean done;
- int gridCountX = countX;
- int gridCountY = countY;
-
- if (count >= maxItemsPerPage) {
- gridCountX = maxCountX;
- gridCountY = maxCountY;
- done = true;
- } else {
- done = false;
- }
-
- while (!done) {
- int oldCountX = gridCountX;
- int oldCountY = gridCountY;
- if (gridCountX * gridCountY < count) {
- // Current grid is too small, expand it
- if ((gridCountX <= gridCountY || gridCountY == maxCountY)
- && gridCountX < maxCountX) {
- gridCountX++;
- } else if (gridCountY < maxCountY) {
- gridCountY++;
- }
- if (gridCountY == 0) gridCountY++;
- } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
- gridCountY = Math.max(0, gridCountY - 1);
- } else if ((gridCountX - 1) * gridCountY >= count) {
- gridCountX = Math.max(0, gridCountX - 1);
- }
- done = gridCountX == oldCountX && gridCountY == oldCountY;
- }
-
- out[0] = gridCountX;
- out[1] = gridCountY;
- }
-
- /**
* Sets up the grid size such that {@param count} items can fit in the grid.
*/
- public void setupContentDimensions(int count) {
+ private void setupContentDimensions(int count) {
mAllocatedContentSize = count;
- calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage,
- sTmpArray);
- mGridCountX = sTmpArray[0];
- mGridCountY = sTmpArray[1];
+ mOrganizer.setContentSize(count);
+ mGridCountX = mOrganizer.getCountX();
+ mGridCountY = mOrganizer.getCountY();
// Update grid size
for (int i = getPageCount() - 1; i >= 0; i--) {
@@ -183,13 +132,13 @@
for (WorkspaceItemInfo item : items) {
icons.add(createNewView(item));
}
- arrangeChildren(icons, icons.size(), false);
+ arrangeChildren(icons);
}
public void allocateSpaceForRank(int rank) {
- ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
+ ArrayList<View> views = new ArrayList<>(mFolder.getIconsInReadingOrder());
views.add(rank, null);
- arrangeChildren(views, views.size(), false);
+ arrangeChildren(views);
}
/**
@@ -199,7 +148,7 @@
public int allocateRankForNewItem() {
int rank = getItemCount();
allocateSpaceForRank(rank);
- setCurrentPage(rank / mMaxItemsPerPage);
+ setCurrentPage(rank / mOrganizer.getMaxItemsPerPage());
return rank;
}
@@ -215,16 +164,10 @@
* related attributes. It assumes that {@param item} is already attached to the view.
*/
public void addViewForRank(View view, WorkspaceItemInfo item, int rank) {
- int pagePos = rank % mMaxItemsPerPage;
- int pageNo = rank / mMaxItemsPerPage;
-
- item.rank = rank;
- item.cellX = pagePos % mGridCountX;
- item.cellY = pagePos / mGridCountX;
+ int pageNo = rank / mOrganizer.getMaxItemsPerPage();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
- lp.cellX = item.cellX;
- lp.cellY = item.cellY;
+ lp.setXY(mOrganizer.getPosForRank(rank));
getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
}
@@ -295,16 +238,10 @@
* page.
*
* @param list the ordered list of children.
- * @param itemCount if greater than the total children count, empty spaces are left
- * at the end, otherwise it is ignored.
- *
*/
- public void arrangeChildren(ArrayList<View> list, int itemCount) {
- arrangeChildren(list, itemCount, true);
- }
-
@SuppressLint("RtlHardcoded")
- private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
+ public void arrangeChildren(ArrayList<View> list) {
+ int itemCount = list.size();
ArrayList<CellLayout> pages = new ArrayList<>();
for (int i = 0; i < getChildCount(); i++) {
CellLayout page = (CellLayout) getChildAt(i);
@@ -317,15 +254,12 @@
CellLayout currentPage = null;
int position = 0;
- int newX, newY, rank;
+ int rank = 0;
- FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
- Launcher.getLauncher(getContext()).getDeviceProfile().inv);
- verifier.setFolderInfo(mFolder.getInfo());
- rank = 0;
+ mOrganizer.setFolderInfo(mFolder.getInfo());
for (int i = 0; i < itemCount; i++) {
View v = list.size() > i ? list.get(i) : null;
- if (currentPage == null || position >= mMaxItemsPerPage) {
+ if (currentPage == null || position >= mOrganizer.getMaxItemsPerPage()) {
// Next page
if (pageItr.hasNext()) {
currentPage = pageItr.next();
@@ -337,28 +271,16 @@
if (v != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
- newX = position % mGridCountX;
- newY = position / mGridCountX;
ItemInfo info = (ItemInfo) v.getTag();
- if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
- info.cellX = newX;
- info.cellY = newY;
- info.rank = rank;
- if (saveChanges) {
- mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info,
- mFolder.mInfo.id, 0, info.cellX, info.cellY);
- }
- }
- lp.cellX = info.cellX;
- lp.cellY = info.cellY;
+ lp.setXY(mOrganizer.getPosForRank(rank));
currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
- if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
+ if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) {
((BubbleTextView) v).verifyHighRes();
}
}
- rank ++;
+ rank++;
position++;
}
@@ -398,7 +320,7 @@
return 0;
}
return getPageAt(lastPageIndex).getShortcutsAndWidgets().getChildCount()
- + lastPageIndex * mMaxItemsPerPage;
+ + lastPageIndex * mOrganizer.getMaxItemsPerPage();
}
/**
@@ -412,31 +334,28 @@
sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
}
return Math.min(mAllocatedContentSize - 1,
- pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
+ pageIndex * mOrganizer.getMaxItemsPerPage()
+ + sTmpArray[1] * mGridCountX + sTmpArray[0]);
}
public View getFirstItem() {
- if (getChildCount() < 1) {
- return null;
- }
- ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
- if (mGridCountX > 0) {
- return currContainer.getChildAt(0, 0);
- } else {
- return currContainer.getChildAt(0);
- }
+ return getViewInCurrentPage(c -> 0);
}
public View getLastItem() {
+ return getViewInCurrentPage(c -> c.getChildCount() - 1);
+ }
+
+ private View getViewInCurrentPage(ToIntFunction<ShortcutAndWidgetContainer> rankProvider) {
if (getChildCount() < 1) {
return null;
}
- ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
- int lastRank = currContainer.getChildCount() - 1;
+ ShortcutAndWidgetContainer container = getCurrentCellLayout().getShortcutsAndWidgets();
+ int rank = rankProvider.applyAsInt(container);
if (mGridCountX > 0) {
- return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+ return container.getChildAt(rank % mGridCountX, rank / mGridCountX);
} else {
- return currContainer.getChildAt(lastRank);
+ return container.getChildAt(rank);
}
}
@@ -517,7 +436,7 @@
}
public boolean rankOnCurrentPage(int rank) {
- int p = rank / mMaxItemsPerPage;
+ int p = rank / mOrganizer.getMaxItemsPerPage();
return p == getNextPage();
}
@@ -563,15 +482,16 @@
// Animation only happens on the current page.
int pageToAnimate = getNextPage();
+ int maxItemsPerPage = mOrganizer.getMaxItemsPerPage();
- int pageT = target / mMaxItemsPerPage;
- int pagePosT = target % mMaxItemsPerPage;
+ int pageT = target / maxItemsPerPage;
+ int pagePosT = target % maxItemsPerPage;
if (pageT != pageToAnimate) {
Log.e(TAG, "Cannot animate when the target cell is invisible");
}
- int pagePosE = empty % mMaxItemsPerPage;
- int pageE = empty / mMaxItemsPerPage;
+ int pagePosE = empty % maxItemsPerPage;
+ int pageE = empty / maxItemsPerPage;
int startPos, endPos;
int moveStart, moveEnd;
@@ -588,7 +508,7 @@
if (pageE < pageToAnimate) {
moveStart = empty;
// Instantly move the first item in the current page.
- moveEnd = pageToAnimate * mMaxItemsPerPage;
+ moveEnd = pageToAnimate * maxItemsPerPage;
// Animate the 2nd item in the current page, as the first item was already moved to
// the last page.
startPos = 0;
@@ -606,10 +526,10 @@
// Move the items immediately.
moveStart = empty;
// Instantly move the last item in the current page.
- moveEnd = (pageToAnimate + 1) * mMaxItemsPerPage - 1;
+ moveEnd = (pageToAnimate + 1) * maxItemsPerPage - 1;
// Animations start with the second last item in the page
- startPos = mMaxItemsPerPage - 1;
+ startPos = maxItemsPerPage - 1;
} else {
moveStart = moveEnd = -1;
startPos = pagePosE;
@@ -621,8 +541,8 @@
// Instant moving views.
while (moveStart != moveEnd) {
int rankToMove = moveStart + direction;
- int p = rankToMove / mMaxItemsPerPage;
- int pagePos = rankToMove % mMaxItemsPerPage;
+ int p = rankToMove / maxItemsPerPage;
+ int pagePos = rankToMove % maxItemsPerPage;
int x = pagePos % mGridCountX;
int y = pagePos / mGridCountX;
@@ -667,9 +587,6 @@
for (int i = startPos; i != endPos; i += direction) {
int nextPos = i + direction;
View v = page.getChildAt(nextPos % mGridCountX, nextPos / mGridCountX);
- if (v != null) {
- ((ItemInfo) v.getTag()).rank -= direction;
- }
if (page.animateChildToPosition(v, i % mGridCountX, i / mGridCountX,
REORDER_ANIMATION_DURATION, delay, true, true)) {
delay += delayAmount;
@@ -679,6 +596,6 @@
}
public int itemsPerPage() {
- return mMaxItemsPerPage;
+ return mOrganizer.getMaxItemsPerPage();
}
}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 49763ba..2ac6bf4 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -30,15 +30,15 @@
import android.view.View;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.NonNull;
-
/**
* Manages the drawing and animations of {@link PreviewItemDrawingParams} for a {@link FolderIcon}.
*/
@@ -200,7 +200,7 @@
}
void buildParamsForPage(int page, ArrayList<PreviewItemDrawingParams> params, boolean animate) {
- List<BubbleTextView> items = mIcon.getPreviewItemsOnPage(page);
+ List<BubbleTextView> items = mIcon.getPreviewIconsOnPage(page);
int prevNumItems = params.size();
// We adjust the size of the list to match the number of items in the preview.
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index f7f8ef1..cc92076 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -8,6 +8,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
import java.io.File;
@@ -131,7 +132,7 @@
private PrintWriter mCurrentWriter = null;
private void closeWriter() {
- Utilities.closeSilently(mCurrentWriter);
+ IOUtils.closeSilently(mCurrentWriter);
mCurrentWriter = null;
}
@@ -219,7 +220,7 @@
} catch (Exception e) {
// ignore
} finally {
- Utilities.closeSilently(in);
+ IOUtils.closeSilently(in);
}
}
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index ed0d470..935326d 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -20,7 +20,6 @@
import android.util.LongSparseArray;
import android.util.Pair;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InvariantDeviceProfile;
@@ -28,12 +27,12 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.List;
@@ -162,7 +161,7 @@
intentWithoutPkg = intent.toUri(0);
}
- boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
+ boolean isLauncherAppTarget = PackageManagerHelper.isLauncherAppTarget(intent);
synchronized (dataModel) {
for (ItemInfo item : dataModel.itemsIdMap) {
if (item instanceof WorkspaceItemInfo) {
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
similarity index 69%
rename from src/com/android/launcher3/AllAppsList.java
rename to src/com/android/launcher3/model/AllAppsList.java
index 733f295..370a812 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -14,25 +14,37 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.model;
+
+import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
+import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.os.LocaleList;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.SafeCloseable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.function.Consumer;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -42,29 +54,40 @@
* Stores the list of all applications for the all apps view.
*/
public class AllAppsList {
+
private static final String TAG = "AllAppsList";
+ private static final Consumer<AppInfo> NO_OP_CONSUMER = a -> { };
+
public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
/** The list off all apps. */
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
- /** The list of apps that have been added since the last notify() call. */
- public ArrayList<AppInfo> added = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
- /** The list of apps that have been removed since the last notify() call. */
- public ArrayList<AppInfo> removed = new ArrayList<>();
- /** The list of apps that have been modified since the last notify() call. */
- public ArrayList<AppInfo> modified = new ArrayList<>();
private IconCache mIconCache;
-
private AppFilter mAppFilter;
+ private boolean mDataChanged = false;
+ private Consumer<AppInfo> mRemoveListener = NO_OP_CONSUMER;
+
+ private AlphabeticIndexCompat mIndex;
+
/**
* Boring constructor.
*/
public AllAppsList(IconCache iconCache, AppFilter appFilter) {
mIconCache = iconCache;
mAppFilter = appFilter;
+ mIndex = new AlphabeticIndexCompat(LocaleList.getDefault());
+ }
+
+ /**
+ * Returns true if there have been any changes since last call.
+ */
+ public boolean getAndResetChangeFlag() {
+ boolean result = mDataChanged;
+ mDataChanged = false;
+ return result;
}
/**
@@ -81,9 +104,10 @@
return;
}
mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
+ info.sectionName = mIndex.computeSectionName(info.title);
data.add(info);
- added.add(info);
+ mDataChanged = true;
}
public void addPromiseApp(Context context,
@@ -94,31 +118,46 @@
if (applicationInfo == null) {
PromiseAppInfo info = new PromiseAppInfo(installInfo);
mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
+ info.sectionName = mIndex.computeSectionName(info.title);
+
data.add(info);
- added.add(info);
+ mDataChanged = true;
}
}
- public void removePromiseApp(AppInfo appInfo) {
- // the <em>removed</em> list is handled by the caller
- // so not adding it here
- data.remove(appInfo);
+ public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+ UserHandle user = Process.myUserHandle();
+ for (int i=0; i < data.size(); i++) {
+ final AppInfo appInfo = data.get(i);
+ final ComponentName tgtComp = appInfo.getTargetComponent();
+ if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
+ && appInfo.user.equals(user)
+ && appInfo instanceof PromiseAppInfo) {
+ final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
+ if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
+ promiseAppInfo.level = installInfo.progress;
+ return promiseAppInfo;
+ } else if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+ removeApp(i);
+ }
+ }
+ }
+ return null;
+ }
+
+ private void removeApp(int index) {
+ AppInfo removed = data.remove(index);
+ if (removed != null) {
+ mDataChanged = true;
+ mRemoveListener.accept(removed);
+ }
}
public void clear() {
data.clear();
- // TODO: do we clear these too?
- added.clear();
- removed.clear();
- modified.clear();
- }
-
- public int size() {
- return data.size();
- }
-
- public AppInfo get(int index) {
- return data.get(index);
+ mDataChanged = false;
+ // Reset the index as locales might have changed
+ mIndex = new AlphabeticIndexCompat(LocaleList.getDefault());
}
/**
@@ -142,8 +181,7 @@
for (int i = data.size() - 1; i >= 0; i--) {
AppInfo info = data.get(i);
if (info.user.equals(user) && packageName.equals(info.componentName.getPackageName())) {
- removed.add(info);
- data.remove(i);
+ removeApp(i);
}
}
}
@@ -157,17 +195,17 @@
AppInfo info = data.get(i);
if (matcher.matches(info, info.componentName)) {
info.runtimeStatusFlags = op.apply(info.runtimeStatusFlags);
- modified.add(info);
+ mDataChanged = true;
}
}
}
- public void updateIconsAndLabels(HashSet<String> packages, UserHandle user,
- ArrayList<AppInfo> outUpdates) {
+ public void updateIconsAndLabels(HashSet<String> packages, UserHandle user) {
for (AppInfo info : data) {
if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) {
mIconCache.updateTitleAndIcon(info);
- outUpdates.add(info);
+ info.sectionName = mIndex.computeSectionName(info.title);
+ mDataChanged = true;
}
}
}
@@ -188,8 +226,7 @@
&& packageName.equals(applicationInfo.componentName.getPackageName())) {
if (!findActivity(matches, applicationInfo.componentName)) {
Log.w(TAG, "Changing shortcut target due to app component name change.");
- removed.add(applicationInfo);
- data.remove(i);
+ removeApp(i);
}
}
}
@@ -202,7 +239,9 @@
add(new AppInfo(context, info, user), info);
} else {
mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
- modified.add(applicationInfo);
+ applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
+
+ mDataChanged = true;
}
}
} else {
@@ -211,15 +250,13 @@
final AppInfo applicationInfo = data.get(i);
if (user.equals(applicationInfo.user)
&& packageName.equals(applicationInfo.componentName.getPackageName())) {
- removed.add(applicationInfo);
mIconCache.remove(applicationInfo.componentName, user);
- data.remove(i);
+ removeApp(i);
}
}
}
}
-
/**
* Returns whether <em>apps</em> contains <em>component</em>.
*/
@@ -247,4 +284,16 @@
}
return null;
}
+
+ public AppInfo[] copyData() {
+ AppInfo[] result = data.toArray(EMPTY_ARRAY);
+ Arrays.sort(result, COMPONENT_KEY_COMPARATOR);
+ return result;
+ }
+
+ public SafeCloseable trackRemoves(Consumer<AppInfo> removeListener) {
+ mRemoveListener = removeListener;
+
+ return () -> mRemoveListener = NO_OP_CONSUMER;
+ }
}
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
index 29a46cf..13ab033 100644
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -51,5 +51,8 @@
public void onStartApp(ComponentName componentName, UserHandle user,
@Nullable String container) { }
+ public void onDismissApp(ComponentName componentName, UserHandle user,
+ @Nullable String container){}
+
public void onReturnedToHome() { }
}
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 97cf267..018f93a 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -19,14 +19,13 @@
import android.os.Looper;
import android.util.Log;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.PagedView;
@@ -279,9 +278,8 @@
public void bindAllApps() {
// shallow copy
- @SuppressWarnings("unchecked")
- ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
- executeCallbacksTask(c -> c.bindAllApplications(list), mUiExecutor);
+ AppInfo[] apps = mBgAllAppsList.copyData();
+ executeCallbacksTask(c -> c.bindAllApplications(apps), mUiExecutor);
}
public abstract void bindWidgets();
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index eea3d8c..e12633b 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -17,12 +17,12 @@
import android.util.Log;
-import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -30,6 +30,7 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -95,12 +96,7 @@
public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
if (!updatedShortcuts.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindWorkspaceItemsChanged(updatedShortcuts);
- }
- });
+ scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts));
}
}
@@ -113,23 +109,20 @@
public void bindUpdatedWidgets(BgDataModel dataModel) {
final ArrayList<WidgetListRowEntry> widgets =
dataModel.widgetsModel.getWidgetsList(mApp.getContext());
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAllWidgets(widgets);
- }
- });
+ scheduleCallbackTask(c -> c.bindAllWidgets(widgets));
}
public void deleteAndBindComponentsRemoved(final ItemInfoMatcher matcher) {
getModelWriter().deleteItemsFromDatabase(matcher);
// Call the components-removed callback
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindWorkspaceComponentsRemoved(matcher);
- }
- });
+ scheduleCallbackTask(c -> c.bindWorkspaceComponentsRemoved(matcher));
+ }
+
+ public void bindApplicationsIfNeeded() {
+ if (mAllAppsList.getAndResetChangeFlag()) {
+ AppInfo[] apps = mAllAppsList.copyData();
+ scheduleCallbackTask(c -> c.bindAllApplications(apps));
+ }
}
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8f0cd08..0e20270 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -22,11 +22,13 @@
import android.util.Log;
import android.util.MutableInt;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
@@ -40,6 +42,10 @@
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.WidgetListRowEntry;
+
import com.google.protobuf.nano.MessageNano;
import java.io.FileDescriptor;
@@ -49,6 +55,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -391,4 +398,30 @@
}
}
}
+
+ public interface Callbacks {
+ void rebindModel();
+
+ int getCurrentWorkspaceScreen();
+ void clearPendingBinds();
+ void startBinding();
+ void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons);
+ void bindScreens(IntArray orderedScreenIds);
+ void finishFirstPageBind(ViewOnDrawExecutor executor);
+ void finishBindingItems(int pageBoundFirst);
+ void preAddApps();
+ void bindAppsAdded(IntArray newScreens,
+ ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
+ void bindPromiseAppProgressUpdated(PromiseAppInfo app);
+ void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
+ void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
+ void bindRestoreItemsChange(HashSet<ItemInfo> updates);
+ void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
+ void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
+ void onPageBoundSynchronously(int page);
+ void executeOnNextDraw(ViewOnDrawExecutor executor);
+ void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
+
+ void bindAllApplications(AppInfo[] apps);
+ }
}
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 7852444..c1c8be3 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -18,14 +18,10 @@
import android.content.ComponentName;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import java.util.ArrayList;
@@ -53,9 +49,9 @@
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IconCache iconCache = app.getIconCache();
- final ArrayList<AppInfo> updatedApps = new ArrayList<>();
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
+
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
@@ -69,18 +65,10 @@
}
}
}
- apps.updateIconsAndLabels(mPackages, mUser, updatedApps);
+ apps.updateIconsAndLabels(mPackages, mUser);
}
bindUpdatedWorkspaceItems(updatedShortcuts);
-
- if (!updatedApps.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppsAddedOrUpdated(updatedApps);
- }
- });
- }
+ bindApplicationsIfNeeded();
}
public boolean isValidShortcut(WorkspaceItemInfo si) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4b01b5e..54249dc 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
+import static com.android.launcher3.util.PackageManagerHelper.isSystemApp;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
@@ -39,7 +40,6 @@
import android.util.LongSparseArray;
import android.util.MutableInt;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
@@ -57,7 +57,7 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.icons.ComponentWithLabel;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.IconCache;
@@ -69,6 +69,7 @@
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutKey;
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;
@@ -317,9 +318,9 @@
// We can only query for shortcuts when the user is unlocked.
if (userUnlocked) {
- List<ShortcutInfo> pinnedShortcuts =
+ DeepShortcutManager.QueryResult pinnedShortcuts =
mShortcutManager.queryForPinnedShortcuts(null, user);
- if (mShortcutManager.wasLastCallSuccess()) {
+ if (pinnedShortcuts.wasSuccess()) {
for (ShortcutInfo shortcut : pinnedShortcuts) {
shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
shortcut);
@@ -531,7 +532,7 @@
info.spanX = 1;
info.spanY = 1;
info.runtimeStatusFlags |= disabledState;
- if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
+ if (isSafeMode && !isSystemApp(context, intent)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
}
@@ -703,7 +704,7 @@
}
}
} finally {
- Utilities.closeSilently(c);
+ IOUtils.closeSilently(c);
}
// Break early if we've stopped loading
@@ -743,8 +744,8 @@
}
// Sort the folder items, update ranks, and make sure all preview items are high res.
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ FolderGridOrganizer verifier =
+ new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
for (FolderInfo folder : mBgDataModel.folders) {
Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
verifier.setFolderInfo(folder);
@@ -829,7 +830,7 @@
}
}
- mBgAllAppsList.added = new ArrayList<>();
+ mBgAllAppsList.getAndResetChangeFlag();
return allActivityList;
}
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index b353810..2bd6cd4 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.util.Log;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 4ce2f4b..d7ab0dd 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -31,7 +31,7 @@
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5f6d128..e43412d 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -20,20 +20,18 @@
import android.content.pm.PackageManager;
import android.os.Process;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.util.InstantAppResolver;
-import java.util.ArrayList;
import java.util.HashSet;
/**
@@ -66,41 +64,11 @@
}
synchronized (apps) {
- PromiseAppInfo updated = null;
- final ArrayList<AppInfo> removed = new ArrayList<>();
- for (int i=0; i < apps.size(); i++) {
- final AppInfo appInfo = apps.get(i);
- final ComponentName tgtComp = appInfo.getTargetComponent();
- if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) {
- if (appInfo instanceof PromiseAppInfo) {
- final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
- if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
- promiseAppInfo.level = mInstallInfo.progress;
- updated = promiseAppInfo;
- } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
- apps.removePromiseApp(appInfo);
- removed.add(appInfo);
- }
- }
- }
- }
+ PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
if (updated != null) {
- final PromiseAppInfo updatedPromiseApp = updated;
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp);
- }
- });
+ scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
}
- if (!removed.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppInfosRemoved(removed);
- }
- });
- }
+ bindApplicationsIfNeeded();
}
synchronized (dataModel) {
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index baeaa94..741be66 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -32,8 +32,17 @@
this.packageName = packageName;
}
+ public PackageItemInfo(PackageItemInfo copy) {
+ this.packageName = copy.packageName;
+ }
+
@Override
protected String dumpProperties() {
return super.dumpProperties() + " packageName=" + packageName;
}
+
+ @Override
+ public PackageItemInfo clone() {
+ return new PackageItemInfo(this);
+ }
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index c37ed99..17a9a02 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -23,23 +23,19 @@
import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.AllAppsList;
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
+import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -48,6 +44,7 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SafeCloseable;
import java.util.ArrayList;
import java.util.Arrays;
@@ -93,6 +90,8 @@
FlagOp flagOp = FlagOp.NO_OP;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
+ final HashSet<ComponentName> removedComponents = new HashSet<>();
+
switch (mOp) {
case OP_ADD: {
for (int i = 0; i < N; i++) {
@@ -112,11 +111,14 @@
break;
}
case OP_UPDATE:
- for (int i = 0; i < N; i++) {
- if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
- iconCache.updateIconsForPkg(packages[i], mUser);
- appsList.updatePackage(context, packages[i], mUser);
- app.getWidgetCache().removePackage(packages[i], mUser);
+ try (SafeCloseable t =
+ appsList.trackRemoves(a -> removedComponents.add(a.componentName))) {
+ for (int i = 0; i < N; i++) {
+ if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
+ iconCache.updateIconsForPkg(packages[i], mUser);
+ appsList.updatePackage(context, packages[i], mUser);
+ app.getWidgetCache().removePackage(packages[i], mUser);
+ }
}
// Since package was just updated, the target must be available now.
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
@@ -153,23 +155,7 @@
break;
}
- final ArrayList<AppInfo> addedOrModified = new ArrayList<>();
- addedOrModified.addAll(appsList.added);
- appsList.added.clear();
- addedOrModified.addAll(appsList.modified);
- appsList.modified.clear();
- if (!addedOrModified.isEmpty()) {
- scheduleCallbackTask((callbacks) -> callbacks.bindAppsAddedOrUpdated(addedOrModified));
- }
-
- final ArrayList<AppInfo> removedApps = new ArrayList<>(appsList.removed);
- appsList.removed.clear();
- final HashSet<ComponentName> removedComponents = new HashSet<>();
- if (mOp == OP_UPDATE) {
- for (AppInfo ai : removedApps) {
- removedComponents.add(ai.componentName);
- }
- }
+ bindApplicationsIfNeeded();
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
@@ -296,12 +282,7 @@
}
if (!widgets.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindWidgetsRestored(widgets);
- }
- });
+ scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets));
}
}
@@ -332,16 +313,6 @@
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
}
- if (!removedApps.isEmpty()) {
- // Remove corresponding apps from All-Apps
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindAppInfosRemoved(removedApps);
- }
- });
- }
-
if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 8528228..c3cd9d0 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -19,7 +19,6 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 2cb256e..4b773d7 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,7 +21,6 @@
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -37,7 +36,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
/**
* Task to handle changing of lock state of the user
@@ -58,9 +56,9 @@
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
if (isUserUnlocked) {
- List<ShortcutInfo> shortcuts =
+ DeepShortcutManager.QueryResult shortcuts =
deepShortcutManager.queryForPinnedShortcuts(null, mUser);
- if (deepShortcutManager.wasLastCallSuccess()) {
+ if (shortcuts.wasSuccess()) {
for (ShortcutInfo shortcut : shortcuts) {
pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut);
}
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index bfa4ba9..a1917ec 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -20,16 +20,14 @@
import android.app.Person;
import android.service.notification.StatusBarNotification;
-import com.android.launcher3.Utilities;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
+
+import java.util.ArrayList;
+import java.util.List;
+
/**
* The key data associated with the notification, used to determine what to include
* in dots and dummy popup views before they are populated.
@@ -39,8 +37,9 @@
public class NotificationKeyData {
public final String notificationKey;
public final String shortcutId;
+ @NonNull
+ public final String[] personKeysFromNotification;
public int count;
- @NonNull public final String[] personKeysFromNotification;
private NotificationKeyData(String notificationKey, String shortcutId, int count,
String[] personKeysFromNotification) {
@@ -70,7 +69,8 @@
if (people == null || people.isEmpty()) {
return Utilities.EMPTY_STRING_ARRAY;
}
- return people.stream().map(Person::getKey).sorted().toArray(String[]::new);
+ return people.stream().filter(person -> person.getKey() != null)
+ .map(Person::getKey).sorted().toArray(String[]::new);
}
@Override
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 563f3b3..1f78a85 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,14 +1,11 @@
package com.android.launcher3.popup;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
@@ -20,23 +17,30 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetsBottomSheet;
import java.util.List;
-
/**
* Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
* onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
+ * @param <T>
*/
-public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo {
+public abstract class SystemShortcut<T extends BaseDraggingActivity>
+ extends ItemInfo {
private final int mIconResId;
private final int mLabelResId;
private final Icon mIcon;
@@ -201,6 +205,27 @@
}
}
+ public static class DismissPrediction extends SystemShortcut<Launcher> {
+ public DismissPrediction() {
+ super(R.drawable.ic_remove_no_shadow, R.string.dismiss_prediction_label);
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(Launcher activity, ItemInfo itemInfo) {
+ if (!FeatureFlags.ENABLE_PREDICTION_DISMISS.get()) return null;
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) return null;
+ return (view) -> {
+ PopupContainerWithArrow.closeAllOpenViews(activity);
+ activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.DISMISS_PREDICTION, ContainerType.DEEPSHORTCUTS);
+ AppLaunchTracker.INSTANCE.get(view.getContext())
+ .onDismissApp(itemInfo.getTargetComponent(),
+ itemInfo.user,
+ AppLaunchTracker.CONTAINER_PREDICTIONS);
+ };
+ }
+ }
+
protected static void dismissTaskMenuView(BaseDraggingActivity activity) {
AbstractFloatingView.closeOpenViews(activity, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
diff --git a/src/com/android/launcher3/popup/SystemShortcutFactory.java b/src/com/android/launcher3/popup/SystemShortcutFactory.java
index 37a2092..dfcc2f8 100644
--- a/src/com/android/launcher3/popup/SystemShortcutFactory.java
+++ b/src/com/android/launcher3/popup/SystemShortcutFactory.java
@@ -39,7 +39,9 @@
@SuppressWarnings("unused")
public SystemShortcutFactory() {
this(new SystemShortcut.AppInfo(),
- new SystemShortcut.Widgets(), new SystemShortcut.Install());
+ new SystemShortcut.Widgets(),
+ new SystemShortcut.Install(),
+ new SystemShortcut.DismissPrediction());
}
protected SystemShortcutFactory(SystemShortcut... shortcuts) {
@@ -53,6 +55,7 @@
systemShortcuts.add(systemShortcut);
}
}
+
return systemShortcuts;
}
}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index 7b62f53..970a03e 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -42,7 +42,6 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -50,6 +49,7 @@
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PackageManagerHelper;
import java.net.URISyntaxException;
import java.util.ArrayList;
@@ -223,7 +223,7 @@
case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_APPLICATION: {
intent = Intent.parseUri(c.getString(intentIndex), 0);
- if (Utilities.isLauncherAppTarget(intent)) {
+ if (PackageManagerHelper.isLauncherAppTarget(intent)) {
type = Favorites.ITEM_TYPE_APPLICATION;
} else {
values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 3c0c5fd..0a2d4e3 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -16,8 +16,6 @@
package com.android.launcher3.provider;
-import static com.android.launcher3.Utilities.getIntArrayFromString;
-import static com.android.launcher3.Utilities.getStringFromIntArray;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import android.app.backup.BackupManager;
@@ -40,6 +38,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
import java.io.InvalidObjectException;
@@ -246,8 +245,8 @@
SharedPreferences prefs = Utilities.getPrefs(context);
if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
- getIntArrayFromString(prefs.getString(APPWIDGET_OLD_IDS, "")),
- getIntArrayFromString(prefs.getString(APPWIDGET_IDS, "")));
+ IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(),
+ IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray());
} else {
FileLog.d(TAG, "No app widget ids to restore.");
}
@@ -259,8 +258,8 @@
public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
@NonNull int[] newIds) {
Utilities.getPrefs(context).edit()
- .putString(APPWIDGET_OLD_IDS, getStringFromIntArray(oldIds))
- .putString(APPWIDGET_IDS, getStringFromIntArray(newIds))
+ .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString())
+ .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString())
.commit();
}
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index c6370c5..446d4f8 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -22,7 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.MainThreadExecutor;
import java.lang.ref.WeakReference;
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index babbcdd..6cd2b2d 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -17,6 +17,7 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -60,7 +61,7 @@
if (info.container >= 0) {
Folder folder = Folder.getOpen(launcher);
if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(v)) {
+ if (!folder.getIconsInReadingOrder().contains(v)) {
folder.close(true);
} else {
folder.startDrag(v, dragOptions);
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index f95f74d..4a4a5ca 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -17,10 +17,13 @@
package com.android.launcher3.util;
import android.content.Context;
+import android.util.Log;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -35,6 +38,7 @@
public class IOUtils {
private static final int BUF_SIZE = 0x1000; // 4K
+ private static final String TAG = "IOUtils";
public static byte[] toByteArray(File file) throws IOException {
try (InputStream in = new FileInputStream(file)) {
@@ -77,4 +81,16 @@
}
return file.getAbsolutePath();
}
+
+ public static void closeSilently(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ Log.d(TAG, "Error closing", e);
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index d2a551f..7252f7a 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -17,6 +17,7 @@
package com.android.launcher3.util;
import java.util.Arrays;
+import java.util.StringTokenizer;
/**
* Copy of the platform hidden implementation of android.util.IntArray.
@@ -248,6 +249,17 @@
return b.toString();
}
+ public static IntArray fromConcatString(String concatString) {
+ StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
+ int[] array = new int[tokenizer.countTokens()];
+ int count = 0;
+ while (tokenizer.hasMoreTokens()) {
+ array[count] = Integer.parseInt(tokenizer.nextToken().trim());
+ count++;
+ }
+ return new IntArray(array, array.length);
+ }
+
/**
* Throws {@link ArrayIndexOutOfBoundsException} if the index is out of bounds.
*
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 7d3a941..ef4307e 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -24,9 +24,11 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
@@ -35,6 +37,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.widget.Toast;
import com.android.launcher3.AppInfo;
@@ -220,4 +223,73 @@
packageFilter.addDataSchemeSpecificPart(pkg, PatternMatcher.PATTERN_LITERAL);
return packageFilter;
}
+
+ public static boolean isSystemApp(Context context, Intent intent) {
+ PackageManager pm = context.getPackageManager();
+ ComponentName cn = intent.getComponent();
+ String packageName = null;
+ if (cn == null) {
+ ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if ((info != null) && (info.activityInfo != null)) {
+ packageName = info.activityInfo.packageName;
+ }
+ } else {
+ packageName = cn.getPackageName();
+ }
+ if (packageName != null) {
+ try {
+ PackageInfo info = pm.getPackageInfo(packageName, 0);
+ return (info != null) && (info.applicationInfo != null) &&
+ ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Finds a system apk which had a broadcast receiver listening to a particular action.
+ * @param action intent action used to find the apk
+ * @return a pair of apk package name and the resources.
+ */
+ public static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
+ final Intent intent = new Intent(action);
+ for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
+ if (info.activityInfo != null &&
+ (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ final String packageName = info.activityInfo.packageName;
+ try {
+ final Resources res = pm.getResourcesForApplication(packageName);
+ return Pair.create(packageName, res);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Failed to find resources for " + packageName);
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the intent is a valid launch intent for a launcher activity of an app.
+ * This is used to identify shortcuts which are different from the ones exposed by the
+ * applications' manifest file.
+ *
+ * @param launchIntent The intent that will be launched when the shortcut is clicked.
+ */
+ public static boolean isLauncherAppTarget(Intent launchIntent) {
+ if (launchIntent != null
+ && Intent.ACTION_MAIN.equals(launchIntent.getAction())
+ && launchIntent.getComponent() != null
+ && launchIntent.getCategories() != null
+ && launchIntent.getCategories().size() == 1
+ && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
+ && TextUtils.isEmpty(launchIntent.getDataString())) {
+ // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
+ Bundle extras = launchIntent.getExtras();
+ return extras == null || extras.keySet().isEmpty();
+ }
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/util/SafeCloseable.java b/src/com/android/launcher3/util/SafeCloseable.java
new file mode 100644
index 0000000..ba8ee04
--- /dev/null
+++ b/src/com/android/launcher3/util/SafeCloseable.java
@@ -0,0 +1,26 @@
+/*
+ * 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.util;
+
+/**
+ * Extension of closeable which does not throw an exception
+ */
+public interface SafeCloseable extends AutoCloseable {
+
+ @Override
+ void close();
+}
diff --git a/src_plugins/com/android/systemui/plugins/HotseatPlugin.java b/src_plugins/com/android/systemui/plugins/HotseatPlugin.java
new file mode 100644
index 0000000..1264e0d
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/HotseatPlugin.java
@@ -0,0 +1,20 @@
+package com.android.systemui.plugins;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Implement this plugin interface to add a sub-view in the Hotseat.
+ */
+@ProvidesInterface(action = HotseatPlugin.ACTION, version = HotseatPlugin.VERSION)
+public interface HotseatPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_HOTSEAT";
+ int VERSION = 1;
+
+ /**
+ * Creates a plugin view which will be added to the Hotseat.
+ */
+ View createView(ViewGroup parent);
+}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index 1710aef..789bfd8 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -16,9 +16,8 @@
package com.android.launcher3.model;
-import com.android.launcher3.AllAppsList;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.widget.WidgetListRowEntry;
diff --git a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
index 7119aea..bd7dd86 100644
--- a/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src_shortcuts_overrides/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -41,35 +41,27 @@
| ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_PINNED;
private static DeepShortcutManager sInstance;
- private static final Object sInstanceLock = new Object();
public static DeepShortcutManager getInstance(Context context) {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new DeepShortcutManager(context.getApplicationContext());
- }
- return sInstance;
+ if (sInstance == null) {
+ sInstance = new DeepShortcutManager(context.getApplicationContext());
}
+ return sInstance;
}
private final LauncherApps mLauncherApps;
- private boolean mWasLastCallSuccess;
private DeepShortcutManager(Context context) {
mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
}
- public boolean wasLastCallSuccess() {
- return mWasLastCallSuccess;
- }
-
/**
* Queries for the shortcuts with the package name and provided ids.
*
* This method is intended to get the full details for shortcuts when they are added or updated,
* because we only get "key" fields in onShortcutsChanged().
*/
- public List<ShortcutInfo> queryForFullDetails(String packageName,
+ public QueryResult queryForFullDetails(String packageName,
List<String> shortcutIds, UserHandle user) {
return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
}
@@ -78,7 +70,7 @@
* Gets all the manifest and dynamic shortcuts associated with the given package and user,
* to be displayed in the shortcuts container on long press.
*/
- public List<ShortcutInfo> queryForShortcutsContainer(ComponentName activity,
+ public QueryResult queryForShortcutsContainer(ComponentName activity,
UserHandle user) {
return query(ShortcutQuery.FLAG_MATCH_MANIFEST | ShortcutQuery.FLAG_MATCH_DYNAMIC,
activity.getPackageName(), activity, null, user);
@@ -96,10 +88,8 @@
pinnedIds.remove(id);
try {
mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
- mWasLastCallSuccess = true;
} catch (SecurityException|IllegalStateException e) {
Log.w(TAG, "Failed to unpin shortcut", e);
- mWasLastCallSuccess = false;
}
}
@@ -115,10 +105,8 @@
pinnedIds.add(id);
try {
mLauncherApps.pinShortcuts(packageName, pinnedIds, user);
- mWasLastCallSuccess = true;
} catch (SecurityException|IllegalStateException e) {
Log.w(TAG, "Failed to pin shortcut", e);
- mWasLastCallSuccess = false;
}
}
@@ -127,23 +115,18 @@
try {
mLauncherApps.startShortcut(packageName, id, sourceBounds,
startActivityOptions, user);
- mWasLastCallSuccess = true;
} catch (SecurityException|IllegalStateException e) {
Log.e(TAG, "Failed to start shortcut", e);
- mWasLastCallSuccess = false;
}
}
public Drawable getShortcutIconDrawable(ShortcutInfo shortcutInfo, int density) {
try {
- Drawable icon = mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
- mWasLastCallSuccess = true;
- return icon;
+ return mLauncherApps.getShortcutIconDrawable(shortcutInfo, density);
} catch (SecurityException|IllegalStateException e) {
Log.e(TAG, "Failed to get shortcut icon", e);
- mWasLastCallSuccess = false;
+ return null;
}
- return null;
}
/**
@@ -151,20 +134,20 @@
*
* If packageName is null, returns all pinned shortcuts regardless of package.
*/
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName, UserHandle user) {
+ public QueryResult queryForPinnedShortcuts(String packageName, UserHandle user) {
return queryForPinnedShortcuts(packageName, null, user);
}
- public List<ShortcutInfo> queryForPinnedShortcuts(String packageName,
- List<String> shortcutIds, UserHandle user) {
+ public QueryResult queryForPinnedShortcuts(String packageName, List<String> shortcutIds,
+ UserHandle user) {
return query(ShortcutQuery.FLAG_MATCH_PINNED, packageName, null, shortcutIds, user);
}
- public List<ShortcutInfo> queryForAllShortcuts(UserHandle user) {
+ public QueryResult queryForAllShortcuts(UserHandle user) {
return query(FLAG_GET_ALL, null, null, null, user);
}
- private List<String> extractIds(List<ShortcutInfo> shortcuts) {
+ private static List<String> extractIds(List<ShortcutInfo> shortcuts) {
List<String> shortcutIds = new ArrayList<>(shortcuts.size());
for (ShortcutInfo shortcut : shortcuts) {
shortcutIds.add(shortcut.getId());
@@ -178,8 +161,8 @@
*
* TODO: Use the cache to optimize this so we don't make an RPC every time.
*/
- private List<ShortcutInfo> query(int flags, String packageName,
- ComponentName activity, List<String> shortcutIds, UserHandle user) {
+ private QueryResult query(int flags, String packageName, ComponentName activity,
+ List<String> shortcutIds, UserHandle user) {
ShortcutQuery q = new ShortcutQuery();
q.setQueryFlags(flags);
if (packageName != null) {
@@ -187,18 +170,12 @@
q.setActivity(activity);
q.setShortcutIds(shortcutIds);
}
- List<ShortcutInfo> shortcutInfos = null;
try {
- shortcutInfos = mLauncherApps.getShortcuts(q, user);
- mWasLastCallSuccess = true;
+ return new QueryResult(mLauncherApps.getShortcuts(q, user));
} catch (SecurityException|IllegalStateException e) {
Log.e(TAG, "Failed to query for shortcuts", e);
- mWasLastCallSuccess = false;
+ return QueryResult.FAILURE;
}
- if (shortcutInfos == null) {
- return Collections.EMPTY_LIST;
- }
- return shortcutInfos;
}
public boolean hasHostPermission() {
@@ -209,4 +186,25 @@
}
return false;
}
+
+ public static class QueryResult extends ArrayList<ShortcutInfo> {
+
+ static QueryResult FAILURE = new QueryResult();
+
+ private final boolean mWasSuccess;
+
+ QueryResult(List<ShortcutInfo> result) {
+ super(result == null ? Collections.emptyList() : result);
+ mWasSuccess = true;
+ }
+
+ QueryResult() {
+ mWasSuccess = false;
+ }
+
+
+ public boolean wasSuccess() {
+ return mWasSuccess;
+ }
+ }
}
diff --git a/tests/Android.mk b/tests/Android.mk
index 0c41241..02ead4e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,7 +27,7 @@
ifneq (,$(wildcard frameworks/base))
else
- LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI
+ LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
../src/com/android/launcher3/ResourceUtils.java \
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 7d60ad6..64df8e0 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -15,9 +15,9 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.util.PackageManagerHelper;
import org.junit.Before;
import org.junit.Test;
@@ -115,7 +115,7 @@
WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
new Intent().setComponent(cn), false /* allowMissingTarget */, true);
assertNotNull(info);
- assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
}
@Test
@@ -127,7 +127,7 @@
WorkspaceItemInfo info = mLoaderCursor.getAppShortcutInfo(
new Intent().setComponent(cn), true /* allowMissingTarget */, true);
assertNotNull(info);
- assertTrue(Utilities.isLauncherAppTarget(info.intent));
+ assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
}
@Test