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