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

Commit d2c78d68 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fix InputMethodPreference.compareTo method"

parents ce31943c 9f240ad6
Loading
Loading
Loading
Loading
+26 −17
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.Toast;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.InputMethodUtils;
import com.android.settingslib.R;
import com.android.settingslib.RestrictedLockUtils;
@@ -91,20 +92,28 @@ public class InputMethodPreference extends RestrictedSwitchPreference implements
    public InputMethodPreference(final Context context, final InputMethodInfo imi,
            final boolean isImeEnabler, final boolean isAllowedByOrganization,
            final OnSavePreferenceListener onSaveListener) {
        this(context, imi, imi.loadLabel(context.getPackageManager()), isAllowedByOrganization,
                onSaveListener);
        if (!isImeEnabler) {
            // Remove switch widget.
            setWidgetLayoutResource(NO_WIDGET);
        }
    }

    @VisibleForTesting
    InputMethodPreference(final Context context, final InputMethodInfo imi,
            final CharSequence title, final boolean isAllowedByOrganization,
            final OnSavePreferenceListener onSaveListener) {
        super(context);
        setPersistent(false);
        mImi = imi;
        mIsAllowedByOrganization = isAllowedByOrganization;
        mOnSaveListener = onSaveListener;
        if (!isImeEnabler) {
            // Remove switch widget.
            setWidgetLayoutResource(NO_WIDGET);
        }
        // Disable on/off switch texts.
        setSwitchTextOn(EMPTY_TEXT);
        setSwitchTextOff(EMPTY_TEXT);
        setKey(imi.getId());
        setTitle(imi.loadLabel(context.getPackageManager()));
        setTitle(title);
        final String settingsActivity = imi.getSettingsActivity();
        if (TextUtils.isEmpty(settingsActivity)) {
            setIntent(null);
@@ -283,18 +292,18 @@ public class InputMethodPreference extends RestrictedSwitchPreference implements
        if (this == rhs) {
            return 0;
        }
        if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) {
            final CharSequence t0 = getTitle();
            final CharSequence t1 = rhs.getTitle();
            if (TextUtils.isEmpty(t0)) {
                return 1;
            }
            if (TextUtils.isEmpty(t1)) {
                return -1;
            }
            return collator.compare(t0.toString(), t1.toString());
        }
        if (mHasPriorityInSorting != rhs.mHasPriorityInSorting) {
            // Prefer always checked system IMEs
            return mHasPriorityInSorting ? -1 : 1;
        }
        final CharSequence title = getTitle();
        final CharSequence rhsTitle = rhs.getTitle();
        final boolean emptyTitle = TextUtils.isEmpty(title);
        final boolean rhsEmptyTitle = TextUtils.isEmpty(rhsTitle);
        if (!emptyTitle && !rhsEmptyTitle) {
            return collator.compare(title.toString(), rhsTitle.toString());
        }
        // For historical reasons, an empty text needs to be put at the first.
        return (emptyTitle ? -1 : 0) - (rhsEmptyTitle ? -1 : 0);
    }
}
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 com.android.settingslib.inputmethod;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;

import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntBiFunction;

/**
 * Utility class to assert {@link Comparable} objects.
 */
final class ComparableUtils {

    private ComparableUtils() {
    }

    /**
     * Checks whether specified items have ascending ordering.
     *
     * @param items     objects to be checked Comparable contracts.
     * @param compareTo function to compare two objects as {@link Comparable#compareTo(Object)}.
     * @param name      function to extract name of an object.
     * @param <T>       type that implements {@link Comparable}.
     */
    static <T> void assertAscendingOrdering(final List<T> items,
            final ToIntBiFunction<T, T> compareTo, final Function<T, String> name) {
        for (int i = 1; i < items.size(); i++) {
            final T x = items.get(i - 1);
            final T y = items.get(i);
            assertTrue(name.apply(x) + " is less than " + name.apply(y),
                    compareTo.applyAsInt(x, y) < 0);
        }
    }

    /**
     * Checks whether specified items have the same ordering.
     *
     * @param items     objects to be checked equality.
     * @param compareTo function to compare two objects as {@link Comparable#compareTo(Object)}.
     * @param name      function to extract name of an object.
     * @param <T>       type that implements {@link Comparable}.
     */
    static <T> void assertSameOrdering(final Collection<T> items,
            final ToIntBiFunction<T, T> compareTo, final Function<T, String> name) {
        for (final T x : items) {
            for (final T y : items) {
                assertTrue(name.apply(x) + " is equal to " + name.apply(y),
                        compareTo.applyAsInt(x, y) == 0);
            }
        }
    }

    /**
     * Checks whether a {@link Comparable} type complies with Comparable contracts.
     * <ul>
     * <li>Ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y.</li>
     * <li>Ensure that the relation is transitive:
     *     (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0.</li>
     * <li>Ensure that x.compareTo(y)==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)),
     *     for all z.</li>
     * </ul>
     *
     * @param items     objects to be checked Comparable contracts.
     * @param compareTo function to compare two objects as {@link Comparable#compareTo(Object)}.
     * @param name      function to extract name of an object.
     * @param <T>       type that implements {@link Comparable}.
     */
    static <T> void assertComparableContracts(final Collection<T> items,
            final ToIntBiFunction<T, T> compareTo, final Function<T, String> name) {
        for (final T x : items) {
            final String nameX = name.apply(x);
            assertTrue("Reflective: " + nameX + " is equal to itself",
                    compareTo.applyAsInt(x, x) == 0);
            for (final T y : items) {
                final String nameY = name.apply(y);
                assertEquals("Asymmetric: " + nameX + " and " + nameY,
                        Integer.signum(compareTo.applyAsInt(x, y)),
                        -Integer.signum(compareTo.applyAsInt(y, x)));
                for (final T z : items) {
                    final String nameZ = name.apply(z);
                    if (compareTo.applyAsInt(x, y) > 0 && compareTo.applyAsInt(y, z) > 0) {
                        assertTrue("Transitive: " + nameX + " is greater than " + nameY
                                        + " and " + nameY + " is greater than " + nameZ
                                        + " then " + nameX + " is greater than " + nameZ,
                                compareTo.applyAsInt(x, z) > 0);
                    }
                    if (compareTo.applyAsInt(x, y) == 0) {
                        assertEquals("Transitive: " + nameX + " and " + nameY + " is same "
                                        + " then both return the same result for " + nameZ,
                                Integer.signum(compareTo.applyAsInt(x, z)),
                                Integer.signum(compareTo.applyAsInt(y, z)));
                    }
                }
            }
        }
    }
}
+150 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 com.android.settingslib.inputmethod;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;

import org.junit.Test;
import org.junit.runner.RunWith;

import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class InputMethodPreferenceTest {

    private static final Collator COLLATOR = Collator.getInstance(Locale.US);

    @Test
    public void testComparableOrdering() throws Exception {
        final List<InputMethodPreference> itemsInAscendingOrder = Arrays.asList(
                createPreference("", true, "no_title-system"),
                createPreference("E", true, "E-system"),
                createPreference("Z", true, "Z-system"),
                createPreference("", false, "no_title-non_system"),
                createPreference("E", false, "E-non_system"),
                createPreference("Z", false, "Z-non_system")
        );
        ComparableUtils.assertAscendingOrdering(
                itemsInAscendingOrder,
                (x, y) -> x.compareTo(y, COLLATOR),
                x -> x.getInputMethodInfo().getServiceName());
    }

    @Test
    public void testComparableEquality() {
        final List<InputMethodPreference> itemsInSameOrder1 = Arrays.asList(
                createPreference("", true, "no_title-system-1"),
                createPreference("", true, "no_title-system-2")
        );
        ComparableUtils.assertSameOrdering(
                itemsInSameOrder1,
                (x, y) -> x.compareTo(y, COLLATOR),
                x -> x.getInputMethodInfo().getServiceName());

        final List<InputMethodPreference> itemsInSameOrder2 = Arrays.asList(
                createPreference("A", false, "A-non_system-1"),
                createPreference("A", false, "A-non_system-2")
        );
        ComparableUtils.assertSameOrdering(
                itemsInSameOrder2,
                (x, y) -> x.compareTo(y, COLLATOR),
                x -> x.getInputMethodInfo().getServiceName());
    }

    @Test
    public void testComparableContracts() {
        final List<InputMethodPreference> items = Arrays.asList(
                // itemsInAscendingOrder.
                createPreference("", true, "no_title-system"),
                createPreference("E", true, "E-system"),
                createPreference("Z", true, "Z-system"),
                createPreference("", false, "no_title-non_system"),
                createPreference("E", false, "E-non_system"),
                createPreference("Z", false, "Z-non_system"),
                // itemsInSameOrder1.
                createPreference("", true, "no_title-system-1"),
                createPreference("", true, "no_title-system-2"),
                // itemsInSameOrder2.
                createPreference("A", false, "A-non_system-1"),
                createPreference("A", false, "A-non_system-2")
        );

        ComparableUtils.assertComparableContracts(
                items,
                (x, y) -> x.compareTo(y, COLLATOR),
                x -> x.getInputMethodInfo().getServiceName());
    }

    private static InputMethodPreference createPreference(
            final CharSequence title,
            final boolean systemIme,
            final String name) {
        return new InputMethodPreference(
                InstrumentationRegistry.getTargetContext(),
                createInputMethodInfo(systemIme, name),
                title,
                true /* isAllowedByOrganization */,
                p -> {} /* onSavePreferenceListener */);
    }

    private static InputMethodInfo createInputMethodInfo(
            final boolean systemIme, final String name) {
        final Context targetContext = InstrumentationRegistry.getTargetContext();
        final Locale systemLocale = targetContext
                .getResources()
                .getConfiguration()
                .getLocales()
                .get(0);
        final InputMethodSubtype systemLocaleSubtype =
                new InputMethodSubtype.InputMethodSubtypeBuilder()
                        .setIsAsciiCapable(true)
                        .setSubtypeLocale(systemLocale.getLanguage())
                        .build();

        final ResolveInfo resolveInfo = new ResolveInfo();
        resolveInfo.serviceInfo = new ServiceInfo();
        resolveInfo.serviceInfo.packageName = "com.android.ime";
        resolveInfo.serviceInfo.name = name;
        resolveInfo.serviceInfo.applicationInfo = new ApplicationInfo();
        resolveInfo.serviceInfo.applicationInfo.enabled = true;
        if (systemIme) {
            resolveInfo.serviceInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        } else {
            resolveInfo.serviceInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
        }
        return new InputMethodInfo(
                resolveInfo,
                false /* isAuxIme */,
                "SettingsActivity",
                Collections.singletonList(systemLocaleSubtype),
                0 /* isDefaultResId */,
                true /* forceDefault */);
    }
}
+29 −60
Original line number Diff line number Diff line
package com.android.settingslib.inputmethod;
/*
 * Copyright (C) 2017 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.
 */

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
package com.android.settingslib.inputmethod;

import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -16,8 +29,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Function;

@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -39,7 +50,7 @@ public class InputMethodSubtypePreferenceTest {
            createPreference("E", "ja", Locale.US),
            createPreference("Z", "ja", Locale.US)
    );
    private static final List<InputMethodSubtypePreference> SAME_ITEMS = Arrays.asList(
    private static final List<InputMethodSubtypePreference> SAME_ORDER_ITEMS = Arrays.asList(
            // Subtypes that has different language than the system's.
            createPreference("A", "ja_JP", Locale.US),
            createPreference("A", "hi_IN", Locale.US),
@@ -50,27 +61,25 @@ public class InputMethodSubtypePreferenceTest {

    @Test
    public void testComparableOrdering() throws Exception {
        onAllAdjacentItems(ITEMS_IN_ASCENDING,
                (x, y) -> assertTrue(
                        x.getKey() + " is less than " + y.getKey(),
                        x.compareTo(y, COLLATOR) < 0)
        );
        ComparableUtils.assertAscendingOrdering(
                ITEMS_IN_ASCENDING,
                (x, y) -> x.compareTo(y, COLLATOR),
                InputMethodSubtypePreference::getKey);
    }

    @Test
    public void testComparableEquality() {
        onAllAdjacentItems(SAME_ITEMS,
                (x, y) -> assertTrue(
                        x.getKey() + " is equal to " + y.getKey(),
                        x.compareTo(y, COLLATOR) == 0)
        );
        ComparableUtils.assertSameOrdering(
                SAME_ORDER_ITEMS,
                (x, y) -> x.compareTo(y, COLLATOR),
                InputMethodSubtypePreference::getKey);
    }

    @Test
    public void testComparableContracts() {
        final Collection<InputMethodSubtypePreference> items = new ArrayList<>();
        items.addAll(ITEMS_IN_ASCENDING);
        items.addAll(SAME_ITEMS);
        items.addAll(SAME_ORDER_ITEMS);
        items.add(createPreference("", "", Locale.US));
        items.add(createPreference("A", "en", Locale.US));
        items.add(createPreference("A", "en_US", Locale.US));
@@ -78,7 +87,7 @@ public class InputMethodSubtypePreferenceTest {
        items.add(createPreference("E", "en", Locale.US));
        items.add(createPreference("Z", "en_US", Locale.US));

        assertComparableContracts(
        ComparableUtils.assertComparableContracts(
                items,
                (x, y) -> x.compareTo(y, COLLATOR),
                InputMethodSubtypePreference::getKey);
@@ -88,52 +97,12 @@ public class InputMethodSubtypePreferenceTest {
            final String subtypeName,
            final String subtypeLocaleString,
            final Locale systemLocale) {
        final String key = subtypeName + "-" + subtypeLocaleString + "-" + systemLocale;
        return new InputMethodSubtypePreference(
                InstrumentationRegistry.getTargetContext(),
                subtypeName + "-" + subtypeLocaleString + "-" + systemLocale,
                key,
                subtypeName,
                subtypeLocaleString,
                systemLocale);
    }

    private static <T> void onAllAdjacentItems(final List<T> items, final BiConsumer<T, T> check) {
        for (int i = 0; i < items.size() - 1; i++) {
            check.accept(items.get(i), items.get(i + 1));
        }
    }

    @FunctionalInterface
    interface CompareTo<T> {
        int apply(T t, T u);
    }

    private static <T> void assertComparableContracts(final Collection<T> items,
            final CompareTo<T> compareTo, final Function<T, String> name) {
        for (final T x : items) {
            final String nameX = name.apply(x);
            assertTrue("Reflective: " + nameX + " is equal to itself",
                    compareTo.apply(x, x) == 0);
            for (final T y : items) {
                final String nameY = name.apply(y);
                assertEquals("Asymmetric: " + nameX + " and " + nameY,
                        Integer.signum(compareTo.apply(x, y)),
                        -Integer.signum(compareTo.apply(y, x)));
                for (final T z : items) {
                    final String nameZ = name.apply(z);
                    if (compareTo.apply(x, y) > 0 && compareTo.apply(y, z) > 0) {
                        assertTrue("Transitive: " + nameX + " is greater than " + nameY
                                        + " and " + nameY + " is greater than " + nameZ
                                        + " then " + nameX + " is greater than " + nameZ,
                                compareTo.apply(x, z) > 0);
                    }
                    if (compareTo.apply(x, y) == 0) {
                        assertEquals("Transitive: " + nameX + " and " + nameY + " is same "
                                        + " then both return the same result for " + nameZ,
                                Integer.signum(compareTo.apply(x, z)),
                                Integer.signum(compareTo.apply(y, z)));
                    }
                }
            }
        }
    }
}