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

Commit d8b3008b authored by Yohei Yukawa's avatar Yohei Yukawa Committed by Automerger Merge Worker
Browse files

Merge "Fully implement PK subtype rotation shortcut" into udc-dev am: 23dc1f8d

parents 55ad6051 23dc1f8d
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));
    }
}