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 android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.app.Dialog;
     22 import android.app.DialogFragment;
     23 import android.app.LoaderManager.LoaderCallbacks;
     24 import android.content.AsyncTaskLoader;
     25 import android.content.Context;
     26 import android.content.DialogInterface;
     27 import android.content.Intent;
     28 import android.content.Loader;
     29 import android.hardware.input.InputDeviceIdentifier;
     30 import android.hardware.input.InputManager;
     31 import android.hardware.input.InputManager.InputDeviceListener;
     32 import android.hardware.input.KeyboardLayout;
     33 import android.os.Bundle;
     34 import android.view.InputDevice;
     35 import android.view.LayoutInflater;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.widget.ArrayAdapter;
     39 import android.widget.CheckedTextView;
     40 import android.widget.RadioButton;
     41 import android.widget.TextView;
     42 
     43 import com.android.settings.R;
     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     private boolean mHasShownLayoutSelectionScreen;
     57 
     58     public KeyboardLayoutDialogFragment() {
     59     }
     60 
     61     public KeyboardLayoutDialogFragment(InputDeviceIdentifier inputDeviceIdentifier) {
     62         mInputDeviceIdentifier = inputDeviceIdentifier;
     63     }
     64 
     65     @Override
     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
     75     public void onCreate(Bundle savedInstanceState) {
     76         super.onCreate(savedInstanceState);
     77 
     78         if (savedInstanceState != null) {
     79             mInputDeviceIdentifier = savedInstanceState.getParcelable(KEY_INPUT_DEVICE_IDENTIFIER);
     80         }
     81 
     82         getLoaderManager().initLoader(0, null, this);
     83     }
     84 
     85     @Override
     86     public void onSaveInstanceState(Bundle outState) {
     87         super.onSaveInstanceState(outState);
     88         outState.putParcelable(KEY_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
     89     }
     90 
     91     @Override
     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
    117     public void onResume() {
    118         super.onResume();
    119 
    120         mIm.registerInputDeviceListener(this, null);
    121 
    122         InputDevice inputDevice =
    123                 mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
    124         if (inputDevice == null) {
    125             dismiss();
    126             return;
    127         }
    128         mInputDeviceId = inputDevice.getId();
    129     }
    130 
    131     @Override
    132     public void onPause() {
    133         mIm.unregisterInputDeviceListener(this);
    134         mInputDeviceId = -1;
    135 
    136         super.onPause();
    137     }
    138 
    139     @Override
    140     public void onCancel(DialogInterface dialog) {
    141         super.onCancel(dialog);
    142         dismiss();
    143     }
    144 
    145     private void onSetupLayoutsButtonClicked() {
    146         ((OnSetupKeyboardLayoutsListener)getTargetFragment()).onSetupKeyboardLayouts(
    147                 mInputDeviceIdentifier);
    148     }
    149 
    150     @Override
    151     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    152         super.onActivityResult(requestCode, resultCode, data);
    153         show(getActivity().getFragmentManager(), "layout");
    154     }
    155 
    156     private void onKeyboardLayoutClicked(int which) {
    157         if (which >= 0 && which < mAdapter.getCount()) {
    158             KeyboardLayout keyboardLayout = mAdapter.getItem(which);
    159             if (keyboardLayout != null) {
    160                 mIm.setCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
    161                         keyboardLayout.getDescriptor());
    162             }
    163             dismiss();
    164         }
    165     }
    166 
    167     @Override
    168     public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
    169         return new KeyboardLayoutLoader(getActivity().getBaseContext(), mInputDeviceIdentifier);
    170     }
    171 
    172     @Override
    173     public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
    174         mAdapter.clear();
    175         mAdapter.addAll(data.keyboardLayouts);
    176         mAdapter.setCheckedItem(data.current);
    177         AlertDialog dialog = (AlertDialog)getDialog();
    178         if (dialog != null) {
    179             dialog.getListView().setItemChecked(data.current, true);
    180         }
    181         updateSwitchHintVisibility();
    182         showSetupKeyboardLayoutsIfNecessary();
    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 void showSetupKeyboardLayoutsIfNecessary() {
    218         AlertDialog dialog = (AlertDialog)getDialog();
    219         if (dialog != null
    220                 && mAdapter.getCount() == 1 && mAdapter.getItem(0) == null
    221                 && !mHasShownLayoutSelectionScreen) {
    222             mHasShownLayoutSelectionScreen = true;
    223             ((OnSetupKeyboardLayoutsListener)getTargetFragment()).onSetupKeyboardLayouts(
    224                     mInputDeviceIdentifier);
    225         }
    226     }
    227 
    228     private static final class KeyboardLayoutAdapter extends ArrayAdapter<KeyboardLayout> {
    229         private final LayoutInflater mInflater;
    230         private int mCheckedItem = -1;
    231 
    232         public KeyboardLayoutAdapter(Context context) {
    233             super(context, com.android.internal.R.layout.simple_list_item_2_single_choice);
    234             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    235         }
    236 
    237         public void setCheckedItem(int position) {
    238             mCheckedItem = position;
    239         }
    240 
    241         @Override
    242         public View getView(int position, View convertView, ViewGroup parent) {
    243             KeyboardLayout item = getItem(position);
    244             String label, collection;
    245             if (item != null) {
    246                 label = item.getLabel();
    247                 collection = item.getCollection();
    248             } else {
    249                 label = getContext().getString(R.string.keyboard_layout_default_label);
    250                 collection = "";
    251             }
    252 
    253             boolean checked = (position == mCheckedItem);
    254             if (collection.isEmpty()) {
    255                 return inflateOneLine(convertView, parent, label, checked);
    256             } else {
    257                 return inflateTwoLine(convertView, parent, label, collection, checked);
    258             }
    259         }
    260 
    261         private View inflateOneLine(View convertView, ViewGroup parent,
    262                 String label, boolean checked) {
    263             View view = convertView;
    264             if (view == null || isTwoLine(view)) {
    265                 view = mInflater.inflate(
    266                         com.android.internal.R.layout.simple_list_item_single_choice,
    267                         parent, false);
    268                 setTwoLine(view, false);
    269             }
    270             CheckedTextView headline = (CheckedTextView) view.findViewById(android.R.id.text1);
    271             headline.setText(label);
    272             headline.setChecked(checked);
    273             return view;
    274         }
    275 
    276         private View inflateTwoLine(View convertView, ViewGroup parent,
    277                 String label, String collection, boolean checked) {
    278             View view = convertView;
    279             if (view == null || !isTwoLine(view)) {
    280                 view = mInflater.inflate(
    281                         com.android.internal.R.layout.simple_list_item_2_single_choice,
    282                         parent, false);
    283                 setTwoLine(view, true);
    284             }
    285             TextView headline = (TextView) view.findViewById(android.R.id.text1);
    286             TextView subText = (TextView) view.findViewById(android.R.id.text2);
    287             RadioButton radioButton =
    288                     (RadioButton)view.findViewById(com.android.internal.R.id.radio);
    289             headline.setText(label);
    290             subText.setText(collection);
    291             radioButton.setChecked(checked);
    292             return view;
    293         }
    294 
    295         private static boolean isTwoLine(View view) {
    296             return view.getTag() == Boolean.TRUE;
    297         }
    298 
    299         private static void setTwoLine(View view, boolean twoLine) {
    300             view.setTag(Boolean.valueOf(twoLine));
    301         }
    302     }
    303 
    304     private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
    305         private final InputDeviceIdentifier mInputDeviceIdentifier;
    306 
    307         public KeyboardLayoutLoader(Context context, InputDeviceIdentifier inputDeviceIdentifier) {
    308             super(context);
    309             mInputDeviceIdentifier = inputDeviceIdentifier;
    310         }
    311 
    312         @Override
    313         public Keyboards loadInBackground() {
    314             Keyboards keyboards = new Keyboards();
    315             InputManager im = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
    316             String[] keyboardLayoutDescriptors = im.getEnabledKeyboardLayoutsForInputDevice(
    317                     mInputDeviceIdentifier);
    318             for (String keyboardLayoutDescriptor : keyboardLayoutDescriptors) {
    319                 KeyboardLayout keyboardLayout = im.getKeyboardLayout(keyboardLayoutDescriptor);
    320                 if (keyboardLayout != null) {
    321                     keyboards.keyboardLayouts.add(keyboardLayout);
    322                 }
    323             }
    324             Collections.sort(keyboards.keyboardLayouts);
    325 
    326             String currentKeyboardLayoutDescriptor =
    327                     im.getCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier);
    328             if (currentKeyboardLayoutDescriptor != null) {
    329                 final int numKeyboardLayouts = keyboards.keyboardLayouts.size();
    330                 for (int i = 0; i < numKeyboardLayouts; i++) {
    331                     if (keyboards.keyboardLayouts.get(i).getDescriptor().equals(
    332                             currentKeyboardLayoutDescriptor)) {
    333                         keyboards.current = i;
    334                         break;
    335                     }
    336                 }
    337             }
    338 
    339             if (keyboards.keyboardLayouts.isEmpty()) {
    340                 keyboards.keyboardLayouts.add(null); // default layout
    341                 keyboards.current = 0;
    342             }
    343             return keyboards;
    344         }
    345 
    346         @Override
    347         protected void onStartLoading() {
    348             super.onStartLoading();
    349             forceLoad();
    350         }
    351 
    352         @Override
    353         protected void onStopLoading() {
    354             super.onStopLoading();
    355             cancelLoad();
    356         }
    357     }
    358 
    359     public static final class Keyboards {
    360         public final ArrayList<KeyboardLayout> keyboardLayouts = new ArrayList<KeyboardLayout>();
    361         public int current = -1;
    362     }
    363 
    364     public interface OnSetupKeyboardLayoutsListener {
    365         public void onSetupKeyboardLayouts(InputDeviceIdentifier mInputDeviceIdentifier);
    366     }
    367 }
    368