Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8596f699 authored by Yohei Yukawa's avatar Yohei Yukawa Committed by Android (Google) Code Review
Browse files

Merge "Add unit tests for InputMethodSubtypeSwitchingController"

parents c9722bd8 d1da1152
Loading
Loading
Loading
Loading
+13 −2
Original line number Original line Diff line number Diff line
@@ -259,7 +259,7 @@ public final class InputMethodInfo implements Parcelable {
    public InputMethodInfo(String packageName, String className,
    public InputMethodInfo(String packageName, String className,
            CharSequence label, String settingsActivity) {
            CharSequence label, String settingsActivity) {
        this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
        this(buildDummyResolveInfo(packageName, className, label), false, settingsActivity, null,
                0, false);
                0, false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */);
    }
    }


    /**
    /**
@@ -269,6 +269,17 @@ public final class InputMethodInfo implements Parcelable {
    public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
    public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
            String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
            String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
            boolean forceDefault) {
            boolean forceDefault) {
        this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId,
                forceDefault, true /* supportsSwitchingToNextInputMethod */);
    }

    /**
     * Temporary API for creating a built-in input method for test.
     * @hide
     */
    public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
            String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
            boolean forceDefault, boolean supportsSwitchingToNextInputMethod) {
        final ServiceInfo si = ri.serviceInfo;
        final ServiceInfo si = ri.serviceInfo;
        mService = ri;
        mService = ri;
        mId = new ComponentName(si.packageName, si.name).flattenToShortString();
        mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -277,7 +288,7 @@ public final class InputMethodInfo implements Parcelable {
        mIsAuxIme = isAuxIme;
        mIsAuxIme = isAuxIme;
        mSubtypes = new InputMethodSubtypeArray(subtypes);
        mSubtypes = new InputMethodSubtypeArray(subtypes);
        mForceDefault = forceDefault;
        mForceDefault = forceDefault;
        mSupportsSwitchingToNextInputMethod = true;
        mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
    }
    }


    private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
    private static ResolveInfo buildDummyResolveInfo(String packageName, String className,
+38 −37
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.internal.inputmethod;
package com.android.internal.inputmethod;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;


import android.content.Context;
import android.content.Context;
@@ -119,14 +120,14 @@ public class InputMethodSubtypeSwitchingController {
        }
        }
    }
    }


    private static class InputMethodAndSubtypeCircularList {
    private static class InputMethodAndSubtypeList {
        private final Context mContext;
        private final Context mContext;
        // Used to load label
        // Used to load label
        private final PackageManager mPm;
        private final PackageManager mPm;
        private final String mSystemLocaleStr;
        private final String mSystemLocaleStr;
        private final InputMethodSettings mSettings;
        private final InputMethodSettings mSettings;


        public InputMethodAndSubtypeCircularList(Context context, InputMethodSettings settings) {
        public InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
            mContext = context;
            mContext = context;
            mSettings = settings;
            mSettings = settings;
            mPm = context.getPackageManager();
            mPm = context.getPackageManager();
@@ -152,38 +153,6 @@ public class InputMethodSubtypeSwitchingController {
                            }
                            }
                        });
                        });


        public ImeSubtypeListItem getNextInputMethod(
                boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
            if (imi == null) {
                return null;
            }
            final List<ImeSubtypeListItem> imList =
                    getSortedInputMethodAndSubtypeList();
            if (imList.size() <= 1) {
                return null;
            }
            final int N = imList.size();
            final int currentSubtypeId =
                    subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
                            subtype.hashCode()) : NOT_A_SUBTYPE_ID;
            for (int i = 0; i < N; ++i) {
                final ImeSubtypeListItem isli = imList.get(i);
                if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
                    if (!onlyCurrentIme) {
                        return imList.get((i + 1) % N);
                    }
                    for (int j = 0; j < N - 1; ++j) {
                        final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
                        if (candidate.mImi.equals(imi)) {
                            return candidate;
                        }
                    }
                    return null;
                }
            }
            return null;
        }

        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
        public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() {
            return getSortedInputMethodAndSubtypeList(true, false, false);
            return getSortedInputMethodAndSubtypeList(true, false, false);
        }
        }
@@ -247,7 +216,38 @@ public class InputMethodSubtypeSwitchingController {
    private final ArrayDeque<SubtypeParams> mTypedSubtypeHistory = new ArrayDeque<SubtypeParams>();
    private final ArrayDeque<SubtypeParams> mTypedSubtypeHistory = new ArrayDeque<SubtypeParams>();
    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private final InputMethodSettings mSettings;
    private final InputMethodSettings mSettings;
    private InputMethodAndSubtypeCircularList mSubtypeList;
    private InputMethodAndSubtypeList mSubtypeList;

    @VisibleForTesting
    public static ImeSubtypeListItem getNextInputMethodImpl(List<ImeSubtypeListItem> imList,
            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
        if (imi == null) {
            return null;
        }
        if (imList.size() <= 1) {
            return null;
        }
        final int N = imList.size();
        final int currentSubtypeId =
                subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
                        subtype.hashCode()) : NOT_A_SUBTYPE_ID;
        for (int i = 0; i < N; ++i) {
            final ImeSubtypeListItem isli = imList.get(i);
            if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) {
                if (!onlyCurrentIme) {
                    return imList.get((i + 1) % N);
                }
                for (int j = 0; j < N - 1; ++j) {
                    final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N);
                    if (candidate.mImi.equals(imi)) {
                        return candidate;
                    }
                }
                return null;
            }
        }
        return null;
    }


    public InputMethodSubtypeSwitchingController(InputMethodSettings settings) {
    public InputMethodSubtypeSwitchingController(InputMethodSettings settings) {
        mSettings = settings;
        mSettings = settings;
@@ -278,14 +278,15 @@ public class InputMethodSubtypeSwitchingController {


    public void resetCircularListLocked(Context context) {
    public void resetCircularListLocked(Context context) {
        synchronized(mLock) {
        synchronized(mLock) {
            mSubtypeList = new InputMethodAndSubtypeCircularList(context, mSettings);
            mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
        }
        }
    }
    }


    public ImeSubtypeListItem getNextInputMethod(
    public ImeSubtypeListItem getNextInputMethod(
            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
            boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) {
        synchronized(mLock) {
        synchronized(mLock) {
            return mSubtypeList.getNextInputMethod(onlyCurrentIme, imi, subtype);
            return getNextInputMethodImpl(mSubtypeList.getSortedInputMethodAndSubtypeList(),
                    onlyCurrentIme, imi, subtype);
        }
        }
    }
    }


+1 −1
Original line number Original line Diff line number Diff line
@@ -21,4 +21,4 @@ if [[ $rebuild == true ]]; then
  $COMMAND
  $COMMAND
fi
fi


adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest,android.os.InputMethodSubtypeSwitchingControllerTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
+205 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;

import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase {
    final private static String DUMMY_PACKAGE_NAME = "dymmy package name";
    final private static String DUMMY_SETTING_ACTIVITY_NAME = "";
    final private static boolean DUMMY_IS_AUX_IME = false;
    final private static boolean DUMMY_FORCE_DEFAULT = false;
    final private static int DUMMY_IS_DEFAULT_RES_ID = 0;
    final private static String SYSTEM_LOCALE = "en_US";

    private static InputMethodSubtype createDummySubtype(final String locale) {
        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
        return builder.setSubtypeNameResId(0)
                .setSubtypeIconResId(0)
                .setSubtypeLocale(locale)
                .setIsAsciiCapable(true)
                .build();
    }

    private static void addDummyImeSubtypeListItems(List<ImeSubtypeListItem> items,
            String imeName, String imeLabel, List<String> subtypeLocales,
            boolean supportsSwitchingToNextInputMethod) {
        final ResolveInfo ri = new ResolveInfo();
        final ServiceInfo si = new ServiceInfo();
        final ApplicationInfo ai = new ApplicationInfo();
        ai.packageName = DUMMY_PACKAGE_NAME;
        ai.enabled = true;
        si.applicationInfo = ai;
        si.enabled = true;
        si.packageName = DUMMY_PACKAGE_NAME;
        si.name = imeName;
        si.exported = true;
        si.nonLocalizedLabel = imeLabel;
        ri.serviceInfo = si;
        final List<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
        for (String subtypeLocale : subtypeLocales) {
            subtypes.add(createDummySubtype(subtypeLocale));
        }
        final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
                DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
        for (int i = 0; i < subtypes.size(); ++i) {
            final String subtypeLocale = subtypeLocales.get(i);
            final InputMethodSubtype subtype = subtypes.get(i);
            items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
                    SYSTEM_LOCALE));
        }
    }

    private static List<ImeSubtypeListItem> createTestData() {
        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
        addDummyImeSubtypeListItems(items, "switchAwareLatinIme", "switchAwareLatinIme",
                Arrays.asList("en_US", "es_US", "fr"),
                true /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "nonSwitchAwareLatinIme", "nonSwitchAwareLatinIme",
                Arrays.asList("en_UK", "hi"),
                false /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "switchAwareJapaneseIme", "switchAwareJapaneseIme",
                Arrays.asList("ja_JP"),
                true /* supportsSwitchingToNextInputMethod*/);
        addDummyImeSubtypeListItems(items, "nonSwitchAwareJapaneseIme", "nonSwitchAwareJapaneseIme",
                Arrays.asList("ja_JP"),
                false /* supportsSwitchingToNextInputMethod*/);
        return items;
    }

    @SmallTest
    public void testGetNextInputMethodImplWithNotOnlyCurrentIme() throws Exception {
        final List<ImeSubtypeListItem> imList = createTestData();

        final boolean ONLY_CURRENT_IME = false;
        ImeSubtypeListItem currentIme;
        ImeSubtypeListItem nextIme;

        // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
        currentIme = imList.get(0);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(1), nextIme);
        // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
        currentIme = imList.get(1);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(2), nextIme);
        // "switchAwareLatinIme/fr" -> "nonSwitchAwareLatinIme/en_UK
        currentIme = imList.get(2);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(3), nextIme);
        // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
        currentIme = imList.get(3);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(4), nextIme);
        // "nonSwitchAwareLatinIme/hi" -> "switchAwareJapaneseIme/ja_JP"
        currentIme = imList.get(4);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(5), nextIme);
        // "switchAwareJapaneseIme/ja_JP" -> "nonSwitchAwareJapaneseIme/ja_JP"
        currentIme = imList.get(5);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(6), nextIme);
        // "nonSwitchAwareJapaneseIme/ja_JP" -> "switchAwareLatinIme/en_US"
        currentIme = imList.get(6);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(0), nextIme);
    }

    @SmallTest
    public void testGetNextInputMethodImplWithOnlyCurrentIme() throws Exception {
        final List<ImeSubtypeListItem> imList = createTestData();

        final boolean ONLY_CURRENT_IME = true;
        ImeSubtypeListItem currentIme;
        ImeSubtypeListItem nextIme;

        // "switchAwareLatinIme/en_US" -> "switchAwareLatinIme/es_US"
        currentIme = imList.get(0);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(1), nextIme);
        // "switchAwareLatinIme/es_US" -> "switchAwareLatinIme/fr"
        currentIme = imList.get(1);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(2), nextIme);
        // "switchAwareLatinIme/fr" -> "switchAwareLatinIme/en_US"
        currentIme = imList.get(2);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(0), nextIme);

        // "nonSwitchAwareLatinIme/en_UK" -> "nonSwitchAwareLatinIme/hi"
        currentIme = imList.get(3);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(4), nextIme);
        // "nonSwitchAwareLatinIme/hi" -> "switchAwareLatinIme/en_UK"
        currentIme = imList.get(4);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertEquals(imList.get(3), nextIme);

        // "switchAwareJapaneseIme/ja_JP" -> null
        currentIme = imList.get(5);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertNull(nextIme);

        // "nonSwitchAwareJapaneseIme/ja_JP" -> null
        currentIme = imList.get(6);
        nextIme = InputMethodSubtypeSwitchingController.getNextInputMethodImpl(
                imList, ONLY_CURRENT_IME, currentIme.mImi, createDummySubtype(
                        currentIme.mSubtypeName.toString()));
        assertNull(nextIme);
    }
 }