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 }