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