summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWinson Chung <winsonc@google.com>2015-05-11 22:12:38 -0700
committerWinson Chung <winsonc@google.com>2015-05-12 21:19:32 +0000
commita3499dc019a677efb008d926b2cd9a5ad0bc0ca0 (patch)
treef4a15e24a57e149f53f9964c8f99978580baaaf6
parentf6d7f4f21c4d3efa08084d3caaa1a19660ca5777 (diff)
downloadandroid_packages_apps_Trebuchet-a3499dc019a677efb008d926b2cd9a5ad0bc0ca0.tar.gz
android_packages_apps_Trebuchet-a3499dc019a677efb008d926b2cd9a5ad0bc0ca0.tar.bz2
android_packages_apps_Trebuchet-a3499dc019a677efb008d926b2cd9a5ad0bc0ca0.zip
Tweaking section processing for different languages
- Ensuring that apps with non-letter/digit characters are ordered last in the misc bucket - Removing duplicate latin-alphabet sections for Simplified Chinese - Adding more appropriate misc bucket label for Japanese Bug 21022854 Change-Id: I62c7b219820ef88787fcfa83f1bd4202f16f9c0c
-rw-r--r--src/com/android/launcher3/AlphabeticalAppsList.java117
-rw-r--r--src/com/android/launcher3/AppsGridAdapter.java17
-rw-r--r--src/com/android/launcher3/DeviceProfile.java7
-rw-r--r--src/com/android/launcher3/compat/AlphabeticIndexCompat.java27
4 files changed, 122 insertions, 46 deletions
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index de4edcb7b..dc75637e5 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -14,23 +14,28 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
/**
* A private class to manage access to an app name comparator.
*/
class AppNameComparator {
- private UserManagerCompat mUserManager;
- private Comparator<AppInfo> mAppNameComparator;
+ private final UserManagerCompat mUserManager;
+ private final Collator mCollator;
+ private final Comparator<AppInfo> mAppInfoComparator;
+ private final Comparator<String> mSectionNameComparator;
private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
public AppNameComparator(Context context) {
- final Collator collator = Collator.getInstance();
+ mCollator = Collator.getInstance();
mUserManager = UserManagerCompat.getInstance(context);
- mAppNameComparator = new Comparator<AppInfo>() {
+ mAppInfoComparator = new Comparator<AppInfo>() {
public final int compare(AppInfo a, AppInfo b) {
- // Order by the title
- int result = collator.compare(a.title.toString(), b.title.toString());
+ // Order by the title in the current locale
+ int result = compareTitles(a.title.toString(), b.title.toString());
if (result == 0) {
// If two apps have the same title, then order by the component name
result = a.componentName.compareTo(b.componentName);
@@ -49,15 +54,45 @@ class AppNameComparator {
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<AppInfo> getComparator() {
+ public Comparator<AppInfo> getAppInfoComparator() {
// Clear the user serial cache so that we get serials as needed in the comparator
mUserSerialCache.clear();
- return mAppNameComparator;
+ 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.
+ */
+ private 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 = Character.isLetterOrDigit(titleA.codePointAt(0));
+ boolean bStartsWithLetter = 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);
}
/**
@@ -219,6 +254,7 @@ public class AlphabeticalAppsList {
private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
private static final int MAX_NUM_MERGES_PHONE = 2;
+ private Context mContext;
private List<AppInfo> mApps = new ArrayList<>();
private List<AppInfo> mFilteredApps = new ArrayList<>();
private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
@@ -234,6 +270,7 @@ public class AlphabeticalAppsList {
private int mNumAppsPerRow;
public AlphabeticalAppsList(Context context, int numAppsPerRow) {
+ mContext = context;
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppNameComparator(context);
setNumAppsPerRow(numAppsPerRow);
@@ -400,7 +437,7 @@ public class AlphabeticalAppsList {
* Implementation to actually add an app to the alphabetic list, but does not notify.
*/
private void addApp(AppInfo info) {
- int index = Collections.binarySearch(mApps, info, mAppNameComparator.getComparator());
+ int index = Collections.binarySearch(mApps, info, mAppNameComparator.getAppInfoComparator());
if (index < 0) {
mApps.add(-(index + 1), info);
}
@@ -411,7 +448,44 @@ public class AlphabeticalAppsList {
*/
private void onAppsUpdated() {
// Sort the list of apps
- Collections.sort(mApps, mAppNameComparator.getComparator());
+ Collections.sort(mApps, mAppNameComparator.getAppInfoComparator());
+
+ // As a special case for some languages (currently only Simplified Chinese), we may need to
+ // coalesce sections
+ Locale curLocale = mContext.getResources().getConfiguration().locale;
+ TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
+ boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
+ if (localeRequiresSectionSorting) {
+ // Compute the section headers. We use a TreeMap with the section name comparator to
+ // ensure that the sections are ordered when we iterate over it later
+ sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator());
+ for (AppInfo info : mApps) {
+ // Add the section to the cache
+ String sectionName = mCachedSectionNames.get(info.title);
+ if (sectionName == null) {
+ sectionName = mIndexer.computeSectionName(info.title);
+ mCachedSectionNames.put(info.title, sectionName);
+ }
+
+ // Add it to the mapping
+ ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
+ if (sectionApps == null) {
+ sectionApps = new ArrayList<>();
+ sectionMap.put(sectionName, sectionApps);
+ }
+ sectionApps.add(info);
+ }
+ } else {
+ // Just compute the section headers for use below
+ for (AppInfo info : mApps) {
+ // Add the section to the cache
+ String sectionName = mCachedSectionNames.get(info.title);
+ if (sectionName == null) {
+ sectionName = mIndexer.computeSectionName(info.title);
+ mCachedSectionNames.put(info.title, sectionName);
+ }
+ }
+ }
// Prepare to update the list of sections, filtered apps, etc.
mFilteredApps.clear();
@@ -444,23 +518,22 @@ public class AlphabeticalAppsList {
}
// Add all the other apps to the combined list
- allApps.addAll(mApps);
+ if (localeRequiresSectionSorting) {
+ for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
+ allApps.addAll(entry.getValue());
+ }
+ } else {
+ allApps.addAll(mApps);
+ }
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
- // combined list
+ // ordered set of sections
int numApps = allApps.size();
for (int i = 0; i < numApps; i++) {
boolean isPredictedApp = i < numPredictedApps;
AppInfo info = allApps.get(i);
- String sectionName = "";
- if (!isPredictedApp) {
- // Only cache section names from non-predicted apps
- sectionName = mCachedSectionNames.get(info.title);
- if (sectionName == null) {
- sectionName = mIndexer.computeSectionName(info.title);
- mCachedSectionNames.put(info.title, sectionName);
- }
- }
+ // The section name was computed above so this should be find
+ String sectionName = isPredictedApp ? "" : mCachedSectionNames.get(info.title);
// Check if we want to retain this app
if (mFilter != null && !mFilter.retainApp(info, sectionName)) {
@@ -478,7 +551,7 @@ public class AlphabeticalAppsList {
mFastScrollerSections.add(lastFastScrollerSectionInfo);
// Create a new section item to break the flow of items in the list
- if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS && !hasFilter()) {
+ if (!hasFilter()) {
AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
mSectionedFilteredApps.add(sectionItem);
}
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index 563044916..a6902d5d3 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -63,11 +63,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
if (mApps.getAdapterItems().get(position).isSectionHeader) {
// Section break spans full width
- if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
- return 0;
- } else {
- return mAppsPerRow;
- }
+ return mAppsPerRow;
} else {
return 1;
}
@@ -290,10 +286,8 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
mTouchListener = touchListener;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
- if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
- mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
- mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset);
- }
+ mStartMargin = res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
+ mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.apps_grid_section_y_offset);
mPaddingStart = res.getDimensionPixelSize(R.dimen.apps_container_inset);
mSectionTextPaint = new Paint();
@@ -342,10 +336,7 @@ class AppsGridAdapter extends RecyclerView.Adapter<AppsGridAdapter.ViewHolder> {
*/
public RecyclerView.ItemDecoration getItemDecoration() {
// We don't draw any headers when we are uncomfortably dense
- if (!AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
- return mItemDecoration;
- }
- return null;
+ return mItemDecoration;
}
/**
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7a6e9a20a..3bbf0e7d8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -437,13 +437,6 @@ public class DeviceProfile {
}
public boolean updateAppsViewNumCols(Resources res, int containerWidth) {
- if (AppsContainerView.GRID_HIDE_SECTION_HEADERS) {
- if (appsViewNumCols != allAppsNumCols) {
- appsViewNumCols = allAppsNumCols;
- return true;
- }
- return false;
- }
int appsViewLeftMarginPx =
res.getDimensionPixelSize(R.dimen.apps_grid_view_start_margin);
int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index 18cdc81f3..ec1fb669f 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -52,12 +52,15 @@ class BaseAlphabeticIndex {
*/
public class AlphabeticIndexCompat extends BaseAlphabeticIndex {
+ private static final String MID_DOT = "\u2219";
+
private Object mAlphabeticIndex;
private Method mAddLabelsMethod;
private Method mSetMaxLabelCountMethod;
private Method mGetBucketIndexMethod;
private Method mGetBucketLabelMethod;
private boolean mHasValidAlphabeticIndex;
+ private String mDefaultMiscLabel;
public AlphabeticIndexCompat(Context context) {
super();
@@ -72,12 +75,20 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex {
mAlphabeticIndex = ctor.newInstance(curLocale);
try {
// Ensure we always have some base English locale buckets
- if (!curLocale.getLanguage().equals(new Locale("en").getLanguage())) {
+ if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
mAddLabelsMethod.invoke(mAlphabeticIndex, Locale.ENGLISH);
}
} catch (Exception e) {
e.printStackTrace();
}
+ if (curLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) {
+ // Japanese character 他 ("misc")
+ mDefaultMiscLabel = "\u4ed6";
+ // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
+ } else {
+ // Dot
+ mDefaultMiscLabel = MID_DOT;
+ }
mHasValidAlphabeticIndex = true;
} catch (Exception e) {
mHasValidAlphabeticIndex = false;
@@ -107,13 +118,21 @@ public class AlphabeticIndexCompat extends BaseAlphabeticIndex {
String s = Utilities.trim(cs);
String sectionName = getBucketLabel(getBucketIndex(s));
if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
- boolean startsWithDigit = Character.isDigit(s.codePointAt(0));
+ int c = s.codePointAt(0);
+ boolean startsWithDigit = Character.isDigit(c);
if (startsWithDigit) {
// Digit section
return "#";
} else {
- // Unknown section
- return "\u2022";
+ boolean startsWithLetter = Character.isLetter(c);
+ if (startsWithLetter) {
+ return mDefaultMiscLabel;
+ } else {
+ // In languages where these differ, this ensures that we differentiate
+ // between the misc section in the native language and a misc section
+ // for everything else.
+ return MID_DOT;
+ }
}
}
return sectionName;