1 package com.android.launcher3.compat; 2 3 import android.content.Context; 4 import android.icu.text.AlphabeticIndex; 5 import android.os.LocaleList; 6 import android.util.Log; 7 8 import androidx.annotation.NonNull; 9 10 import com.android.launcher3.Utilities; 11 12 import java.util.Locale; 13 14 public class AlphabeticIndexCompat { 15 16 // TODO(b/336947811): Set to false after root causing is done. 17 private static final boolean DEBUG = true; 18 private static final String TAG = "AlphabeticIndexCompat"; 19 private static final String MID_DOT = "\u2219"; 20 private final String mDefaultMiscLabel; 21 22 private final AlphabeticIndex.ImmutableIndex mBaseIndex; 23 AlphabeticIndexCompat(Context context)24 public AlphabeticIndexCompat(Context context) { 25 this(context.getResources().getConfiguration().getLocales()); 26 } 27 AlphabeticIndexCompat(LocaleList locales)28 public AlphabeticIndexCompat(LocaleList locales) { 29 int localeCount = locales.size(); 30 31 Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0); 32 AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale); 33 for (int i = 1; i < localeCount; i++) { 34 indexBuilder.addLabels(locales.get(i)); 35 } 36 indexBuilder.addLabels(Locale.ENGLISH); 37 mBaseIndex = indexBuilder.buildImmutableIndex(); 38 39 if (primaryLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) { 40 // Japanese character 他 ("misc") 41 mDefaultMiscLabel = "\u4ed6"; 42 // TODO(winsonc, omakoto): We need to handle Japanese sections better, 43 // especially the kanji 44 } else { 45 // Dot 46 mDefaultMiscLabel = MID_DOT; 47 } 48 } 49 50 /** 51 * Computes the section name for an given string {@param s}. 52 */ computeSectionName(@onNull CharSequence cs)53 public String computeSectionName(@NonNull CharSequence cs) { 54 String s = Utilities.trim(cs); 55 String sectionName = mBaseIndex.getBucket(mBaseIndex.getBucketIndex(s)).getLabel(); 56 if (DEBUG) { 57 Log.d(TAG, "computeSectionName: cs: " + cs + " sectionName: " + sectionName); 58 } 59 if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) { 60 int c = s.codePointAt(0); 61 boolean startsWithDigit = Character.isDigit(c); 62 if (startsWithDigit) { 63 // Digit section 64 return "#"; 65 } else { 66 boolean startsWithLetter = Character.isLetter(c); 67 if (startsWithLetter) { 68 return mDefaultMiscLabel; 69 } else { 70 // In languages where these differ, this ensures that we differentiate 71 // between the misc section in the native language and a misc section 72 // for everything else. 73 return MID_DOT; 74 } 75 } 76 } 77 return sectionName; 78 } 79 } 80