summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
blob: 602a8456662ae935b92657cc14f21ad457cedf22 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package com.android.launcher3.compat;

import android.content.Context;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Locale;

/**
 * Fallback class to support Alphabetic indexing if not supported by the framework.
 * TODO(winsonc): disable for non-english locales
 */
class BaseAlphabeticIndex {

    private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-";
    private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1;

    public BaseAlphabeticIndex() {}

    /**
     * Sets the max number of the label buckets in this index.
     */
    public void setMaxLabelCount(int count) {
        // Not currently supported
    }

    /**
     * Returns the index of the bucket in which the given string should appear.
     */
    public int getBucketIndex(String s) {
        if (s.isEmpty()) {
            return UNKNOWN_BUCKET_INDEX;
        }
        int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase());
        if (index != -1) {
            return index;
        }
        return UNKNOWN_BUCKET_INDEX;
    }

    /**
     * Returns the label for the bucket at the given index (as returned by getBucketIndex).
     */
    public String getBucketLabel(int index) {
        return BUCKETS.substring(index, index + 1);
    }
}

/**
 * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base alphabetic index.
 */
public class AlphabeticIndexCompat extends BaseAlphabeticIndex {

    private Object mAlphabeticIndex;
    private Method mAddLabelsMethod;
    private Method mSetMaxLabelCountMethod;
    private Method mGetBucketIndexMethod;
    private Method mGetBucketLabelMethod;
    private boolean mHasValidAlphabeticIndex;

    public AlphabeticIndexCompat(Context context) {
        super();
        try {
            Locale curLocale = context.getResources().getConfiguration().locale;
            Class clazz = Class.forName("libcore.icu.AlphabeticIndex");
            Constructor ctor = clazz.getConstructor(Locale.class);
            mAddLabelsMethod = clazz.getDeclaredMethod("addLabels", Locale.class);
            mSetMaxLabelCountMethod = clazz.getDeclaredMethod("setMaxLabelCount", int.class);
            mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class);
            mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class);
            mAlphabeticIndex = ctor.newInstance(curLocale);
            try {
                // Ensure we always have some base English locale buckets
                if (!curLocale.getLanguage().equals(new Locale("en").getLanguage())) {
                    mAddLabelsMethod.invoke(mAlphabeticIndex, Locale.ENGLISH);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            mHasValidAlphabeticIndex = true;
        } catch (Exception e) {
            mHasValidAlphabeticIndex = false;
        }
    }

    /**
     * Sets the max number of the label buckets in this index.
     * (ICU 51 default is 99)
     */
    public void setMaxLabelCount(int count) {
        if (mHasValidAlphabeticIndex) {
            try {
                mSetMaxLabelCountMethod.invoke(mAlphabeticIndex, count);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            super.setMaxLabelCount(count);
        }
    }

    /**
     * Returns the index of the bucket in which {@param s} should appear.
     * Function is synchronized because underlying routine walks an iterator
     * whose state is maintained inside the index object.
     */
    public int getBucketIndex(String s) {
        if (mHasValidAlphabeticIndex) {
            try {
                return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return super.getBucketIndex(s);
    }

    /**
     * Returns the label for the bucket at the given index (as returned by getBucketIndex).
     */
    public String getBucketLabel(int index) {
        if (mHasValidAlphabeticIndex) {
            try {
                return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return super.getBucketLabel(index);
    }
}