1 /*
2  * Copyright (C) 2019 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.car.developeroptions.inputmethod;
18 
19 import android.app.Activity;
20 import android.app.admin.DevicePolicyManager;
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.graphics.Color;
24 import android.graphics.drawable.ColorDrawable;
25 import android.graphics.drawable.Drawable;
26 import android.os.Bundle;
27 import android.provider.SearchIndexableResource;
28 import android.view.inputmethod.InputMethodInfo;
29 import android.view.inputmethod.InputMethodManager;
30 
31 import androidx.preference.Preference;
32 
33 import com.android.internal.util.Preconditions;
34 import com.android.car.developeroptions.R;
35 import com.android.car.developeroptions.SettingsPreferenceFragment;
36 import com.android.car.developeroptions.search.BaseSearchIndexProvider;
37 import com.android.car.developeroptions.search.Indexable;
38 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtilCompat;
39 import com.android.settingslib.inputmethod.InputMethodPreference;
40 import com.android.settingslib.search.SearchIndexable;
41 
42 import java.text.Collator;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 
47 @SearchIndexable
48 public final class VirtualKeyboardFragment extends SettingsPreferenceFragment implements Indexable {
49 
50     private static final String ADD_VIRTUAL_KEYBOARD_SCREEN = "add_virtual_keyboard_screen";
51     private static final Drawable NO_ICON = new ColorDrawable(Color.TRANSPARENT);
52 
53     private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
54     private InputMethodManager mImm;
55     private DevicePolicyManager mDpm;
56     private Preference mAddVirtualKeyboardScreen;
57 
58     @Override
onCreatePreferences(Bundle bundle, String s)59     public void onCreatePreferences(Bundle bundle, String s) {
60         Activity activity = Preconditions.checkNotNull(getActivity());
61         addPreferencesFromResource(R.xml.virtual_keyboard_settings);
62         mImm = Preconditions.checkNotNull(activity.getSystemService(InputMethodManager.class));
63         mDpm = Preconditions.checkNotNull(activity.getSystemService(DevicePolicyManager.class));
64         mAddVirtualKeyboardScreen = Preconditions.checkNotNull(
65                 findPreference(ADD_VIRTUAL_KEYBOARD_SCREEN));
66     }
67 
68     @Override
onResume()69     public void onResume() {
70         super.onResume();
71         // Refresh internal states in mInputMethodSettingValues to keep the latest
72         // "InputMethodInfo"s and "InputMethodSubtype"s
73         updateInputMethodPreferenceViews();
74     }
75 
76     @Override
getMetricsCategory()77     public int getMetricsCategory() {
78         return SettingsEnums.VIRTUAL_KEYBOARDS;
79     }
80 
updateInputMethodPreferenceViews()81     private void updateInputMethodPreferenceViews() {
82         // Clear existing "InputMethodPreference"s
83         mInputMethodPreferenceList.clear();
84         List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
85         final Context context = getPrefContext();
86         final List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
87         final int N = (imis == null ? 0 : imis.size());
88         for (int i = 0; i < N; ++i) {
89             final InputMethodInfo imi = imis.get(i);
90             final boolean isAllowedByOrganization = permittedList == null
91                     || permittedList.contains(imi.getPackageName());
92             Drawable icon;
93             try {
94                 // TODO: Consider other ways to retrieve an icon to show here.
95                 icon = getActivity().getPackageManager().getApplicationIcon(imi.getPackageName());
96             } catch (Exception e) {
97                 // TODO: Consider handling the error differently perhaps by showing default icons.
98                 icon = NO_ICON;
99             }
100             final InputMethodPreference pref = new InputMethodPreference(
101                     context,
102                     imi,
103                     false,  /* isImeEnabler */
104                     isAllowedByOrganization,
105                     null  /* this can be null since isImeEnabler is false */);
106             pref.setIcon(icon);
107             mInputMethodPreferenceList.add(pref);
108         }
109         final Collator collator = Collator.getInstance();
110         mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator));
111         getPreferenceScreen().removeAll();
112         for (int i = 0; i < N; ++i) {
113             final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
114             pref.setOrder(i);
115             getPreferenceScreen().addPreference(pref);
116             InputMethodAndSubtypeUtilCompat.removeUnnecessaryNonPersistentPreference(pref);
117             pref.updatePreferenceViews();
118         }
119         mAddVirtualKeyboardScreen.setIcon(R.drawable.ic_add_24dp);
120         mAddVirtualKeyboardScreen.setOrder(N);
121         getPreferenceScreen().addPreference(mAddVirtualKeyboardScreen);
122     }
123 
124     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
125             new BaseSearchIndexProvider() {
126                 @Override
127                 public List<SearchIndexableResource> getXmlResourcesToIndex(
128                         Context context, boolean enabled) {
129                     final SearchIndexableResource sir = new SearchIndexableResource(context);
130                     sir.xmlResId = R.xml.virtual_keyboard_settings;
131                     return Arrays.asList(sir);
132                 }
133             };
134 }
135