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