Home | History | Annotate | Download | only in inputmethod
      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 
     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 }