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.inputmethod.latin; 18 19 import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.ASCII_CAPABLE; 20 21 import android.app.AlertDialog; 22 import android.app.Dialog; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.SharedPreferences; 27 import android.content.res.Resources; 28 import android.os.Bundle; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.preference.DialogPreference; 32 import android.preference.Preference; 33 import android.preference.PreferenceFragment; 34 import android.preference.PreferenceGroup; 35 import android.util.Pair; 36 import android.view.Menu; 37 import android.view.MenuInflater; 38 import android.view.MenuItem; 39 import android.view.View; 40 import android.view.inputmethod.InputMethodInfo; 41 import android.view.inputmethod.InputMethodSubtype; 42 import android.widget.ArrayAdapter; 43 import android.widget.Spinner; 44 import android.widget.SpinnerAdapter; 45 import android.widget.Toast; 46 47 import java.util.ArrayList; 48 import java.util.TreeSet; 49 50 public final class AdditionalSubtypeSettings extends PreferenceFragment { 51 private RichInputMethodManager mRichImm; 52 private SharedPreferences mPrefs; 53 private SubtypeLocaleAdapter mSubtypeLocaleAdapter; 54 private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter; 55 56 private boolean mIsAddingNewSubtype; 57 private AlertDialog mSubtypeEnablerNotificationDialog; 58 private String mSubtypePreferenceKeyForSubtypeEnabler; 59 60 private static final int MENU_ADD_SUBTYPE = Menu.FIRST; 61 private static final String KEY_IS_ADDING_NEW_SUBTYPE = "is_adding_new_subtype"; 62 private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN = 63 "is_subtype_enabler_notification_dialog_open"; 64 private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler"; 65 66 static final class SubtypeLocaleItem extends Pair<String, String> 67 implements Comparable<SubtypeLocaleItem> { 68 public SubtypeLocaleItem(final String localeString, final String displayName) { 69 super(localeString, displayName); 70 } 71 72 public SubtypeLocaleItem(final String localeString) { 73 this(localeString, 74 SubtypeLocale.getSubtypeLocaleDisplayNameInSystemLocale(localeString)); 75 } 76 77 @Override 78 public String toString() { 79 return second; 80 } 81 82 @Override 83 public int compareTo(final SubtypeLocaleItem o) { 84 return first.compareTo(o.first); 85 } 86 } 87 88 static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> { 89 private static final String TAG = SubtypeLocaleAdapter.class.getSimpleName(); 90 private static final boolean DEBUG_SUBTYPE_ID = false; 91 92 public SubtypeLocaleAdapter(final Context context) { 93 super(context, android.R.layout.simple_spinner_item); 94 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 95 96 final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet(); 97 final InputMethodInfo imi = RichInputMethodManager.getInstance() 98 .getInputMethodInfoOfThisIme(); 99 final int count = imi.getSubtypeCount(); 100 for (int i = 0; i < count; i++) { 101 final InputMethodSubtype subtype = imi.getSubtypeAt(i); 102 if (DEBUG_SUBTYPE_ID) { 103 android.util.Log.d(TAG, String.format("%-6s 0x%08x %11d %s", 104 subtype.getLocale(), subtype.hashCode(), subtype.hashCode(), 105 SubtypeLocale.getSubtypeDisplayNameInSystemLocale(subtype))); 106 } 107 if (subtype.containsExtraValueKey(ASCII_CAPABLE)) { 108 items.add(createItem(context, subtype.getLocale())); 109 } 110 } 111 // TODO: Should filter out already existing combinations of locale and layout. 112 addAll(items); 113 } 114 115 public static SubtypeLocaleItem createItem(final Context context, 116 final String localeString) { 117 if (localeString.equals(SubtypeLocale.NO_LANGUAGE)) { 118 final String displayName = context.getString(R.string.subtype_no_language); 119 return new SubtypeLocaleItem(localeString, displayName); 120 } else { 121 return new SubtypeLocaleItem(localeString); 122 } 123 } 124 } 125 126 static final class KeyboardLayoutSetItem extends Pair<String, String> { 127 public KeyboardLayoutSetItem(final InputMethodSubtype subtype) { 128 super(SubtypeLocale.getKeyboardLayoutSetName(subtype), 129 SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype)); 130 } 131 132 @Override 133 public String toString() { 134 return second; 135 } 136 } 137 138 static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> { 139 public KeyboardLayoutSetAdapter(final Context context) { 140 super(context, android.R.layout.simple_spinner_item); 141 setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 142 143 // TODO: Should filter out already existing combinations of locale and layout. 144 for (final String layout : SubtypeLocale.getPredefinedKeyboardLayoutSet()) { 145 // This is a dummy subtype with NO_LANGUAGE, only for display. 146 final InputMethodSubtype subtype = AdditionalSubtype.createAdditionalSubtype( 147 SubtypeLocale.NO_LANGUAGE, layout, null); 148 add(new KeyboardLayoutSetItem(subtype)); 149 } 150 } 151 } 152 153 private interface SubtypeDialogProxy { 154 public void onRemovePressed(SubtypePreference subtypePref); 155 public void onSavePressed(SubtypePreference subtypePref); 156 public void onAddPressed(SubtypePreference subtypePref); 157 public SubtypeLocaleAdapter getSubtypeLocaleAdapter(); 158 public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter(); 159 } 160 161 static final class SubtypePreference extends DialogPreference 162 implements DialogInterface.OnCancelListener { 163 private static final String KEY_PREFIX = "subtype_pref_"; 164 private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new"; 165 166 private InputMethodSubtype mSubtype; 167 private InputMethodSubtype mPreviousSubtype; 168 169 private final SubtypeDialogProxy mProxy; 170 private Spinner mSubtypeLocaleSpinner; 171 private Spinner mKeyboardLayoutSetSpinner; 172 173 public static SubtypePreference newIncompleteSubtypePreference(final Context context, 174 final SubtypeDialogProxy proxy) { 175 return new SubtypePreference(context, null, proxy); 176 } 177 178 public SubtypePreference(final Context context, final InputMethodSubtype subtype, 179 final SubtypeDialogProxy proxy) { 180 super(context, null); 181 setDialogLayoutResource(R.layout.additional_subtype_dialog); 182 setPersistent(false); 183 mProxy = proxy; 184 setSubtype(subtype); 185 } 186 187 public void show() { 188 showDialog(null); 189 } 190 191 public final boolean isIncomplete() { 192 return mSubtype == null; 193 } 194 195 public InputMethodSubtype getSubtype() { 196 return mSubtype; 197 } 198 199 public void setSubtype(final InputMethodSubtype subtype) { 200 mPreviousSubtype = mSubtype; 201 mSubtype = subtype; 202 if (isIncomplete()) { 203 setTitle(null); 204 setDialogTitle(R.string.add_style); 205 setKey(KEY_NEW_SUBTYPE); 206 } else { 207 final String displayName = 208 SubtypeLocale.getSubtypeDisplayNameInSystemLocale(subtype); 209 setTitle(displayName); 210 setDialogTitle(displayName); 211 setKey(KEY_PREFIX + subtype.getLocale() + "_" 212 + SubtypeLocale.getKeyboardLayoutSetName(subtype)); 213 } 214 } 215 216 public void revert() { 217 setSubtype(mPreviousSubtype); 218 } 219 220 public boolean hasBeenModified() { 221 return mSubtype != null && !mSubtype.equals(mPreviousSubtype); 222 } 223 224 @Override 225 protected View onCreateDialogView() { 226 final View v = super.onCreateDialogView(); 227 mSubtypeLocaleSpinner = (Spinner) v.findViewById(R.id.subtype_locale_spinner); 228 mSubtypeLocaleSpinner.setAdapter(mProxy.getSubtypeLocaleAdapter()); 229 mKeyboardLayoutSetSpinner = (Spinner) v.findViewById(R.id.keyboard_layout_set_spinner); 230 mKeyboardLayoutSetSpinner.setAdapter(mProxy.getKeyboardLayoutSetAdapter()); 231 return v; 232 } 233 234 @Override 235 protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) { 236 final Context context = builder.getContext(); 237 builder.setCancelable(true).setOnCancelListener(this); 238 if (isIncomplete()) { 239 builder.setPositiveButton(R.string.add, this) 240 .setNegativeButton(android.R.string.cancel, this); 241 } else { 242 builder.setPositiveButton(R.string.save, this) 243 .setNeutralButton(android.R.string.cancel, this) 244 .setNegativeButton(R.string.remove, this); 245 final SubtypeLocaleItem localeItem = SubtypeLocaleAdapter.createItem( 246 context, mSubtype.getLocale()); 247 final KeyboardLayoutSetItem layoutItem = new KeyboardLayoutSetItem(mSubtype); 248 setSpinnerPosition(mSubtypeLocaleSpinner, localeItem); 249 setSpinnerPosition(mKeyboardLayoutSetSpinner, layoutItem); 250 } 251 } 252 253 private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) { 254 final SpinnerAdapter adapter = spinner.getAdapter(); 255 final int count = adapter.getCount(); 256 for (int i = 0; i < count; i++) { 257 final Object item = spinner.getItemAtPosition(i); 258 if (item.equals(itemToSelect)) { 259 spinner.setSelection(i); 260 return; 261 } 262 } 263 } 264 265 @Override 266 public void onCancel(final DialogInterface dialog) { 267 if (isIncomplete()) { 268 mProxy.onRemovePressed(this); 269 } 270 } 271 272 @Override 273 public void onClick(final DialogInterface dialog, final int which) { 274 super.onClick(dialog, which); 275 switch (which) { 276 case DialogInterface.BUTTON_POSITIVE: 277 final boolean isEditing = !isIncomplete(); 278 final SubtypeLocaleItem locale = 279 (SubtypeLocaleItem) mSubtypeLocaleSpinner.getSelectedItem(); 280 final KeyboardLayoutSetItem layout = 281 (KeyboardLayoutSetItem) mKeyboardLayoutSetSpinner.getSelectedItem(); 282 final InputMethodSubtype subtype = AdditionalSubtype.createAdditionalSubtype( 283 locale.first, layout.first, ASCII_CAPABLE); 284 setSubtype(subtype); 285 notifyChanged(); 286 if (isEditing) { 287 mProxy.onSavePressed(this); 288 } else { 289 mProxy.onAddPressed(this); 290 } 291 break; 292 case DialogInterface.BUTTON_NEUTRAL: 293 // Nothing to do 294 break; 295 case DialogInterface.BUTTON_NEGATIVE: 296 mProxy.onRemovePressed(this); 297 break; 298 } 299 } 300 301 private static int getSpinnerPosition(final Spinner spinner) { 302 if (spinner == null) return -1; 303 return spinner.getSelectedItemPosition(); 304 } 305 306 private static void setSpinnerPosition(final Spinner spinner, final int position) { 307 if (spinner == null || position < 0) return; 308 spinner.setSelection(position); 309 } 310 311 @Override 312 protected Parcelable onSaveInstanceState() { 313 final Parcelable superState = super.onSaveInstanceState(); 314 final Dialog dialog = getDialog(); 315 if (dialog == null || !dialog.isShowing()) { 316 return superState; 317 } 318 319 final SavedState myState = new SavedState(superState); 320 myState.mSubtype = mSubtype; 321 myState.mSubtypeLocaleSelectedPos = getSpinnerPosition(mSubtypeLocaleSpinner); 322 myState.mKeyboardLayoutSetSelectedPos = getSpinnerPosition(mKeyboardLayoutSetSpinner); 323 return myState; 324 } 325 326 @Override 327 protected void onRestoreInstanceState(final Parcelable state) { 328 if (!(state instanceof SavedState)) { 329 super.onRestoreInstanceState(state); 330 return; 331 } 332 333 final SavedState myState = (SavedState) state; 334 super.onRestoreInstanceState(myState.getSuperState()); 335 setSpinnerPosition(mSubtypeLocaleSpinner, myState.mSubtypeLocaleSelectedPos); 336 setSpinnerPosition(mKeyboardLayoutSetSpinner, myState.mKeyboardLayoutSetSelectedPos); 337 setSubtype(myState.mSubtype); 338 } 339 340 static final class SavedState extends Preference.BaseSavedState { 341 InputMethodSubtype mSubtype; 342 int mSubtypeLocaleSelectedPos; 343 int mKeyboardLayoutSetSelectedPos; 344 345 public SavedState(final Parcelable superState) { 346 super(superState); 347 } 348 349 @Override 350 public void writeToParcel(final Parcel dest, final int flags) { 351 super.writeToParcel(dest, flags); 352 dest.writeInt(mSubtypeLocaleSelectedPos); 353 dest.writeInt(mKeyboardLayoutSetSelectedPos); 354 dest.writeParcelable(mSubtype, 0); 355 } 356 357 public SavedState(final Parcel source) { 358 super(source); 359 mSubtypeLocaleSelectedPos = source.readInt(); 360 mKeyboardLayoutSetSelectedPos = source.readInt(); 361 mSubtype = (InputMethodSubtype)source.readParcelable(null); 362 } 363 364 @SuppressWarnings("hiding") 365 public static final Parcelable.Creator<SavedState> CREATOR = 366 new Parcelable.Creator<SavedState>() { 367 @Override 368 public SavedState createFromParcel(final Parcel source) { 369 return new SavedState(source); 370 } 371 372 @Override 373 public SavedState[] newArray(final int size) { 374 return new SavedState[size]; 375 } 376 }; 377 } 378 } 379 380 public AdditionalSubtypeSettings() { 381 // Empty constructor for fragment generation. 382 } 383 384 @Override 385 public void onCreate(final Bundle savedInstanceState) { 386 super.onCreate(savedInstanceState); 387 388 mPrefs = getPreferenceManager().getSharedPreferences(); 389 RichInputMethodManager.init(getActivity()); 390 mRichImm = RichInputMethodManager.getInstance(); 391 addPreferencesFromResource(R.xml.additional_subtype_settings); 392 setHasOptionsMenu(true); 393 } 394 395 @Override 396 public void onActivityCreated(final Bundle savedInstanceState) { 397 final Context context = getActivity(); 398 mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context); 399 mKeyboardLayoutSetAdapter = new KeyboardLayoutSetAdapter(context); 400 401 final String prefSubtypes = 402 Settings.readPrefAdditionalSubtypes(mPrefs, getResources()); 403 setPrefSubtypes(prefSubtypes, context); 404 405 mIsAddingNewSubtype = (savedInstanceState != null) 406 && savedInstanceState.containsKey(KEY_IS_ADDING_NEW_SUBTYPE); 407 if (mIsAddingNewSubtype) { 408 getPreferenceScreen().addPreference( 409 SubtypePreference.newIncompleteSubtypePreference(context, mSubtypeProxy)); 410 } 411 412 super.onActivityCreated(savedInstanceState); 413 414 if (savedInstanceState != null && savedInstanceState.containsKey( 415 KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN)) { 416 mSubtypePreferenceKeyForSubtypeEnabler = savedInstanceState.getString( 417 KEY_SUBTYPE_FOR_SUBTYPE_ENABLER); 418 final SubtypePreference subtypePref = (SubtypePreference)findPreference( 419 mSubtypePreferenceKeyForSubtypeEnabler); 420 mSubtypeEnablerNotificationDialog = createDialog(subtypePref); 421 mSubtypeEnablerNotificationDialog.show(); 422 } 423 } 424 425 @Override 426 public void onSaveInstanceState(final Bundle outState) { 427 super.onSaveInstanceState(outState); 428 if (mIsAddingNewSubtype) { 429 outState.putBoolean(KEY_IS_ADDING_NEW_SUBTYPE, true); 430 } 431 if (mSubtypeEnablerNotificationDialog != null 432 && mSubtypeEnablerNotificationDialog.isShowing()) { 433 outState.putBoolean(KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN, true); 434 outState.putString( 435 KEY_SUBTYPE_FOR_SUBTYPE_ENABLER, mSubtypePreferenceKeyForSubtypeEnabler); 436 } 437 } 438 439 private final SubtypeDialogProxy mSubtypeProxy = new SubtypeDialogProxy() { 440 @Override 441 public void onRemovePressed(final SubtypePreference subtypePref) { 442 mIsAddingNewSubtype = false; 443 final PreferenceGroup group = getPreferenceScreen(); 444 group.removePreference(subtypePref); 445 mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); 446 } 447 448 @Override 449 public void onSavePressed(final SubtypePreference subtypePref) { 450 final InputMethodSubtype subtype = subtypePref.getSubtype(); 451 if (!subtypePref.hasBeenModified()) { 452 return; 453 } 454 if (findDuplicatedSubtype(subtype) == null) { 455 mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); 456 return; 457 } 458 459 // Saved subtype is duplicated. 460 final PreferenceGroup group = getPreferenceScreen(); 461 group.removePreference(subtypePref); 462 subtypePref.revert(); 463 group.addPreference(subtypePref); 464 showSubtypeAlreadyExistsToast(subtype); 465 } 466 467 @Override 468 public void onAddPressed(final SubtypePreference subtypePref) { 469 mIsAddingNewSubtype = false; 470 final InputMethodSubtype subtype = subtypePref.getSubtype(); 471 if (findDuplicatedSubtype(subtype) == null) { 472 mRichImm.setAdditionalInputMethodSubtypes(getSubtypes()); 473 mSubtypePreferenceKeyForSubtypeEnabler = subtypePref.getKey(); 474 mSubtypeEnablerNotificationDialog = createDialog(subtypePref); 475 mSubtypeEnablerNotificationDialog.show(); 476 return; 477 } 478 479 // Newly added subtype is duplicated. 480 final PreferenceGroup group = getPreferenceScreen(); 481 group.removePreference(subtypePref); 482 showSubtypeAlreadyExistsToast(subtype); 483 } 484 485 @Override 486 public SubtypeLocaleAdapter getSubtypeLocaleAdapter() { 487 return mSubtypeLocaleAdapter; 488 } 489 490 @Override 491 public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter() { 492 return mKeyboardLayoutSetAdapter; 493 } 494 }; 495 496 private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) { 497 final Context context = getActivity(); 498 final Resources res = context.getResources(); 499 final String message = res.getString(R.string.custom_input_style_already_exists, 500 SubtypeLocale.getSubtypeDisplayNameInSystemLocale(subtype)); 501 Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); 502 } 503 504 private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) { 505 final String localeString = subtype.getLocale(); 506 final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype); 507 return mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( 508 localeString, keyboardLayoutSetName); 509 } 510 511 private AlertDialog createDialog( 512 @SuppressWarnings("unused") final SubtypePreference subtypePref) { 513 final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 514 builder.setTitle(R.string.custom_input_styles_title) 515 .setMessage(R.string.custom_input_style_note_message) 516 .setNegativeButton(R.string.not_now, null) 517 .setPositiveButton(R.string.enable, new DialogInterface.OnClickListener() { 518 @Override 519 public void onClick(DialogInterface dialog, int which) { 520 final Intent intent = IntentUtils.getInputLanguageSelectionIntent( 521 mRichImm.getInputMethodIdOfThisIme(), 522 Intent.FLAG_ACTIVITY_NEW_TASK 523 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 524 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 525 // TODO: Add newly adding subtype to extra value of the intent as a hint 526 // for the input language selection activity. 527 // intent.putExtra("newlyAddedSubtype", subtypePref.getSubtype()); 528 startActivity(intent); 529 } 530 }); 531 532 return builder.create(); 533 } 534 535 private void setPrefSubtypes(final String prefSubtypes, final Context context) { 536 final PreferenceGroup group = getPreferenceScreen(); 537 group.removeAll(); 538 final InputMethodSubtype[] subtypesArray = 539 AdditionalSubtype.createAdditionalSubtypesArray(prefSubtypes); 540 for (final InputMethodSubtype subtype : subtypesArray) { 541 final SubtypePreference pref = new SubtypePreference( 542 context, subtype, mSubtypeProxy); 543 group.addPreference(pref); 544 } 545 } 546 547 private InputMethodSubtype[] getSubtypes() { 548 final PreferenceGroup group = getPreferenceScreen(); 549 final ArrayList<InputMethodSubtype> subtypes = CollectionUtils.newArrayList(); 550 final int count = group.getPreferenceCount(); 551 for (int i = 0; i < count; i++) { 552 final Preference pref = group.getPreference(i); 553 if (pref instanceof SubtypePreference) { 554 final SubtypePreference subtypePref = (SubtypePreference)pref; 555 // We should not save newly adding subtype to preference because it is incomplete. 556 if (subtypePref.isIncomplete()) continue; 557 subtypes.add(subtypePref.getSubtype()); 558 } 559 } 560 return subtypes.toArray(new InputMethodSubtype[subtypes.size()]); 561 } 562 563 @Override 564 public void onPause() { 565 super.onPause(); 566 final String oldSubtypes = Settings.readPrefAdditionalSubtypes(mPrefs, getResources()); 567 final InputMethodSubtype[] subtypes = getSubtypes(); 568 final String prefSubtypes = AdditionalSubtype.createPrefSubtypes(subtypes); 569 if (prefSubtypes.equals(oldSubtypes)) { 570 return; 571 } 572 Settings.writePrefAdditionalSubtypes(mPrefs, prefSubtypes); 573 mRichImm.setAdditionalInputMethodSubtypes(subtypes); 574 } 575 576 @Override 577 public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { 578 final MenuItem addSubtypeMenu = menu.add(0, MENU_ADD_SUBTYPE, 0, R.string.add_style); 579 addSubtypeMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 580 } 581 582 @Override 583 public boolean onOptionsItemSelected(final MenuItem item) { 584 final int itemId = item.getItemId(); 585 if (itemId == MENU_ADD_SUBTYPE) { 586 final SubtypePreference newSubtype = 587 SubtypePreference.newIncompleteSubtypePreference(getActivity(), mSubtypeProxy); 588 getPreferenceScreen().addPreference(newSubtype); 589 newSubtype.show(); 590 mIsAddingNewSubtype = true; 591 return true; 592 } 593 return super.onOptionsItemSelected(item); 594 } 595 } 596