• 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 import com.android.settings.Settings.KeyboardLayoutPickerActivity;
21 
22 import android.app.AlertDialog;
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.app.DialogFragment;
26 import android.app.LoaderManager.LoaderCallbacks;
27 import android.content.AsyncTaskLoader;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.content.Loader;
32 import android.content.res.Resources;
33 import android.hardware.input.InputManager;
34 import android.hardware.input.KeyboardLayout;
35 import android.hardware.input.InputManager.InputDeviceListener;
36 import android.os.Bundle;
37 import android.view.InputDevice;
38 import android.view.LayoutInflater;
39 import android.view.View;
40 import android.view.ViewGroup;
41 import android.widget.ArrayAdapter;
42 import android.widget.CheckedTextView;
43 import android.widget.RadioButton;
44 import android.widget.TextView;
45 
46 import java.util.ArrayList;
47 import java.util.Collections;
48 
49 public class KeyboardLayoutDialogFragment extends DialogFragment
50         implements InputDeviceListener, LoaderCallbacks<KeyboardLayoutDialogFragment.Keyboards> {
51     private static final String KEY_INPUT_DEVICE_DESCRIPTOR = "inputDeviceDescriptor";
52 
53     private String mInputDeviceDescriptor;
54     private int mInputDeviceId = -1;
55     private InputManager mIm;
56     private KeyboardLayoutAdapter mAdapter;
57 
KeyboardLayoutDialogFragment()58     public KeyboardLayoutDialogFragment() {
59     }
60 
KeyboardLayoutDialogFragment(String inputDeviceDescriptor)61     public KeyboardLayoutDialogFragment(String inputDeviceDescriptor) {
62         mInputDeviceDescriptor = inputDeviceDescriptor;
63     }
64 
65     @Override
onAttach(Activity activity)66     public void onAttach(Activity activity) {
67         super.onAttach(activity);
68 
69         Context context = activity.getBaseContext();
70         mIm = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
71         mAdapter = new KeyboardLayoutAdapter(context);
72     }
73 
74     @Override
onCreate(Bundle savedInstanceState)75     public void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77 
78         if (savedInstanceState != null) {
79             mInputDeviceDescriptor = savedInstanceState.getString(KEY_INPUT_DEVICE_DESCRIPTOR);
80         }
81 
82         getLoaderManager().initLoader(0, null, this);
83     }
84 
85     @Override
onSaveInstanceState(Bundle outState)86     public void onSaveInstanceState(Bundle outState) {
87         super.onSaveInstanceState(outState);
88         outState.putString(KEY_INPUT_DEVICE_DESCRIPTOR, mInputDeviceDescriptor);
89     }
90 
91     @Override
onCreateDialog(Bundle savedInstanceState)92     public Dialog onCreateDialog(Bundle savedInstanceState) {
93         Context context = getActivity();
94         LayoutInflater inflater = LayoutInflater.from(context);
95         AlertDialog.Builder builder = new AlertDialog.Builder(context)
96             .setTitle(R.string.keyboard_layout_dialog_title)
97             .setPositiveButton(R.string.keyboard_layout_dialog_setup_button,
98                     new DialogInterface.OnClickListener() {
99                         @Override
100                         public void onClick(DialogInterface dialog, int which) {
101                             onSetupLayoutsButtonClicked();
102                         }
103                     })
104             .setSingleChoiceItems(mAdapter, -1,
105                     new DialogInterface.OnClickListener() {
106                         @Override
107                         public void onClick(DialogInterface dialog, int which) {
108                             onKeyboardLayoutClicked(which);
109                         }
110                     })
111             .setView(inflater.inflate(R.layout.keyboard_layout_dialog_switch_hint, null));
112         updateSwitchHintVisibility();
113         return builder.create();
114     }
115 
116     @Override
onResume()117     public void onResume() {
118         super.onResume();
119 
120         mIm.registerInputDeviceListener(this, null);
121 
122         InputDevice inputDevice = mIm.getInputDeviceByDescriptor(mInputDeviceDescriptor);
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                 mInputDeviceDescriptor);
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(mInputDeviceDescriptor,
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(), mInputDeviceDescriptor);
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 String mInputDeviceDescriptor;
293 
KeyboardLayoutLoader(Context context, String inputDeviceDescriptor)294         public KeyboardLayoutLoader(Context context, String inputDeviceDescriptor) {
295             super(context);
296             mInputDeviceDescriptor = inputDeviceDescriptor;
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                     mInputDeviceDescriptor);
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(mInputDeviceDescriptor);
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(String inputDeviceDescriptor)352         public void onSetupKeyboardLayouts(String inputDeviceDescriptor);
353     }
354 }