1 /*
2  * Copyright (C) 2021 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.tv.settings.library.settingslib;
18 
19 import android.annotation.UiThread;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.util.Log;
23 import android.view.inputmethod.InputMethodInfo;
24 import android.view.inputmethod.InputMethodManager;
25 
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 
31 /**
32  * This class is a wrapper for {@link InputMethodManager} and
33  * {@link android.provider.Settings.Secure#ENABLED_INPUT_METHODS}. You need to refresh internal
34  * states manually on some events when "InputMethodInfo"s and "InputMethodSubtype"s can be changed.
35  */
36 @UiThread
37 public class InputMethodSettingValuesWrapper {
38     private static final String TAG = InputMethodSettingValuesWrapper.class.getSimpleName();
39 
40     private static volatile InputMethodSettingValuesWrapper sInstance;
41     private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
42     private final ContentResolver mContentResolver;
43     private final InputMethodManager mImm;
44 
getInstance( Context context)45     public static InputMethodSettingValuesWrapper getInstance(
46             Context context) {
47         if (sInstance == null) {
48             synchronized (TAG) {
49                 if (sInstance == null) {
50                     sInstance = new InputMethodSettingValuesWrapper(context);
51                 }
52             }
53         }
54         return sInstance;
55     }
56 
57     // Ensure singleton
InputMethodSettingValuesWrapper(Context context)58     private InputMethodSettingValuesWrapper(Context context) {
59         mContentResolver = context.getContentResolver();
60         mImm = context.getSystemService(InputMethodManager.class);
61         refreshAllInputMethodAndSubtypes();
62     }
63 
refreshAllInputMethodAndSubtypes()64     public void refreshAllInputMethodAndSubtypes() {
65         mMethodList.clear();
66         mMethodList.addAll(mImm.getInputMethodList());
67     }
68 
getInputMethodList()69     public List<InputMethodInfo> getInputMethodList() {
70         return new ArrayList<>(mMethodList);
71     }
72 
isAlwaysCheckedIme(InputMethodInfo imi)73     public boolean isAlwaysCheckedIme(InputMethodInfo imi) {
74         final boolean isEnabled = isEnabledImi(imi);
75         if (getEnabledInputMethodList().size() <= 1 && isEnabled) {
76             return true;
77         }
78 
79         final int enabledValidNonAuxAsciiCapableImeCount =
80                 getEnabledValidNonAuxAsciiCapableImeCount();
81 
82         return enabledValidNonAuxAsciiCapableImeCount <= 1
83                 && !(enabledValidNonAuxAsciiCapableImeCount == 1 && !isEnabled)
84                 && imi.isSystem()
85                 && InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(imi);
86     }
87 
getEnabledValidNonAuxAsciiCapableImeCount()88     private int getEnabledValidNonAuxAsciiCapableImeCount() {
89         int count = 0;
90         final List<InputMethodInfo> enabledImis = getEnabledInputMethodList();
91         for (final InputMethodInfo imi : enabledImis) {
92             if (InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(imi)) {
93                 ++count;
94             }
95         }
96         if (count == 0) {
97             Log.w(TAG, "No \"enabledValidNonAuxAsciiCapableIme\"s found.");
98         }
99         return count;
100     }
101 
isEnabledImi(InputMethodInfo imi)102     public boolean isEnabledImi(InputMethodInfo imi) {
103         final List<InputMethodInfo> enabledImis = getEnabledInputMethodList();
104         for (final InputMethodInfo tempImi : enabledImis) {
105             if (tempImi.getId().equals(imi.getId())) {
106                 return true;
107             }
108         }
109         return false;
110     }
111 
112     /**
113      * Returns the list of the enabled {@link InputMethodInfo} determined by
114      * {@link android.provider.Settings.Secure#ENABLED_INPUT_METHODS} rather than just returning
115      * {@link InputMethodManager#getEnabledInputMethodList()}.
116      *
117      * @return the list of the enabled {@link InputMethodInfo}
118      */
getEnabledInputMethodList()119     private ArrayList<InputMethodInfo> getEnabledInputMethodList() {
120         final HashMap<String, HashSet<String>> enabledInputMethodsAndSubtypes =
121                 InputMethodAndSubtypeUtil.getEnabledInputMethodsAndSubtypeList(mContentResolver);
122         final ArrayList<InputMethodInfo> result = new ArrayList<>();
123         for (InputMethodInfo imi : mMethodList) {
124             if (enabledInputMethodsAndSubtypes.containsKey(imi.getId())) {
125                 result.add(imi);
126             }
127         }
128         return result;
129     }
130 }