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

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

Merge "Fully implement PK subtype rotation shortcut" into udc-dev

parents abd084a6 2297ab6d
Loading
Loading
Loading
Loading
+76 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.server.inputmethod;

import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.InputMethodSubtypeHandle;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

final class HardwareKeyboardShortcutController {
    @GuardedBy("ImfLock.class")
    private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();

    @GuardedBy("ImfLock.class")
    void reset(@NonNull InputMethodUtils.InputMethodSettings settings) {
        mSubtypeHandles.clear();
        for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
            if (!imi.shouldShowInInputMethodPicker()) {
                continue;
            }
            final List<InputMethodSubtype> subtypes =
                    settings.getEnabledInputMethodSubtypeListLocked(imi, true);
            if (subtypes.isEmpty()) {
                mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, null));
            } else {
                for (final InputMethodSubtype subtype : subtypes) {
                    if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
                        mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, subtype));
                    }
                }
            }
        }
    }

    @AnyThread
    @Nullable
    static <T> T getNeighborItem(@NonNull List<T> list, @NonNull T value, boolean next) {
        final int size = list.size();
        for (int i = 0; i < size; ++i) {
            if (Objects.equals(value, list.get(i))) {
                final int nextIndex = (i + (next ? 1 : -1) + size) % size;
                return list.get(nextIndex);
            }
        }
        return null;
    }

    @GuardedBy("ImfLock.class")
    @Nullable
    InputMethodSubtypeHandle onSubtypeSwitch(
            @NonNull InputMethodSubtypeHandle currentImeAndSubtype, boolean forward) {
        return getNeighborItem(mSubtypeHandles, currentImeAndSubtype, forward);
    }
}
+36 −4
Original line number Diff line number Diff line
@@ -316,6 +316,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
    final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
    final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
    final InputMethodSubtypeSwitchingController mSwitchingController;
    final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
            new HardwareKeyboardShortcutController();

    /**
     * Tracks how many times {@link #mMethodMap} was updated.
@@ -1731,6 +1733,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
        mSwitchingController =
                InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
        mHardwareKeyboardShortcutController.reset(mSettings);
        mMenuController = new InputMethodMenuController(this);
        mBindingController =
                bindingControllerForTesting != null
@@ -3268,6 +3271,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        // TODO: Make sure that mSwitchingController and mSettings are sharing the
        // the same enabled IMEs list.
        mSwitchingController.resetCircularListLocked(mContext);
        mHardwareKeyboardShortcutController.reset(mSettings);

        sendOnNavButtonFlagsChangedLocked();
    }
@@ -5293,6 +5297,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        // TODO: Make sure that mSwitchingController and mSettings are sharing the
        // the same enabled IMEs list.
        mSwitchingController.resetCircularListLocked(mContext);
        mHardwareKeyboardShortcutController.reset(mSettings);

        sendOnNavButtonFlagsChangedLocked();

@@ -5827,10 +5832,37 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        @Override
        public void switchKeyboardLayout(int direction) {
            synchronized (ImfLock.class) {
                if (direction > 0) {
                    switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */);
                } else {
                    // TODO(b/258853866): Support backwards switching.
                final InputMethodInfo currentImi = mMethodMap.get(getSelectedMethodIdLocked());
                if (currentImi == null) {
                    return;
                }
                final InputMethodSubtypeHandle currentSubtypeHandle =
                        InputMethodSubtypeHandle.of(currentImi, mCurrentSubtype);
                final InputMethodSubtypeHandle nextSubtypeHandle =
                        mHardwareKeyboardShortcutController.onSubtypeSwitch(currentSubtypeHandle,
                                direction > 0);
                if (nextSubtypeHandle == null) {
                    return;
                }
                final InputMethodInfo nextImi = mMethodMap.get(nextSubtypeHandle.getImeId());
                if (nextImi == null) {
                    return;
                }

                final int subtypeCount = nextImi.getSubtypeCount();
                if (subtypeCount == 0) {
                    if (nextSubtypeHandle.equals(InputMethodSubtypeHandle.of(nextImi, null))) {
                        setInputMethodLocked(nextImi.getId(), NOT_A_SUBTYPE_ID);
                    }
                    return;
                }

                for (int i = 0; i < subtypeCount; ++i) {
                    if (nextSubtypeHandle.equals(
                            InputMethodSubtypeHandle.of(nextImi, nextImi.getSubtypeAt(i)))) {
                        setInputMethodLocked(nextImi.getId(), i);
                        return;
                    }
                }
            }
        }
+57 −0
Original line number Diff line number Diff line
@@ -16,24 +16,42 @@

package com.android.server.inputmethod;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import androidx.test.ext.junit.runners.AndroidJUnit4;

import com.android.dx.mockito.inline.extended.ExtendedMockito;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

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

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

@SmallTest
@RunWith(AndroidJUnit4.class)
public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase {
public final class HardwareKeyboardShortcutControllerTest {

    @Test
    public void testForwardRotation() {
        final List<String> handles = Arrays.asList("0", "1", "2", "3");
        assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", true));
        assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "2", true));
        assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", true));
    }

    @Test
    public void testBackwardRotation() {
        final List<String> handles = Arrays.asList("0", "1", "2", "3");
        assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", false));
        assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "0", false));
        assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", false));
    }

    @Test
    public void testSwitchToNextKeyboardLayout() {
        ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController);
        InputMethodManagerInternal.get().switchKeyboardLayout(1);
        verify(mInputMethodManagerService.mSwitchingController)
                .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any());
    public void testNotMatching() {
        final List<String> handles = Arrays.asList("0", "1", "2", "3");
        assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", true));
        assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", false));
    }
}