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.settings.inputmethod;
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.car.drivingstate.CarUxRestrictions;
21 import android.content.ActivityNotFoundException;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.PackageManager;
25 import android.util.ArrayMap;
26 import android.view.inputmethod.InputMethodInfo;
27 import android.view.inputmethod.InputMethodManager;
28 
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.Preference;
31 import androidx.preference.PreferenceGroup;
32 
33 import com.android.car.settings.common.FragmentController;
34 import com.android.car.settings.common.Logger;
35 import com.android.car.settings.common.PreferenceController;
36 import com.android.car.ui.preference.CarUiPreference;
37 
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.HashSet;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Objects;
45 import java.util.Set;
46 
47 /** Updates the enabled keyboard list. */
48 public class EnabledKeyboardPreferenceController extends
49         PreferenceController<PreferenceGroup> {
50     private static final Logger LOG = new Logger(EnabledKeyboardPreferenceController.class);
51 
52     private final Map<String, Preference> mPreferences = new ArrayMap<>();
53     private final InputMethodManager mInputMethodManager;
54     private final DevicePolicyManager mDevicePolicyManager;
55     private final PackageManager mPackageManager;
56 
EnabledKeyboardPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)57     public EnabledKeyboardPreferenceController(Context context, String preferenceKey,
58             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
59         super(context, preferenceKey, fragmentController, uxRestrictions);
60         mPackageManager = context.getPackageManager();
61         mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
62         mInputMethodManager = context.getSystemService(InputMethodManager.class);
63     }
64 
65     @Override
getPreferenceType()66     protected Class<PreferenceGroup> getPreferenceType() {
67         return PreferenceGroup.class;
68     }
69 
70     @Override
updateState(PreferenceGroup preferenceGroup)71     protected void updateState(PreferenceGroup preferenceGroup) {
72         List<Preference> preferencesToDisplay = new ArrayList<>();
73         Set<String> preferencesToRemove = new HashSet<>(mPreferences.keySet());
74         List<InputMethodInfo> inputMethodInfos =
75                 InputMethodUtil.getPermittedAndEnabledInputMethodList(
76                     mInputMethodManager, mDevicePolicyManager);
77         int size = (inputMethodInfos == null) ? 0 : inputMethodInfos.size();
78         for (int i = 0; i < size; ++i) {
79             InputMethodInfo inputMethodInfo = inputMethodInfos.get(i);
80 
81             Preference preference = createPreference(inputMethodInfo);
82             if (mPreferences.containsKey(preference.getKey())) {
83                 Preference displayedPreference = mPreferences.get(preference.getKey());
84                 if (arePreferencesDifferent(displayedPreference, preference)) {
85                     preferencesToDisplay.add(preference);
86                 } else {
87                     preferencesToRemove.remove(preference.getKey());
88                 }
89             } else {
90                 preferencesToDisplay.add(preference);
91             }
92         }
93         updatePreferenceGroup(preferenceGroup, preferencesToDisplay, preferencesToRemove);
94     }
95 
updatePreferenceGroup( PreferenceGroup preferenceGroup, List<Preference> preferencesToDisplay, Set<String> preferencesToRemove)96     private void updatePreferenceGroup(
97             PreferenceGroup preferenceGroup, List<Preference> preferencesToDisplay,
98             Set<String> preferencesToRemove) {
99         Collections.sort(preferencesToDisplay, Comparator.comparing(
100                 (Preference a) -> a.getTitle().toString())
101                 .thenComparing((Preference a) -> a.getSummary().toString()));
102 
103         for (String key : preferencesToRemove) {
104             preferenceGroup.removePreference(mPreferences.get(key));
105             mPreferences.remove(key);
106         }
107 
108         for (int i = 0; i < preferencesToDisplay.size(); ++i) {
109             Preference pref = preferencesToDisplay.get(i);
110             pref.setOrder(i);
111             mPreferences.put(pref.getKey(), pref);
112             preferenceGroup.addPreference(pref);
113         }
114     }
115 
116     /**
117      * Creates a preference.
118      */
createPreference(InputMethodInfo inputMethodInfo)119     private Preference createPreference(InputMethodInfo inputMethodInfo) {
120         CarUiPreference preference = new CarUiPreference(getContext());
121         preference.setKey(String.valueOf(inputMethodInfo.hashCode()));
122         preference.setIcon(InputMethodUtil.getPackageIcon(mPackageManager, inputMethodInfo));
123         preference.setTitle(InputMethodUtil.getPackageLabel(mPackageManager, inputMethodInfo));
124         preference.setSummary(InputMethodUtil.getSummaryString(
125                 getContext(), mInputMethodManager, inputMethodInfo));
126         preference.setOnPreferenceClickListener(pref -> {
127             try {
128                 String settingsActivity = inputMethodInfo.getSettingsActivity();
129                 if (settingsActivity == null) {
130                     LOG.d("IME's Settings Activity not defined.");
131                     return true;
132                 }
133 
134                 Intent intent = createInputMethodSettingsIntent(inputMethodInfo.getPackageName(),
135                         settingsActivity);
136 
137                 // Invoke a settings activity of an input method.
138                 getContext().startActivity(intent);
139             } catch (final ActivityNotFoundException e) {
140                 LOG.d("IME's Settings Activity Not Found. " + e);
141             }
142             return true;
143         });
144         return preference;
145     }
146 
147     @VisibleForTesting
createInputMethodSettingsIntent(String packageName, String settingsActivity)148     Intent createInputMethodSettingsIntent(String packageName, String settingsActivity) {
149         Intent intent = new Intent(Intent.ACTION_MAIN);
150         intent.setClassName(packageName, settingsActivity);
151         return intent;
152     }
153 
arePreferencesDifferent(Preference lhs, Preference rhs)154     private boolean arePreferencesDifferent(Preference lhs, Preference rhs) {
155         return !Objects.equals(lhs.getTitle(), rhs.getTitle())
156                 || !Objects.equals(lhs.getSummary(), rhs.getSummary());
157     }
158 }
159