• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.settings.inputmethod;
18 
19 import com.android.settings.R;
20 
21 import android.app.AlertDialog;
22 import android.app.Activity;
23 import android.app.Dialog;
24 import android.app.DialogFragment;
25 import android.app.LoaderManager.LoaderCallbacks;
26 import android.content.AsyncTaskLoader;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.Loader;
31 import android.hardware.input.InputDeviceIdentifier;
32 import android.hardware.input.InputManager;
33 import android.hardware.input.KeyboardLayout;
34 import android.hardware.input.InputManager.InputDeviceListener;
35 import android.os.Bundle;
36 import android.view.InputDevice;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.widget.ArrayAdapter;
41 import android.widget.CheckedTextView;
42 import android.widget.RadioButton;
43 import android.widget.TextView;
44 
45 import java.util.ArrayList;
46 import java.util.Collections;
47 
48 public class KeyboardLayoutDialogFragment extends DialogFragment
49         implements InputDeviceListener, LoaderCallbacks<KeyboardLayoutDialogFragment.Keyboards> {
50     private static final String KEY_INPUT_DEVICE_IDENTIFIER = "inputDeviceIdentifier";
51 
52     private InputDeviceIdentifier mInputDeviceIdentifier;
53     private int mInputDeviceId = -1;
54     private InputManager mIm;
55     private KeyboardLayoutAdapter mAdapter;
56 
KeyboardLayoutDialogFragment()57     public KeyboardLayoutDialogFragment() {
58     }
59 
KeyboardLayoutDialogFragment(InputDeviceIdentifier inputDeviceIdentifier)60     public KeyboardLayoutDialogFragment(InputDeviceIdentifier inputDeviceIdentifier) {
61         mInputDeviceIdentifier = inputDeviceIdentifier;
62     }
63 
64     @Override
onAttach(Activity activity)65     public void onAttach(Activity activity) {
66         super.onAttach(activity);
67 
68         Context context = activity.getBaseContext();
69         mIm = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
70         mAdapter = new KeyboardLayoutAdapter(context);
71     }
72 
73     @Override
onCreate(Bundle savedInstanceState)74     public void onCreate(Bundle savedInstanceState) {
75         super.onCreate(savedInstanceState);
76 
77         if (savedInstanceState != null) {
78             mInputDeviceIdentifier = savedInstanceState.getParcelable(KEY_INPUT_DEVICE_IDENTIFIER);
79         }
80 
81         getLoaderManager().initLoader(0, null, this);
82     }
83 
84     @Override
onSaveInstanceState(Bundle outState)85     public void onSaveInstanceState(Bundle outState) {
86         super.onSaveInstanceState(outState);
87         outState.putParcelable(KEY_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
88     }
89 
90     @Override
onCreateDialog(Bundle savedInstanceState)91     public Dialog onCreateDialog(Bundle savedInstanceState) {
92         Context context = getActivity();
93         LayoutInflater inflater = LayoutInflater.from(context);
94         AlertDialog.Builder builder = new AlertDialog.Builder(context)
95             .setTitle(R.string.keyboard_layout_dialog_title)
96             .setPositiveButton(R.string.keyboard_layout_dialog_setup_button,
97                     new DialogInterface.OnClickListener() {
98                         @Override
99                         public void onClick(DialogInterface dialog, int which) {
100                             onSetupLayoutsButtonClicked();
101                         }
102                     })
103             .setSingleChoiceItems(mAdapter, -1,
104                     new DialogInterface.OnClickListener() {
105                         @Override
106                         public void onClick(DialogInterface dialog, int which) {
107                             onKeyboardLayoutClicked(which);
108                         }
109                     })
110             .setView(inflater.inflate(R.layout.keyboard_layout_dialog_switch_hint, null));
111         updateSwitchHintVisibility();
112         return builder.create();
113     }
114 
115     @Override
onResume()116     public void onResume() {
117         super.onResume();
118 
119         mIm.registerInputDeviceListener(this, null);
120 
121         InputDevice inputDevice =
122                 mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
123         if (inputDevice == null) {
124             dismiss();
125             return;
126         }
127         mInputDeviceId = inputDevice.getId();
128     }
129 
130     @Override
onPause()131     public void onPause() {
132         mIm.unregisterInputDeviceListener(this);
133         mInputDeviceId = -1;
134 
135         super.onPause();
136     }
137 
138     @Override
onCancel(DialogInterface dialog)139     public void onCancel(DialogInterface dialog) {
140         super.onCancel(dialog);
141         dismiss();
142     }
143 
onSetupLayoutsButtonClicked()144     private void onSetupLayoutsButtonClicked() {
145         ((OnSetupKeyboardLayoutsListener)getTargetFragment()).onSetupKeyboardLayouts(
146                 mInputDeviceIdentifier);
147     }
148 
149     @Override
onActivityResult(int requestCode, int resultCode, Intent data)150     public void onActivityResult(int requestCode, int resultCode, Intent data) {
151         super.onActivityResult(requestCode, resultCode, data);
152         show(getActivity().getFragmentManager(), "layout");
153     }
154 
onKeyboardLayoutClicked(int which)155     private void onKeyboardLayoutClicked(int which) {
156         if (which >= 0 && which < mAdapter.getCount()) {
157             KeyboardLayout keyboardLayout = mAdapter.getItem(which);
158             if (keyboardLayout != null) {
159                 mIm.setCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
160                         keyboardLayout.getDescriptor());
161             }
162             dismiss();
163         }
164     }
165 
166     @Override
onCreateLoader(int id, Bundle args)167     public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
168         return new KeyboardLayoutLoader(getActivity().getBaseContext(), mInputDeviceIdentifier);
169     }
170 
171     @Override
onLoadFinished(Loader<Keyboards> loader, Keyboards data)172     public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
173         mAdapter.clear();
174         mAdapter.addAll(data.keyboardLayouts);
175         mAdapter.setCheckedItem(data.current);
176         AlertDialog dialog = (AlertDialog)getDialog();
177         if (dialog != null) {
178             dialog.getListView().setItemChecked(data.current, true);
179         }
180         updateSwitchHintVisibility();
181     }
182 
183     @Override
onLoaderReset(Loader<Keyboards> loader)184     public void onLoaderReset(Loader<Keyboards> loader) {
185         mAdapter.clear();
186         updateSwitchHintVisibility();
187     }
188 
189     @Override
onInputDeviceAdded(int deviceId)190     public void onInputDeviceAdded(int deviceId) {
191     }
192 
193     @Override
onInputDeviceChanged(int deviceId)194     public void onInputDeviceChanged(int deviceId) {
195         if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
196             getLoaderManager().restartLoader(0, null, this);
197         }
198     }
199 
200     @Override
onInputDeviceRemoved(int deviceId)201     public void onInputDeviceRemoved(int deviceId) {
202         if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
203             dismiss();
204         }
205     }
206 
updateSwitchHintVisibility()207     private void updateSwitchHintVisibility() {
208         AlertDialog dialog = (AlertDialog)getDialog();
209         if (dialog != null) {
210             View customPanel = dialog.findViewById(com.android.internal.R.id.customPanel);
211             customPanel.setVisibility(mAdapter.getCount() > 1 ? View.VISIBLE : View.GONE);
212         }
213     }
214 
215     private static final class KeyboardLayoutAdapter extends ArrayAdapter<KeyboardLayout> {
216         private final LayoutInflater mInflater;
217         private int mCheckedItem = -1;
218 
KeyboardLayoutAdapter(Context context)219         public KeyboardLayoutAdapter(Context context) {
220             super(context, com.android.internal.R.layout.simple_list_item_2_single_choice);
221             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
222         }
223 
setCheckedItem(int position)224         public void setCheckedItem(int position) {
225             mCheckedItem = position;
226         }
227 
228         @Override
getView(int position, View convertView, ViewGroup parent)229         public View getView(int position, View convertView, ViewGroup parent) {
230             KeyboardLayout item = getItem(position);
231             String label, collection;
232             if (item != null) {
233                 label = item.getLabel();
234                 collection = item.getCollection();
235             } else {
236                 label = getContext().getString(R.string.keyboard_layout_default_label);
237                 collection = "";
238             }
239 
240             boolean checked = (position == mCheckedItem);
241             if (collection.isEmpty()) {
242                 return inflateOneLine(convertView, parent, label, checked);
243             } else {
244                 return inflateTwoLine(convertView, parent, label, collection, checked);
245             }
246         }
247 
inflateOneLine(View convertView, ViewGroup parent, String label, boolean checked)248         private View inflateOneLine(View convertView, ViewGroup parent,
249                 String label, boolean checked) {
250             View view = convertView;
251             if (view == null || isTwoLine(view)) {
252                 view = mInflater.inflate(
253                         com.android.internal.R.layout.simple_list_item_single_choice,
254                         parent, false);
255                 setTwoLine(view, false);
256             }
257             CheckedTextView headline = (CheckedTextView) view.findViewById(android.R.id.text1);
258             headline.setText(label);
259             headline.setChecked(checked);
260             return view;
261         }
262 
inflateTwoLine(View convertView, ViewGroup parent, String label, String collection, boolean checked)263         private View inflateTwoLine(View convertView, ViewGroup parent,
264                 String label, String collection, boolean checked) {
265             View view = convertView;
266             if (view == null || !isTwoLine(view)) {
267                 view = mInflater.inflate(
268                         com.android.internal.R.layout.simple_list_item_2_single_choice,
269                         parent, false);
270                 setTwoLine(view, true);
271             }
272             TextView headline = (TextView) view.findViewById(android.R.id.text1);
273             TextView subText = (TextView) view.findViewById(android.R.id.text2);
274             RadioButton radioButton =
275                     (RadioButton)view.findViewById(com.android.internal.R.id.radio);
276             headline.setText(label);
277             subText.setText(collection);
278             radioButton.setChecked(checked);
279             return view;
280         }
281 
isTwoLine(View view)282         private static boolean isTwoLine(View view) {
283             return view.getTag() == Boolean.TRUE;
284         }
285 
setTwoLine(View view, boolean twoLine)286         private static void setTwoLine(View view, boolean twoLine) {
287             view.setTag(Boolean.valueOf(twoLine));
288         }
289     }
290 
291     private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
292         private final InputDeviceIdentifier mInputDeviceIdentifier;
293 
KeyboardLayoutLoader(Context context, InputDeviceIdentifier inputDeviceIdentifier)294         public KeyboardLayoutLoader(Context context, InputDeviceIdentifier inputDeviceIdentifier) {
295             super(context);
296             mInputDeviceIdentifier = inputDeviceIdentifier;
297         }
298 
299         @Override
loadInBackground()300         public Keyboards loadInBackground() {
301             Keyboards keyboards = new Keyboards();
302             InputManager im = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
303             String[] keyboardLayoutDescriptors = im.getKeyboardLayoutsForInputDevice(
304                     mInputDeviceIdentifier);
305             for (String keyboardLayoutDescriptor : keyboardLayoutDescriptors) {
306                 KeyboardLayout keyboardLayout = im.getKeyboardLayout(keyboardLayoutDescriptor);
307                 if (keyboardLayout != null) {
308                     keyboards.keyboardLayouts.add(keyboardLayout);
309                 }
310             }
311             Collections.sort(keyboards.keyboardLayouts);
312 
313             String currentKeyboardLayoutDescriptor =
314                     im.getCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier);
315             if (currentKeyboardLayoutDescriptor != null) {
316                 final int numKeyboardLayouts = keyboards.keyboardLayouts.size();
317                 for (int i = 0; i < numKeyboardLayouts; i++) {
318                     if (keyboards.keyboardLayouts.get(i).getDescriptor().equals(
319                             currentKeyboardLayoutDescriptor)) {
320                         keyboards.current = i;
321                         break;
322                     }
323                 }
324             }
325 
326             if (keyboards.keyboardLayouts.isEmpty()) {
327                 keyboards.keyboardLayouts.add(null); // default layout
328                 keyboards.current = 0;
329             }
330             return keyboards;
331         }
332 
333         @Override
onStartLoading()334         protected void onStartLoading() {
335             super.onStartLoading();
336             forceLoad();
337         }
338 
339         @Override
onStopLoading()340         protected void onStopLoading() {
341             super.onStopLoading();
342             cancelLoad();
343         }
344     }
345 
346     public static final class Keyboards {
347         public final ArrayList<KeyboardLayout> keyboardLayouts = new ArrayList<KeyboardLayout>();
348         public int current = -1;
349     }
350 
351     public interface OnSetupKeyboardLayoutsListener {
onSetupKeyboardLayouts(InputDeviceIdentifier mInputDeviceIdentifier)352         public void onSetupKeyboardLayouts(InputDeviceIdentifier mInputDeviceIdentifier);
353     }
354 }