From 1f3261467d4c9f95508fd9a28b6aed10c09162f9 Mon Sep 17 00:00:00 2001 From: danielwbhuang Date: Fri, 24 Feb 2023 18:16:30 +0800 Subject: [PATCH] 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 --- .../KeyboardLayoutDialogFragment.java | 6 ++++ .../KeyboardLayoutPickerController.java | 16 ++++------ .../KeyboardLayoutPickerFragment.java | 8 ++--- ...wKeyboardLayoutEnabledLocalesFragment.java | 7 ++-- .../NewKeyboardLayoutPickerContent.java | 14 ++++---- .../NewKeyboardLayoutPickerController.java | 13 ++++---- .../NewKeyboardLayoutPickerFragment.java | 6 ++-- .../inputmethod/NewKeyboardSettingsUtils.java | 6 ++++ .../inputmethod/PhysicalKeyboardFragment.java | 4 +++ .../PhysicalKeyboardPreferenceController.java | 32 ++++++++++++------- ...sicalKeyboardPreferenceControllerTest.java | 29 ++++++++++++++--- 11 files changed, 93 insertions(+), 48 deletions(-) diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java index f91c9d81cad..94f0757c961 100644 --- a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java +++ b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java @@ -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) { diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java index c6a0d755151..10cd4a27931 100644 --- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java +++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java @@ -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()); diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java index a13ebc00ffd..8abde7e35b4 100644 --- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java +++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java @@ -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); } diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java index 9311c97e104..2db338254e4 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java +++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java @@ -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 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; diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerContent.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerContent.java index bb452f7a2bd..605095f103b 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerContent.java +++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerContent.java @@ -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 diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java index eb0a7aa05ff..893ad4cea01 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java +++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerController.java @@ -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())) { diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerFragment.java index 169b84b5122..88cacd2b046 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerFragment.java +++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutPickerFragment.java @@ -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; } } diff --git a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java index 0bd938e2e44..dda5500bc98 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java +++ b/src/com/android/settings/inputmethod/NewKeyboardSettingsUtils.java @@ -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()); + } } diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java index 20a634e023d..e9dba57a90e 100644 --- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java +++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java @@ -160,6 +160,10 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment final Context context = getContext(); ThreadUtils.postOnBackgroundThread(() -> { final List newHardKeyboards = getHardKeyboards(context); + if (newHardKeyboards.isEmpty()) { + getActivity().finish(); + return; + } ThreadUtils.postOnMainThread(() -> updateHardKeyboards(newHardKeyboards)); }); } diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java b/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java index 367ea80e07d..3dcae09081c 100644 --- a/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java +++ b/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceController.java @@ -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 keyboards = - PhysicalKeyboardFragment.getHardKeyboards(mContext); + List keyboards = getKeyboards(); if (keyboards.isEmpty()) { - mPreference.setSummary(R.string.keyboard_disconnected); + mPreference.setVisible(false); return; } - final List summaries = new ArrayList<>(); + updateSummary(keyboards); + } + + private void updateSummary(List keyboards) { + mPreference.setVisible(true); + List summaries = new ArrayList<>(); for (HardKeyboardDeviceInfo info : keyboards) { summaries.add(info.mDeviceName); } mPreference.setSummary(ListFormatter.getInstance().format(summaries)); } + + @VisibleForTesting + List getKeyboards() { + return PhysicalKeyboardFragment.getHardKeyboards(mContext); + } } diff --git a/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java index d675e8911e5..df052037507 100644 --- a/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java @@ -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 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