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