1 package com.android.launcher3.compat;
3 import android.content.Context;
4 import com.android.launcher3.Utilities;
6 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Method;
8 import java.util.Locale;
11 * Fallback class to support Alphabetic indexing if not supported by the framework.
12 * TODO(winsonc): disable for non-english locales
14 class BaseAlphabeticIndex {
16 private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-";
17 private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1;
19 public BaseAlphabeticIndex() {}
22 * Sets the max number of the label buckets in this index.
24 public void setMaxLabelCount(int count) {
25 // Not currently supported
29 * Returns the index of the bucket in which the given string should appear.
31 protected int getBucketIndex(String s) {
33 return UNKNOWN_BUCKET_INDEX;
35 int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase());
39 return UNKNOWN_BUCKET_INDEX;
43 * Returns the label for the bucket at the given index (as returned by getBucketIndex).
45 protected String getBucketLabel(int index) {
46 return BUCKETS.substring(index, index + 1);
51 * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base alphabetic index.
53 public class AlphabeticIndexCompat extends BaseAlphabeticIndex {
55 private static final String MID_DOT = "\u2219";
57 private Object mAlphabeticIndex;
58 private Method mAddLabelsMethod;
59 private Method mSetMaxLabelCountMethod;
60 private Method mGetBucketIndexMethod;
61 private Method mGetBucketLabelMethod;
62 private boolean mHasValidAlphabeticIndex;
63 private String mDefaultMiscLabel;
65 public AlphabeticIndexCompat(Context context) {
68 Locale curLocale = context.getResources().getConfiguration().locale;
69 Class clazz = Class.forName("libcore.icu.AlphabeticIndex");
70 Constructor ctor = clazz.getConstructor(Locale.class);
71 mAddLabelsMethod = clazz.getDeclaredMethod("addLabels", Locale.class);
72 mSetMaxLabelCountMethod = clazz.getDeclaredMethod("setMaxLabelCount", int.class);
73 mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class);
74 mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class);
75 mAlphabeticIndex = ctor.newInstance(curLocale);
77 // Ensure we always have some base English locale buckets
78 if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
79 mAddLabelsMethod.invoke(mAlphabeticIndex, Locale.ENGLISH);
81 } catch (Exception e) {
84 if (curLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) {
85 // Japanese character 他 ("misc")
86 mDefaultMiscLabel = "\u4ed6";
87 // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
90 mDefaultMiscLabel = MID_DOT;
92 mHasValidAlphabeticIndex = true;
93 } catch (Exception e) {
94 mHasValidAlphabeticIndex = false;
99 * Sets the max number of the label buckets in this index.
100 * (ICU 51 default is 99)
102 public void setMaxLabelCount(int count) {
103 if (mHasValidAlphabeticIndex) {
105 mSetMaxLabelCountMethod.invoke(mAlphabeticIndex, count);
106 } catch (Exception e) {
110 super.setMaxLabelCount(count);
115 * Computes the section name for an given string {@param s}.
117 public String computeSectionName(CharSequence cs) {
118 String s = Utilities.trim(cs);
119 String sectionName = getBucketLabel(getBucketIndex(s));
120 if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
121 int c = s.codePointAt(0);
122 boolean startsWithDigit = Character.isDigit(c);
123 if (startsWithDigit) {
127 boolean startsWithLetter = Character.isLetter(c);
128 if (startsWithLetter) {
129 return mDefaultMiscLabel;
131 // In languages where these differ, this ensures that we differentiate
132 // between the misc section in the native language and a misc section
133 // for everything else.
142 * Returns the index of the bucket in which {@param s} should appear.
143 * Function is synchronized because underlying routine walks an iterator
144 * whose state is maintained inside the index object.
146 protected int getBucketIndex(String s) {
147 if (mHasValidAlphabeticIndex) {
149 return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s);
150 } catch (Exception e) {
154 return super.getBucketIndex(s);
158 * Returns the label for the bucket at the given index (as returned by getBucketIndex).
160 protected String getBucketLabel(int index) {
161 if (mHasValidAlphabeticIndex) {
163 return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index);
164 } catch (Exception e) {
168 return super.getBucketLabel(index);