summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYohei Yukawa <yukawa@google.com>2014-05-26 16:37:22 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-05-26 16:37:23 +0000
commitc659053c4cd4ce4ead59f9709af7e007308daa20 (patch)
tree9dcce6319dddeb1522c21275933f9928ab443a0c
parentbdbb0f0eeff051ca309a788fe6bd399cabb5fd66 (diff)
parenta9bda774276f1c5a1fc6fd67a7782a06e696be8f (diff)
downloadframeworks_base-c659053c4cd4ce4ead59f9709af7e007308daa20.zip
frameworks_base-c659053c4cd4ce4ead59f9709af7e007308daa20.tar.gz
frameworks_base-c659053c4cd4ce4ead59f9709af7e007308daa20.tar.bz2
Merge "Implement dynamic IME rotation based on user action"
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java96
-rw-r--r--core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java111
2 files changed, 182 insertions, 25 deletions
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index 70c77b8..bfa5c22 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -271,13 +271,87 @@ public class InputMethodSubtypeSwitchingController {
}
}
+ private static class DynamicRotationList {
+ private static final String TAG = DynamicRotationList.class.getSimpleName();
+ private final List<ImeSubtypeListItem> mImeSubtypeList;
+ private final int[] mUsageHistoryOfSubtypeListItemIndex;
+
+ public DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
+ mImeSubtypeList = imeSubtypeListItems;
+ mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; i++) {
+ mUsageHistoryOfSubtypeListItemIndex[i] = i;
+ }
+ }
+
+ /**
+ * Returns the index of the specified object in
+ * {@link #mUsageHistoryOfSubtypeListItemIndex}.
+ * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank"
+ * so as not to be confused with the index in {@link #mImeSubtypeList}.
+ * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually.
+ */
+ private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentSubtypeId = calculateSubtypeId(imi, subtype);
+ final int N = mUsageHistoryOfSubtypeListItemIndex.length;
+ for (int usageRank = 0; usageRank < N; usageRank++) {
+ final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank];
+ final ImeSubtypeListItem subtypeListItem =
+ mImeSubtypeList.get(subtypeListItemIndex);
+ if (subtypeListItem.mImi.equals(imi) &&
+ subtypeListItem.mSubtypeId == currentSubtypeId) {
+ return usageRank;
+ }
+ }
+ // Not found in the known IME/Subtype list.
+ return -1;
+ }
+
+ public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) {
+ final int currentUsageRank = getUsageRank(imi, subtype);
+ // Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0
+ if (currentUsageRank <= 0) {
+ return;
+ }
+ final int currentItemIndex = mUsageHistoryOfSubtypeListItemIndex[currentUsageRank];
+ System.arraycopy(mUsageHistoryOfSubtypeListItemIndex, 0,
+ mUsageHistoryOfSubtypeListItemIndex, 1, currentUsageRank);
+ mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
+ }
+
+ public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
+ InputMethodInfo imi, InputMethodSubtype subtype) {
+ int currentUsageRank = getUsageRank(imi, subtype);
+ if (currentUsageRank < 0) {
+ if (DEBUG) {
+ Slog.d(TAG, "IME/subtype is not found: " + imi.getId() + ", " + subtype);
+ }
+ return null;
+ }
+ final int N = mUsageHistoryOfSubtypeListItemIndex.length;
+ for (int i = 1; i < N; i++) {
+ final int subtypeListItemRank = (currentUsageRank + i) % N;
+ final int subtypeListItemIndex =
+ mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank];
+ final ImeSubtypeListItem subtypeListItem =
+ mImeSubtypeList.get(subtypeListItemIndex);
+ if (onlyCurrentIme && !imi.equals(subtypeListItem.mImi)) {
+ continue;
+ }
+ return subtypeListItem;
+ }
+ return null;
+ }
+ }
+
@VisibleForTesting
public static class ControllerImpl {
- private final StaticRotationList mSwitchingAwareSubtypeList;
+ private final DynamicRotationList mSwitchingAwareSubtypeList;
private final StaticRotationList mSwitchingUnawareSubtypeList;
public ControllerImpl(final List<ImeSubtypeListItem> sortedItems) {
- mSwitchingAwareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
+ mSwitchingAwareSubtypeList = new DynamicRotationList(filterImeSubtypeList(sortedItems,
true /* supportsSwitchingToNextInputMethod */));
mSwitchingUnawareSubtypeList = new StaticRotationList(filterImeSubtypeList(sortedItems,
false /* supportsSwitchingToNextInputMethod */));
@@ -297,6 +371,15 @@ public class InputMethodSubtypeSwitchingController {
}
}
+ public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
+ if (imi == null) {
+ return;
+ }
+ if (imi.supportsSwitchingToNextInputMethod()) {
+ mSwitchingAwareSubtypeList.onUserAction(imi, subtype);
+ }
+ }
+
private static List<ImeSubtypeListItem> filterImeSubtypeList(
final List<ImeSubtypeListItem> items,
final boolean supportsSwitchingToNextInputMethod) {
@@ -327,9 +410,14 @@ public class InputMethodSubtypeSwitchingController {
return new InputMethodSubtypeSwitchingController(settings, context);
}
- // TODO: write unit tests for this method and the logic that determines the next subtype
public void onCommitTextLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
- // TODO: Implement this.
+ if (mController == null) {
+ if (DEBUG) {
+ Log.e(TAG, "mController shouldn't be null.");
+ }
+ return;
+ }
+ mController.onUserActionLocked(imi, subtype);
}
public void resetCircularListLocked(Context context) {
diff --git a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
index 459bed5..04cfe05 100644
--- a/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
+++ b/core/tests/inputmethodtests/src/android/os/InputMethodSubtypeSwitchingControllerTest.java
@@ -132,6 +132,29 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
assertEquals(nextItem, nextIme);
}
+ private void assertRotationOrder(final ControllerImpl controller,
+ final boolean onlyCurrentIme,
+ final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
+ final int N = expectedRotationOrderOfImeSubtypeList.length;
+ for (int i = 0; i < N; i++) {
+ final int currentIndex = i;
+ final int nextIndex = (currentIndex + 1) % N;
+ final ImeSubtypeListItem currentItem =
+ expectedRotationOrderOfImeSubtypeList[currentIndex];
+ final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
+ assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem);
+ }
+ }
+
+ private void onUserAction(final ControllerImpl controller,
+ final ImeSubtypeListItem subtypeListItem) {
+ InputMethodSubtype subtype = null;
+ if (subtypeListItem.mSubtypeName != null) {
+ subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString());
+ }
+ controller.onUserActionLocked(subtypeListItem.mImi, subtype);
+ }
+
@SmallTest
public void testControllerImpl() throws Exception {
final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
@@ -152,33 +175,20 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
final ControllerImpl controller = new ControllerImpl(enabledItems);
// switching-aware loop
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- latinIme_en_US, latinIme_fr);
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- latinIme_fr, japaneseIme_ja_JP);
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- japaneseIme_ja_JP, latinIme_en_US);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
// switching-unaware loop
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- switchingUnawarelatinIme_hi, subtypeUnawareIme);
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- subtypeUnawareIme, switchUnawareJapaneseIme_ja_JP);
- assertNextInputMethod(controller, false /* onlyCurrentIme */,
- switchUnawareJapaneseIme_ja_JP, switchingUnawarelatinIme_en_UK);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
// test onlyCurrentIme == true
- assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
latinIme_en_US, latinIme_fr);
- assertNextInputMethod(controller, true /* onlyCurrentIme */,
- latinIme_fr, latinIme_en_US);
- assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
- switchingUnawarelatinIme_hi, switchingUnawarelatinIme_en_UK);
- assertNextInputMethod(controller, true /* onlyCurrentIme */,
subtypeUnawareIme, null);
assertNextInputMethod(controller, true /* onlyCurrentIme */,
japaneseIme_ja_JP, null);
@@ -203,4 +213,63 @@ public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTe
assertNextInputMethod(controller, true /* onlyCurrentIme */,
disabledSubtypeUnawareIme, null);
}
- }
+
+ @SmallTest
+ public void testControllerImplWithUserAction() throws Exception {
+ final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
+ final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
+ final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
+ final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
+ final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
+ final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
+ final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
+ final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
+
+ final ControllerImpl controller = new ControllerImpl(enabledItems);
+
+ // === switching-aware loop ===
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
+ // Then notify that a user did something for latinIme_fr.
+ onUserAction(controller, latinIme_fr);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
+ // Then notify that a user did something for latinIme_fr again.
+ onUserAction(controller, latinIme_fr);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
+ // Then notify that a user did something for japaneseIme_ja_JP.
+ onUserAction(controller, latinIme_fr);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
+ // Check onlyCurrentIme == true.
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ japaneseIme_ja_JP, null);
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ latinIme_fr, latinIme_en_US);
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ latinIme_en_US, latinIme_fr);
+
+ // === switching-unaware loop ===
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+ // User action should be ignored for switching unaware IMEs.
+ onUserAction(controller, switchingUnawarelatinIme_hi);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+ // User action should be ignored for switching unaware IMEs.
+ onUserAction(controller, switchUnawareJapaneseIme_ja_JP);
+ assertRotationOrder(controller, false /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
+ switchUnawareJapaneseIme_ja_JP);
+ // Check onlyCurrentIme == true.
+ assertRotationOrder(controller, true /* onlyCurrentIme */,
+ switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ subtypeUnawareIme, null);
+ assertNextInputMethod(controller, true /* onlyCurrentIme */,
+ switchUnawareJapaneseIme_ja_JP, null);
+ }
+}