From e612775922ec9f8cc4e5cb976bc62b3312a3de0e Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Tue, 21 Jul 2015 17:01:26 -0700 Subject: resolved conflicts for merge of 13ef17a3 to mnc-dr-dev b/22609402 Change-Id: I140cf972d57e14737a6f91c0b4a8ec6c7ff1af2b --- .../launcher3/model/AbstractUserComparator.java | 67 ++++++++ .../android/launcher3/model/AppNameComparator.java | 101 ++++++++++++ .../android/launcher3/model/PackageItemInfo.java | 65 ++++++++ .../model/WidgetsAndShortcutNameComparator.java | 68 +++++++++ src/com/android/launcher3/model/WidgetsModel.java | 170 +++++++++++++++++++++ 5 files changed, 471 insertions(+) create mode 100644 src/com/android/launcher3/model/AbstractUserComparator.java create mode 100644 src/com/android/launcher3/model/AppNameComparator.java create mode 100644 src/com/android/launcher3/model/PackageItemInfo.java create mode 100644 src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java create mode 100644 src/com/android/launcher3/model/WidgetsModel.java (limited to 'src/com/android/launcher3/model') 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 implements Comparator { + + private HashMap 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. + *

+ * Used to sort application name in all apps view and widget tray view. + */ +public class AppNameComparator { + private final Collator mCollator; + private final AbstractUserComparator mAppInfoComparator; + private final Comparator mSectionNameComparator; + + public AppNameComparator(Context context) { + mCollator = Collator.getInstance(); + mAppInfoComparator = new AbstractUserComparator(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() { + @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 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 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 { + private final AppWidgetManagerCompat mManager; + private final PackageManager mPackageManager; + private final HashMap mLabelCache; + private final Collator mCollator; + private final UserHandleCompat mMainHandle; + + public WidgetsAndShortcutNameComparator(Context context) { + mManager = AppWidgetManagerCompat.getInstance(context); + mPackageManager = context.getPackageManager(); + mLabelCache = new HashMap(); + 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..185dfcae3 --- /dev/null +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -0,0 +1,170 @@ + +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.LauncherAppWidgetProviderInfo; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.AlphabeticIndexCompat; +import com.android.launcher3.compat.AppWidgetManagerCompat; +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. + * + *

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 mPackageItemInfos = new ArrayList<>(); + + /* Map of widgets and shortcuts that are tracked per package. */ + private HashMap> mWidgetsList = new HashMap<>(); + + private ArrayList mRawList; + + private final AppWidgetManagerCompat mAppWidgetMgr; + 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) { + mAppWidgetMgr = AppWidgetManagerCompat.getInstance(context); + mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context); + mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator(); + mIconCache = iconCache; + mAppFilter = appFilter; + mIndexer = new AlphabeticIndexCompat(context); + } + + private WidgetsModel(WidgetsModel model) { + mAppWidgetMgr = model.mAppWidgetMgr; + mPackageItemInfos = (ArrayList) model.mPackageItemInfos.clone(); + mWidgetsList = (HashMap>) model.mWidgetsList.clone(); + mRawList = (ArrayList) 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 getSortedWidgets(int pos) { + return mWidgetsList.get(mPackageItemInfos.get(pos)); + } + + public ArrayList getRawList() { + return mRawList; + } + + public void setWidgetsAndShortcuts(ArrayList 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 tmpPackageItemInfos = new HashMap<>(); + + // clear the lists. + mWidgetsList.clear(); + mPackageItemInfos.clear(); + + // add and update. + for (Object o: rawWidgetsShortcuts) { + String packageName = ""; + UserHandleCompat userHandle = null; + ComponentName componentName = null; + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; + componentName = widgetInfo.provider; + packageName = widgetInfo.provider.getPackageName(); + userHandle = mAppWidgetMgr.getUser(widgetInfo); + } else if (o instanceof ResolveInfo) { + ResolveInfo resolveInfo = (ResolveInfo) o; + componentName = new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name); + packageName = resolveInfo.activityInfo.packageName; + userHandle = UserHandleCompat.myUserHandle(); + } + + if (componentName == null || userHandle == null) { + Log.e(TAG, String.format("Widget cannot be set for %s.", o.getClass().toString())); + continue; + } + if (mAppFilter != null && !mAppFilter.shouldShowApp(componentName)) { + if (DEBUG) { + Log.d(TAG, String.format("%s is filtered and not added to the widget tray.", + packageName)); + } + continue; + } + + PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName); + ArrayList widgetsShortcutsList = mWidgetsList.get(pInfo); + if (widgetsShortcutsList != null) { + widgetsShortcutsList.add(o); + } else { + widgetsShortcutsList = new ArrayList(); + widgetsShortcutsList.add(o); + pInfo = new PackageItemInfo(packageName); + mIconCache.getTitleAndIconForApp(packageName, userHandle, + 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. + *

+ * Usage case: view binding without being modified from package updates. + */ + @Override + public WidgetsModel clone(){ + return new WidgetsModel(this); + } +} \ No newline at end of file -- cgit v1.2.3