• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.inputmethod;
18 
19 import android.content.Context;
20 import android.content.CursorLoader;
21 import android.database.Cursor;
22 import android.database.MatrixCursor;
23 import android.provider.UserDictionary;
24 import android.support.annotation.VisibleForTesting;
25 import android.util.ArraySet;
26 
27 import java.util.Locale;
28 import java.util.Objects;
29 import java.util.Set;
30 
31 public class UserDictionaryCursorLoader extends CursorLoader {
32 
33     @VisibleForTesting
34     static final String[] QUERY_PROJECTION = {
35             UserDictionary.Words._ID,
36             UserDictionary.Words.WORD,
37             UserDictionary.Words.SHORTCUT
38     };
39 
40     // The index of the shortcut in the above array.
41     static final int INDEX_SHORTCUT = 2;
42 
43     // Either the locale is empty (means the word is applicable to all locales)
44     // or the word equals our current locale
45     private static final String QUERY_SELECTION =
46             UserDictionary.Words.LOCALE + "=?";
47     private static final String QUERY_SELECTION_ALL_LOCALES =
48             UserDictionary.Words.LOCALE + " is null";
49 
50 
51     // Locale can be any of:
52     // - The string representation of a locale, as returned by Locale#toString()
53     // - The empty string. This means we want a cursor returning words valid for all locales.
54     // - null. This means we want a cursor for the current locale, whatever this is.
55     // Note that this contrasts with the data inside the database, where NULL means "all
56     // locales" and there should never be an empty string. The confusion is called by the
57     // historical use of null for "all locales".
58     // TODO: it should be easy to make this more readable by making the special values
59     // human-readable, like "all_locales" and "current_locales" strings, provided they
60     // can be guaranteed not to match locales that may exist.
61     private final String mLocale;
62 
UserDictionaryCursorLoader(Context context, String locale)63     public UserDictionaryCursorLoader(Context context, String locale) {
64         super(context);
65         mLocale = locale;
66     }
67 
68     @Override
loadInBackground()69     public Cursor loadInBackground() {
70         final MatrixCursor result = new MatrixCursor(QUERY_PROJECTION);
71         final Cursor candidate;
72         if ("".equals(mLocale)) {
73             // Case-insensitive sort
74             candidate = getContext().getContentResolver().query(
75                     UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
76                     QUERY_SELECTION_ALL_LOCALES, null,
77                     "UPPER(" + UserDictionary.Words.WORD + ")");
78         } else {
79             final String queryLocale = null != mLocale ? mLocale : Locale.getDefault().toString();
80             candidate = getContext().getContentResolver().query(UserDictionary.Words.CONTENT_URI,
81                     QUERY_PROJECTION, QUERY_SELECTION,
82                     new String[]{queryLocale}, "UPPER(" + UserDictionary.Words.WORD + ")");
83         }
84         final Set<Integer> hashSet = new ArraySet<>();
85         for (candidate.moveToFirst(); !candidate.isAfterLast(); candidate.moveToNext()) {
86             final int id = candidate.getInt(0);
87             final String word = candidate.getString(1);
88             final String shortcut = candidate.getString(2);
89             final int hash = Objects.hash(word, shortcut);
90             if (hashSet.contains(hash)) {
91                 continue;
92             }
93             hashSet.add(hash);
94             result.addRow(new Object[]{id, word, shortcut});
95         }
96         return result;
97     }
98 }
99