Set preference visible to false when there is no keyboard.

1. Physical keyboard settings should only show up if at least one PK is connected.
2. Update tests.
3. Fix NullPointerException.

Bug: 269983475
Bug: 270109384
Bug: 271357910
Test: atest and manual
Change-Id: If7798587fb386dbf669fc249cab304d91a26879b
This commit is contained in:
danielwbhuang
2023-02-24 18:16:30 +08:00
parent fa57dd3f2e
commit 1f3261467d
11 changed files with 93 additions and 48 deletions

View File

@@ -308,6 +308,12 @@ public class KeyboardLayoutDialogFragment extends InstrumentedDialogFragment
public Keyboards loadInBackground() {
Keyboards keyboards = new Keyboards();
InputManager im = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
if (mInputDeviceIdentifier == null || NewKeyboardSettingsUtils.getInputDevice(
im, mInputDeviceIdentifier) == null) {
keyboards.keyboardLayouts.add(null); // default layout
keyboards.current = 0;
return keyboards;
}
String[] keyboardLayoutDescriptors = im.getEnabledKeyboardLayoutsForInputDevice(
mInputDeviceIdentifier);
for (String keyboardLayoutDescriptor : keyboardLayoutDescriptors) {

View File

@@ -21,7 +21,6 @@ import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
import android.view.InputDevice;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
@@ -37,7 +36,6 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class KeyboardLayoutPickerController extends BasePreferenceController implements
InputManager.InputDeviceListener, LifecycleObserver, OnStart, OnStop {
@@ -68,15 +66,12 @@ public class KeyboardLayoutPickerController extends BasePreferenceController imp
@Override
public void onStart() {
mIm.registerInputDeviceListener(this, null);
final InputDevice inputDevice =
mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
if (inputDevice == null) {
mParent.getActivity().finish();
if (mInputDeviceIdentifier == null
|| NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) {
return;
}
mInputDeviceId = inputDevice.getId();
mInputDeviceId =
NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier).getId();
updateCheckedState();
}
@@ -150,6 +145,9 @@ public class KeyboardLayoutPickerController extends BasePreferenceController imp
}
private void createPreferenceHierarchy() {
if (mKeyboardLayouts == null) {
return;
}
for (KeyboardLayout layout : mKeyboardLayouts) {
final SwitchPreference pref = new SwitchPreference(mScreen.getContext());
pref.setTitle(layout.getLabel());

View File

@@ -19,11 +19,11 @@ package com.android.settings.inputmethod;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
public class KeyboardLayoutPickerFragment extends DashboardFragment {
private static final String TAG = "KeyboardLayoutPicker";
@@ -45,10 +45,10 @@ public class KeyboardLayoutPickerFragment extends DashboardFragment {
final InputDeviceIdentifier inputDeviceIdentifier = getActivity().getIntent().
getParcelableExtra(EXTRA_INPUT_DEVICE_IDENTIFIER);
if (inputDeviceIdentifier == null) {
getActivity().finish();
final InputManager im = context.getSystemService(InputManager.class);
if (NewKeyboardSettingsUtils.getInputDevice(im, inputDeviceIdentifier) == null) {
return;
}
use(KeyboardLayoutPickerController.class).initialize(this /*parent*/,
inputDeviceIdentifier);
}

View File

@@ -68,6 +68,9 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment
}
private void updateCheckedState() {
if (NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) {
return;
}
PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.removeAll();
List<InputMethodInfo> infoList = mImm.getEnabledInputMethodListAsUser(mUserId);
@@ -174,8 +177,8 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment
public void onStart() {
super.onStart();
mIm.registerInputDeviceListener(this, null);
final InputDevice inputDevice =
mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
InputDevice inputDevice =
NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier);
if (inputDevice == null) {
getActivity().finish();
return;

View File

@@ -19,6 +19,7 @@ package com.android.settings.inputmethod;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -33,25 +34,24 @@ public class NewKeyboardLayoutPickerContent extends DashboardFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
InputManager inputManager = getContext().getSystemService(InputManager.class);
Bundle arguments = getArguments();
final String title = arguments.getString(NewKeyboardSettingsUtils.EXTRA_TITLE);
final String layout = arguments.getString(NewKeyboardSettingsUtils.EXTRA_KEYBOARD_LAYOUT);
final int userId = arguments.getInt(NewKeyboardSettingsUtils.EXTRA_USER_ID);
final InputDeviceIdentifier inputDeviceIdentifier =
final InputDeviceIdentifier identifier =
arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER);
final InputMethodInfo inputMethodInfo =
arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_METHOD_INFO);
final InputMethodSubtype inputMethodSubtype =
arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_METHOD_SUBTYPE);
if (inputDeviceIdentifier == null) {
getActivity().finish();
if (identifier == null
|| NewKeyboardSettingsUtils.getInputDevice(inputManager, identifier) == null) {
return;
}
getActivity().setTitle(title);
use(NewKeyboardLayoutPickerController.class).initialize(this /*parent*/, userId,
inputDeviceIdentifier, inputMethodInfo, inputMethodSubtype, layout);
identifier, inputMethodInfo, inputMethodSubtype, layout);
}
@Override

View File

@@ -20,7 +20,6 @@ import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.KeyboardLayout;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -75,13 +74,12 @@ public class NewKeyboardLayoutPickerController extends BasePreferenceController
@Override
public void onStart() {
mIm.registerInputDeviceListener(this, null);
final InputDevice inputDevice =
mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
if (inputDevice == null) {
mParent.getActivity().finish();
if (mInputDeviceIdentifier == null
|| NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) {
return;
}
mInputDeviceId = inputDevice.getId();
mInputDeviceId =
NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier).getId();
}
@Override
@@ -138,6 +136,9 @@ public class NewKeyboardLayoutPickerController extends BasePreferenceController
}
private void createPreferenceHierarchy() {
if (mKeyboardLayouts == null) {
return;
}
for (KeyboardLayout layout : mKeyboardLayouts) {
final KeyboardLayoutPreference pref;
if (mLayout.equals(layout.getLabel())) {

View File

@@ -27,13 +27,11 @@ import com.android.settings.R;
public class NewKeyboardLayoutPickerFragment extends Fragment {
private ViewGroup mFragmentView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mFragmentView = (ViewGroup) inflater.inflate(
ViewGroup fragmentView = (ViewGroup) inflater.inflate(
R.layout.keyboard_layout_picker, container, false);
getActivity().getSupportFragmentManager()
.beginTransaction()
@@ -47,6 +45,6 @@ public class NewKeyboardLayoutPickerFragment extends Fragment {
.replace(R.id.keyboard_layouts, fragment)
.commit();
return mFragmentView;
return fragmentView;
}
}

View File

@@ -17,6 +17,8 @@
package com.android.settings.inputmethod;
import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -102,4 +104,8 @@ public class NewKeyboardSettingsUtils {
return mInputMethodSubtype;
}
}
static InputDevice getInputDevice(InputManager im, InputDeviceIdentifier identifier) {
return im.getInputDeviceByDescriptor(identifier.getDescriptor());
}
}

View File

@@ -160,6 +160,10 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
final Context context = getContext();
ThreadUtils.postOnBackgroundThread(() -> {
final List<HardKeyboardDeviceInfo> newHardKeyboards = getHardKeyboards(context);
if (newHardKeyboards.isEmpty()) {
getActivity().finish();
return;
}
ThreadUtils.postOnMainThread(() -> updateHardKeyboards(newHardKeyboards));
});
}

View File

@@ -22,6 +22,7 @@ import android.icu.text.ListFormatter;
import androidx.preference.Preference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.inputmethod.PhysicalKeyboardFragment.HardKeyboardDeviceInfo;
@@ -34,7 +35,6 @@ import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.ArrayList;
import java.util.List;
public class PhysicalKeyboardPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause,
InputManager.InputDeviceListener {
@@ -54,13 +54,14 @@ public class PhysicalKeyboardPreferenceController extends AbstractPreferenceCont
@Override
public boolean isAvailable() {
return mContext.getResources().getBoolean(R.bool.config_show_physical_keyboard_pref);
return !getKeyboards().isEmpty()
&& mContext.getResources().getBoolean(R.bool.config_show_physical_keyboard_pref);
}
@Override
public void updateState(Preference preference) {
mPreference = preference;
updateSummary();
updateEntry();
}
@Override
@@ -80,33 +81,42 @@ public class PhysicalKeyboardPreferenceController extends AbstractPreferenceCont
@Override
public void onInputDeviceAdded(int deviceId) {
updateSummary();
updateEntry();
}
@Override
public void onInputDeviceRemoved(int deviceId) {
updateSummary();
updateEntry();
}
@Override
public void onInputDeviceChanged(int deviceId) {
updateSummary();
updateEntry();
}
private void updateSummary() {
private void updateEntry() {
if (mPreference == null) {
return;
}
final List<HardKeyboardDeviceInfo> keyboards =
PhysicalKeyboardFragment.getHardKeyboards(mContext);
List<HardKeyboardDeviceInfo> keyboards = getKeyboards();
if (keyboards.isEmpty()) {
mPreference.setSummary(R.string.keyboard_disconnected);
mPreference.setVisible(false);
return;
}
final List<String> summaries = new ArrayList<>();
updateSummary(keyboards);
}
private void updateSummary(List<HardKeyboardDeviceInfo> keyboards) {
mPreference.setVisible(true);
List<String> summaries = new ArrayList<>();
for (HardKeyboardDeviceInfo info : keyboards) {
summaries.add(info.mDeviceName);
}
mPreference.setSummary(ListFormatter.getInstance().format(summaries));
}
@VisibleForTesting
List<HardKeyboardDeviceInfo> getKeyboards() {
return PhysicalKeyboardFragment.getHardKeyboards(mContext);
}
}

View File

@@ -24,12 +24,13 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.view.InputDevice;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.inputmethod.PhysicalKeyboardFragment.HardKeyboardDeviceInfo;
import com.android.settings.testutils.shadow.ShadowInputDevice;
import org.junit.After;
@@ -42,15 +43,24 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class PhysicalKeyboardPreferenceControllerTest {
private static final String DEVICE_NAME = "deviceName";
private static final String LAYOUT_LABEL = "deviceLayutLabel";
private static final String BLUETOOTHADDRESS = "deviceBluetoothAddress";
@Mock
private Context mContext;
@Mock
private InputManager mIm;
@Mock
private Preference mPreference;
@Mock
private InputDeviceIdentifier mIdentifier;
private PhysicalKeyboardPreferenceController mController;
@@ -69,9 +79,18 @@ public class PhysicalKeyboardPreferenceControllerTest {
@Test
public void testPhysicalKeyboard_byDefault_shouldBeShown() {
final Context context = spy(RuntimeEnvironment.application.getApplicationContext());
mController = new PhysicalKeyboardPreferenceController(context, null);
List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
keyboards.add(new HardKeyboardDeviceInfo(
DEVICE_NAME,
mIdentifier,
LAYOUT_LABEL,
BLUETOOTHADDRESS));
mController = spy(new PhysicalKeyboardPreferenceController(context, null));
when(mController.getKeyboards()).thenReturn(keyboards);
assertThat(mController.isAvailable()).isTrue();
boolean result = mController.isAvailable();
assertThat(result).isTrue();
}
@Test
@@ -85,11 +104,11 @@ public class PhysicalKeyboardPreferenceControllerTest {
@Test
@Config(shadows = ShadowInputDevice.class)
public void updateState_noKeyboard_setDisconnectedSummary() {
public void updateState_noKeyboard_setPreferenceVisibleFalse() {
ShadowInputDevice.sDeviceIds = new int[0];
mController.updateState(mPreference);
verify(mPreference).setSummary(R.string.keyboard_disconnected);
verify(mPreference).setVisible(false);
}
@Test