KeyboardLayoutDialogFragment.java revision 39b467482d1bf256a111c757e9b7621c6f523271
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 android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.app.DialogFragment;
23import android.app.LoaderManager.LoaderCallbacks;
24import android.content.AsyncTaskLoader;
25import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.Loader;
29import android.hardware.input.InputDeviceIdentifier;
30import android.hardware.input.InputManager;
31import android.hardware.input.InputManager.InputDeviceListener;
32import android.hardware.input.KeyboardLayout;
33import android.os.Bundle;
34import android.view.InputDevice;
35import android.view.LayoutInflater;
36import android.view.View;
37import android.view.ViewGroup;
38import android.widget.ArrayAdapter;
39import android.widget.CheckedTextView;
40import android.widget.RadioButton;
41import android.widget.TextView;
42
43import com.android.settings.R;
44
45import java.util.ArrayList;
46import java.util.Collections;
47
48public 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
57    public KeyboardLayoutDialogFragment() {
58    }
59
60    public KeyboardLayoutDialogFragment(InputDeviceIdentifier inputDeviceIdentifier) {
61        mInputDeviceIdentifier = inputDeviceIdentifier;
62    }
63
64    @Override
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
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
85    public void onSaveInstanceState(Bundle outState) {
86        super.onSaveInstanceState(outState);
87        outState.putParcelable(KEY_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
88    }
89
90    @Override
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
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
131    public void onPause() {
132        mIm.unregisterInputDeviceListener(this);
133        mInputDeviceId = -1;
134
135        super.onPause();
136    }
137
138    @Override
139    public void onCancel(DialogInterface dialog) {
140        super.onCancel(dialog);
141        dismiss();
142    }
143
144    private void onSetupLayoutsButtonClicked() {
145        ((OnSetupKeyboardLayoutsListener)getTargetFragment()).onSetupKeyboardLayouts(
146                mInputDeviceIdentifier);
147    }
148
149    @Override
150    public void onActivityResult(int requestCode, int resultCode, Intent data) {
151        super.onActivityResult(requestCode, resultCode, data);
152        show(getActivity().getFragmentManager(), "layout");
153    }
154
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
167    public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
168        return new KeyboardLayoutLoader(getActivity().getBaseContext(), mInputDeviceIdentifier);
169    }
170
171    @Override
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
184    public void onLoaderReset(Loader<Keyboards> loader) {
185        mAdapter.clear();
186        updateSwitchHintVisibility();
187    }
188
189    @Override
190    public void onInputDeviceAdded(int deviceId) {
191    }
192
193    @Override
194    public void onInputDeviceChanged(int deviceId) {
195        if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
196            getLoaderManager().restartLoader(0, null, this);
197        }
198    }
199
200    @Override
201    public void onInputDeviceRemoved(int deviceId) {
202        if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
203            dismiss();
204        }
205    }
206
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
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
224        public void setCheckedItem(int position) {
225            mCheckedItem = position;
226        }
227
228        @Override
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
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
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
282        private static boolean isTwoLine(View view) {
283            return view.getTag() == Boolean.TRUE;
284        }
285
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
294        public KeyboardLayoutLoader(Context context, InputDeviceIdentifier inputDeviceIdentifier) {
295            super(context);
296            mInputDeviceIdentifier = inputDeviceIdentifier;
297        }
298
299        @Override
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
334        protected void onStartLoading() {
335            super.onStartLoading();
336            forceLoad();
337        }
338
339        @Override
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 {
352        public void onSetupKeyboardLayouts(InputDeviceIdentifier mInputDeviceIdentifier);
353    }
354}