1 /*
2  * Copyright (C) 2015 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.system;
18 
19 import android.app.ActivityManagerNative;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.os.Bundle;
24 import android.os.RemoteException;
25 import android.os.UserHandle;
26 import android.provider.Settings;
27 import android.support.v17.preference.LeanbackPreferenceFragment;
28 import android.support.v7.preference.ListPreference;
29 import android.support.v7.preference.Preference;
30 import android.support.v7.preference.PreferenceScreen;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.inputmethod.InputMethodInfo;
34 import android.view.inputmethod.InputMethodManager;
35 
36 import com.android.tv.settings.R;
37 
38 import java.util.ArrayList;
39 import java.util.Iterator;
40 import java.util.List;
41 
42 public class KeyboardFragment extends LeanbackPreferenceFragment {
43     private static final String TAG = "KeyboardFragment";
44     private static final String INPUT_METHOD_SEPARATOR = ":";
45     private static final String KEY_CURRENT_KEYBOARD = "currentKeyboard";
46 
47     private InputMethodManager mInputMethodManager;
48 
newInstance()49     public static KeyboardFragment newInstance() {
50         return new KeyboardFragment();
51     }
52 
53     @Override
onCreate(Bundle savedInstanceState)54     public void onCreate(Bundle savedInstanceState) {
55         mInputMethodManager =
56                 (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
57 
58         enableAllInputMethods();
59 
60         super.onCreate(savedInstanceState);
61     }
62 
63     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)64     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
65         final Context preferenceContext = getPreferenceManager().getContext();
66         final PackageManager packageManager = getActivity().getPackageManager();
67 
68         final PreferenceScreen screen =
69                 getPreferenceManager().createPreferenceScreen(preferenceContext);
70         screen.setTitle(R.string.system_keyboard);
71 
72         List<InputMethodInfo> enabledInputMethodInfos = getEnabledSystemInputMethodList();
73 
74         final List<CharSequence> entries = new ArrayList<>(enabledInputMethodInfos.size());
75         final List<CharSequence> values = new ArrayList<>(enabledInputMethodInfos.size());
76 
77         int defaultIndex = 0;
78         final String defaultId = Settings.Secure.getString(getActivity().getContentResolver(),
79                 Settings.Secure.DEFAULT_INPUT_METHOD);
80 
81         for (final InputMethodInfo info : enabledInputMethodInfos) {
82             entries.add(info.loadLabel(packageManager));
83             final String id = info.getId();
84             values.add(id);
85             if (TextUtils.equals(id, defaultId)) {
86                 defaultIndex = values.size() - 1;
87             }
88         }
89 
90         final ListPreference currentKeyboard = new ListPreference(preferenceContext);
91         currentKeyboard.setPersistent(false);
92         currentKeyboard.setTitle(R.string.title_current_keyboard);
93         currentKeyboard.setDialogTitle(R.string.title_current_keyboard);
94         currentKeyboard.setSummary("%s");
95         currentKeyboard.setKey(KEY_CURRENT_KEYBOARD);
96         currentKeyboard.setEntries(entries.toArray(new CharSequence[entries.size()]));
97         currentKeyboard.setEntryValues(values.toArray(new CharSequence[values.size()]));
98         currentKeyboard.setValueIndex(defaultIndex);
99         currentKeyboard.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
100             @Override
101             public boolean onPreferenceChange(Preference preference, Object newValue) {
102                 setInputMethod((String) newValue);
103                 return true;
104             }
105         });
106         screen.addPreference(currentKeyboard);
107 
108         // Add per-IME settings
109         for (final InputMethodInfo info : enabledInputMethodInfos) {
110             final Intent settingsIntent = getInputMethodSettingsIntent(info);
111             if (settingsIntent == null) {
112                 continue;
113             }
114             final Preference preference = new Preference(preferenceContext);
115             preference.setTitle(info.loadLabel(packageManager));
116             preference.setKey("keyboardSettings:" + info.getId());
117             preference.setIntent(settingsIntent);
118             screen.addPreference(preference);
119         }
120         setPreferenceScreen(screen);
121     }
122 
123 
124 
setInputMethod(String imid)125     private void setInputMethod(String imid) {
126         if (imid == null) {
127             throw new IllegalArgumentException("Null ID");
128         }
129 
130         int userId;
131         try {
132             userId = ActivityManagerNative.getDefault().getCurrentUser().id;
133             Settings.Secure.putStringForUser(getActivity().getContentResolver(),
134                     Settings.Secure.DEFAULT_INPUT_METHOD, imid, userId);
135 
136             if (ActivityManagerNative.isSystemReady()) {
137                 Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
138                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
139                 intent.putExtra("input_method_id", imid);
140                 getActivity().sendBroadcastAsUser(intent, UserHandle.CURRENT);
141             }
142         } catch (RemoteException e) {
143             Log.d(TAG, "set default input method remote exception");
144         }
145     }
146 
getEnabledSystemInputMethodList()147     private List<InputMethodInfo> getEnabledSystemInputMethodList() {
148         List<InputMethodInfo> enabledInputMethodInfos =
149                 new ArrayList<>(mInputMethodManager.getEnabledInputMethodList());
150         // Filter auxiliary keyboards out
151         for (Iterator<InputMethodInfo> it = enabledInputMethodInfos.iterator(); it.hasNext();) {
152             if (it.next().isAuxiliaryIme()) {
153                 it.remove();
154             }
155         }
156         return enabledInputMethodInfos;
157     }
158 
getInputMethodSettingsIntent(InputMethodInfo imi)159     private Intent getInputMethodSettingsIntent(InputMethodInfo imi) {
160         final Intent intent;
161         final String settingsActivity = imi.getSettingsActivity();
162         if (!TextUtils.isEmpty(settingsActivity)) {
163             intent = new Intent(Intent.ACTION_MAIN);
164             intent.setClassName(imi.getPackageName(), settingsActivity);
165         } else {
166             intent = null;
167         }
168         return intent;
169     }
170 
enableAllInputMethods()171     private void enableAllInputMethods() {
172         List<InputMethodInfo> allInputMethodInfos =
173                 new ArrayList<>(mInputMethodManager.getInputMethodList());
174         boolean needAppendSeparator = false;
175         StringBuilder builder = new StringBuilder();
176         for (InputMethodInfo imi : allInputMethodInfos) {
177             if (needAppendSeparator) {
178                 builder.append(INPUT_METHOD_SEPARATOR);
179             } else {
180                 needAppendSeparator = true;
181             }
182             builder.append(imi.getId());
183         }
184         Settings.Secure.putString(getActivity().getContentResolver(),
185                 Settings.Secure.ENABLED_INPUT_METHODS, builder.toString());
186     }
187 
188 }
189