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 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 
     58     public KeyboardLayoutDialogFragment() {
     59     }
     60 
     61     public KeyboardLayoutDialogFragment(String inputDeviceDescriptor) {
     62         mInputDeviceDescriptor = inputDeviceDescriptor;
     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             mInputDeviceDescriptor = savedInstanceState.getString(KEY_INPUT_DEVICE_DESCRIPTOR);
     80         }
     81 
     82         getLoaderManager().initLoader(0, null, this);
     83     }
     84 
     85     @Override
     86     public void onSaveInstanceState(Bundle outState) {
     87         super.onSaveInstanceState(outState);
     88         outState.putString(KEY_INPUT_DEVICE_DESCRIPTOR, mInputDeviceDescriptor);
     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 = mIm.getInputDeviceByDescriptor(mInputDeviceDescriptor);
    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                 mInputDeviceDescriptor);
    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(mInputDeviceDescriptor,
    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(), mInputDeviceDescriptor);
    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 String mInputDeviceDescriptor;
    293 
    294         public KeyboardLayoutLoader(Context context, String inputDeviceDescriptor) {
    295             super(context);
    296             mInputDeviceDescriptor = inputDeviceDescriptor;
    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                     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
    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(String inputDeviceDescriptor);
    353     }
    354 }