summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/model')
-rw-r--r--src/com/android/launcher3/model/AbstractUserComparator.java67
-rw-r--r--src/com/android/launcher3/model/AppNameComparator.java101
-rw-r--r--src/com/android/launcher3/model/PackageItemInfo.java65
-rw-r--r--src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java68
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java163
5 files changed, 464 insertions, 0 deletions
diff --git a/src/com/android/launcher3/model/AbstractUserComparator.java b/src/com/android/launcher3/model/AbstractUserComparator.java
new file mode 100644
index 000000000..cf47ce648
--- /dev/null
+++ b/src/com/android/launcher3/model/AbstractUserComparator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.content.Context;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
+import java.util.Comparator;
+import java.util.HashMap;
+
+/**
+ * A comparator to arrange items based on user profiles.
+ */
+public abstract class AbstractUserComparator<T extends ItemInfo> implements Comparator<T> {
+
+ private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
+ private final UserManagerCompat mUserManager;
+ private final UserHandleCompat mMyUser;
+
+ public AbstractUserComparator(Context context) {
+ mUserManager = UserManagerCompat.getInstance(context);
+ mMyUser = UserHandleCompat.myUserHandle();
+ }
+
+ @Override
+ public int compare(T lhs, T rhs) {
+ if (mMyUser.equals(lhs.user)) {
+ return -1;
+ } else {
+ Long aUserSerial = getAndCacheUserSerial(lhs.user);
+ Long bUserSerial = getAndCacheUserSerial(rhs.user);
+ return aUserSerial.compareTo(bUserSerial);
+ }
+ }
+
+ /**
+ * Returns the user serial for this user, using a cached serial if possible.
+ */
+ private Long getAndCacheUserSerial(UserHandleCompat user) {
+ Long userSerial = mUserSerialCache.get(user);
+ if (userSerial == null) {
+ userSerial = mUserManager.getSerialNumberForUser(user);
+ mUserSerialCache.put(user, userSerial);
+ }
+ return userSerial;
+ }
+
+ public void clearUserCache() {
+ mUserSerialCache.clear();
+ }
+}
diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java
new file mode 100644
index 000000000..c4b74d4dc
--- /dev/null
+++ b/src/com/android/launcher3/model/AppNameComparator.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.content.Context;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.util.Thunk;
+
+import java.text.Collator;
+import java.util.Comparator;
+
+/**
+ * Class to manage access to an app name comparator.
+ * <p>
+ * Used to sort application name in all apps view and widget tray view.
+ */
+public class AppNameComparator {
+ private final Collator mCollator;
+ private final AbstractUserComparator<ItemInfo> mAppInfoComparator;
+ private final Comparator<String> mSectionNameComparator;
+
+ public AppNameComparator(Context context) {
+ mCollator = Collator.getInstance();
+ mAppInfoComparator = new AbstractUserComparator<ItemInfo>(context) {
+
+ @Override
+ public final int compare(ItemInfo a, ItemInfo b) {
+ // Order by the title in the current locale
+ int result = compareTitles(a.title.toString(), b.title.toString());
+ if (result == 0 && a instanceof AppInfo && b instanceof AppInfo) {
+ AppInfo aAppInfo = (AppInfo) a;
+ AppInfo bAppInfo = (AppInfo) b;
+ // If two apps have the same title, then order by the component name
+ result = aAppInfo.componentName.compareTo(bAppInfo.componentName);
+ if (result == 0) {
+ // If the two apps are the same component, then prioritize by the order that
+ // the app user was created (prioritizing the main user's apps)
+ return super.compare(a, b);
+ }
+ }
+ return result;
+ }
+ };
+ mSectionNameComparator = new Comparator<String>() {
+ @Override
+ public int compare(String o1, String o2) {
+ return compareTitles(o1, o2);
+ }
+ };
+ }
+
+ /**
+ * Returns a locale-aware comparator that will alphabetically order a list of applications.
+ */
+ public Comparator<ItemInfo> getAppInfoComparator() {
+ // Clear the user serial cache so that we get serials as needed in the comparator
+ mAppInfoComparator.clearUserCache();
+ return mAppInfoComparator;
+ }
+
+ /**
+ * Returns a locale-aware comparator that will alphabetically order a list of section names.
+ */
+ public Comparator<String> getSectionNameComparator() {
+ return mSectionNameComparator;
+ }
+
+ /**
+ * Compares two titles with the same return value semantics as Comparator.
+ */
+ @Thunk int compareTitles(String titleA, String titleB) {
+ // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit
+ boolean aStartsWithLetter = (titleA.length() > 0) &&
+ Character.isLetterOrDigit(titleA.codePointAt(0));
+ boolean bStartsWithLetter = (titleB.length() > 0) &&
+ Character.isLetterOrDigit(titleB.codePointAt(0));
+ if (aStartsWithLetter && !bStartsWithLetter) {
+ return -1;
+ } else if (!aStartsWithLetter && bStartsWithLetter) {
+ return 1;
+ }
+
+ // Order by the title in the current locale
+ return mCollator.compare(titleA, titleB);
+ }
+}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
new file mode 100644
index 000000000..30f228c68
--- /dev/null
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.graphics.Bitmap;
+
+import com.android.launcher3.ItemInfo;
+
+import java.util.Arrays;
+
+/**
+ * Represents a {@link Package} in the widget tray section.
+ */
+public class PackageItemInfo extends ItemInfo {
+
+ /**
+ * A bitmap version of the application icon.
+ */
+ public Bitmap iconBitmap;
+
+ /**
+ * Indicates whether we're using a low res icon.
+ */
+ public boolean usingLowResIcon;
+
+ /**
+ * Package name of the {@link ItemInfo}.
+ */
+ public String packageName;
+
+ /**
+ * Character that is used as a section name for the {@link ItemInfo#title}.
+ * (e.g., "G" will be stored if title is "Google")
+ */
+ public String titleSectionName;
+
+ int flags = 0;
+
+ PackageItemInfo(String packageName) {
+ this.packageName = packageName;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageItemInfo(title=" + title + " id=" + this.id
+ + " type=" + this.itemType + " container=" + this.container
+ + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
+ + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ + " user=" + user + ")";
+ }
+}
diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
new file mode 100644
index 000000000..61e895283
--- /dev/null
+++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
@@ -0,0 +1,68 @@
+package com.android.launcher3.model;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.HashMap;
+
+public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
+ private final AppWidgetManagerCompat mManager;
+ private final PackageManager mPackageManager;
+ private final HashMap<Object, String> mLabelCache;
+ private final Collator mCollator;
+ private final UserHandleCompat mMainHandle;
+
+ public WidgetsAndShortcutNameComparator(Context context) {
+ mManager = AppWidgetManagerCompat.getInstance(context);
+ mPackageManager = context.getPackageManager();
+ mLabelCache = new HashMap<Object, String>();
+ mCollator = Collator.getInstance();
+ mMainHandle = UserHandleCompat.myUserHandle();
+ }
+
+ @Override
+ public final int compare(Object a, Object b) {
+ String labelA, labelB;
+ if (mLabelCache.containsKey(a)) {
+ labelA = mLabelCache.get(a);
+ } else {
+ labelA = (a instanceof LauncherAppWidgetProviderInfo)
+ ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
+ : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
+ mLabelCache.put(a, labelA);
+ }
+ if (mLabelCache.containsKey(b)) {
+ labelB = mLabelCache.get(b);
+ } else {
+ labelB = (b instanceof LauncherAppWidgetProviderInfo)
+ ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
+ : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
+ mLabelCache.put(b, labelB);
+ }
+
+ // Currently, there is no work profile shortcuts, hence only considering the widget cases.
+
+ boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) &&
+ !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a));
+ boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) &&
+ !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b));
+
+ // Independent of how the labels compare, if only one of the two widget info belongs to
+ // work profile, put that one in the back.
+ if (aWorkProfile && !bWorkProfile) {
+ return 1;
+ }
+ if (!aWorkProfile && bWorkProfile) {
+ return -1;
+ }
+ return mCollator.compare(labelA, labelB);
+ }
+};
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
new file mode 100644
index 000000000..09a3242b5
--- /dev/null
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -0,0 +1,163 @@
+
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.util.Log;
+
+import com.android.launcher3.AppFilter;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Widgets data model that is used by the adapters of the widget views and controllers.
+ *
+ * <p> The widgets and shortcuts are organized using package name as its index.
+ */
+public class WidgetsModel {
+
+ private static final String TAG = "WidgetsModel";
+ private static final boolean DEBUG = false;
+
+ /* List of packages that is tracked by this model. */
+ private ArrayList<PackageItemInfo> mPackageItemInfos = new ArrayList<>();
+
+ /* Map of widgets and shortcuts that are tracked per package. */
+ private HashMap<PackageItemInfo, ArrayList<Object>> mWidgetsList = new HashMap<>();
+
+ private ArrayList<Object> mRawList;
+
+ private final Comparator mWidgetAndShortcutNameComparator;
+ private final Comparator mAppNameComparator;
+ private final IconCache mIconCache;
+ private final AppFilter mAppFilter;
+ private AlphabeticIndexCompat mIndexer;
+
+ public WidgetsModel(Context context, IconCache iconCache, AppFilter appFilter) {
+ mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
+ mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
+ mIconCache = iconCache;
+ mAppFilter = appFilter;
+ mIndexer = new AlphabeticIndexCompat(context);
+ }
+
+ private WidgetsModel(WidgetsModel model) {
+ mPackageItemInfos = (ArrayList<PackageItemInfo>) model.mPackageItemInfos.clone();
+ mWidgetsList = (HashMap<PackageItemInfo, ArrayList<Object>>) model.mWidgetsList.clone();
+ mRawList = (ArrayList<Object>) model.mRawList.clone();
+ mWidgetAndShortcutNameComparator = model.mWidgetAndShortcutNameComparator;
+ mAppNameComparator = model.mAppNameComparator;
+ mIconCache = model.mIconCache;
+ mAppFilter = model.mAppFilter;
+ }
+
+ // Access methods that may be deleted if the private fields are made package-private.
+ public int getPackageSize() {
+ if (mPackageItemInfos == null) {
+ return 0;
+ }
+ return mPackageItemInfos.size();
+ }
+
+ // Access methods that may be deleted if the private fields are made package-private.
+ public PackageItemInfo getPackageItemInfo(int pos) {
+ if (pos >= mPackageItemInfos.size() || pos < 0) {
+ return null;
+ }
+ return mPackageItemInfos.get(pos);
+ }
+
+ public List<Object> getSortedWidgets(int pos) {
+ return mWidgetsList.get(mPackageItemInfos.get(pos));
+ }
+
+ public ArrayList<Object> getRawList() {
+ return mRawList;
+ }
+
+ public void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+ Utilities.assertWorkerThread();
+ mRawList = rawWidgetsShortcuts;
+ if (DEBUG) {
+ Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
+ }
+
+ // Temporary list for {@link PackageItemInfos} to avoid having to go through
+ // {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
+ HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
+
+ // clear the lists.
+ mWidgetsList.clear();
+ mPackageItemInfos.clear();
+
+ // add and update.
+ for (Object o: rawWidgetsShortcuts) {
+ String packageName = "";
+ ComponentName componentName = null;
+ if (o instanceof LauncherAppWidgetProviderInfo) {
+ LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
+ componentName = widgetInfo.provider;
+ packageName = widgetInfo.provider.getPackageName();
+ } else if (o instanceof ResolveInfo) {
+ ResolveInfo resolveInfo = (ResolveInfo) o;
+ componentName = new ComponentName(resolveInfo.activityInfo.packageName,
+ resolveInfo.activityInfo.name);
+ packageName = resolveInfo.activityInfo.packageName;
+ }
+
+ if (componentName == null) {
+ Log.e(TAG, String.format("Widget cannot be set for class=%s",
+ o.getClass().toString()));
+ continue;
+ }
+ if (mAppFilter != null && !mAppFilter.shouldShowApp(componentName)) {
+ Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
+ packageName));
+ continue;
+ }
+
+ PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
+ ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(pInfo);
+ if (widgetsShortcutsList != null) {
+ widgetsShortcutsList.add(o);
+ } else {
+ widgetsShortcutsList = new ArrayList<Object>();
+ widgetsShortcutsList.add(o);
+ pInfo = new PackageItemInfo(packageName);
+ mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
+ true /* userLowResIcon */, pInfo);
+ pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title);
+ mWidgetsList.put(pInfo, widgetsShortcutsList);
+ tmpPackageItemInfos.put(packageName, pInfo);
+ mPackageItemInfos.add(pInfo);
+ }
+ }
+
+ // sort.
+ Collections.sort(mPackageItemInfos, mAppNameComparator);
+ for (PackageItemInfo p: mPackageItemInfos) {
+ Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
+ }
+ }
+
+ /**
+ * Create a snapshot of the widgets model.
+ * <p>
+ * Usage case: view binding without being modified from package updates.
+ */
+ @Override
+ public WidgetsModel clone(){
+ return new WidgetsModel(this);
+ }
+} \ No newline at end of file