diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 2d9c5f37935..a6e035d5d1f 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -280,7 +280,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra controllers.add(new WifiRoamScansPreferenceController(context)); controllers.add(new MobileDataAlwaysOnPreferenceController(context)); controllers.add(new TetheringHardwareAccelPreferenceController(context)); - // select usb configuration + controllers.add(new SelectUsbConfigPreferenceController(context, lifecycle)); controllers.add(new BluetoothDeviceNoNamePreferenceController(context)); controllers.add(new BluetoothAbsoluteVolumePreferenceController(context)); controllers.add(new BluetoothInbandRingingPreferenceController(context)); diff --git a/src/com/android/settings/development/SelectUsbConfigPreferenceController.java b/src/com/android/settings/development/SelectUsbConfigPreferenceController.java new file mode 100644 index 00000000000..41fe6a3967e --- /dev/null +++ b/src/com/android/settings/development/SelectUsbConfigPreferenceController.java @@ -0,0 +1,139 @@ +/* + * 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.settings.development; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbManager; +import android.os.Bundle; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceScreen; +import android.text.TextUtils; + +import com.android.settings.R; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settingslib.core.lifecycle.events.OnCreate; +import com.android.settingslib.core.lifecycle.events.OnDestroy; + +public class SelectUsbConfigPreferenceController extends + DeveloperOptionsPreferenceController implements + Preference.OnPreferenceChangeListener, LifecycleObserver, OnCreate, OnDestroy { + + private static final String USB_CONFIGURATION_KEY = "select_usb_configuration"; + + private final String[] mListValues; + private final String[] mListSummaries; + private final UsbManager mUsbManager; + private BroadcastReceiver mUsbReceiver; + private ListPreference mPreference; + + public SelectUsbConfigPreferenceController(Context context, Lifecycle lifecycle) { + super(context); + + mListValues = context.getResources().getStringArray(R.array.usb_configuration_values); + mListSummaries = context.getResources().getStringArray(R.array.usb_configuration_titles); + mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); + mUsbReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mPreference != null) { + updateUsbConfigurationValues(); + } + } + }; + if (lifecycle != null) { + lifecycle.addObserver(this); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_STATE); + mContext.registerReceiver(mUsbReceiver, filter); + } + + @Override + public String getPreferenceKey() { + return USB_CONFIGURATION_KEY; + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + + mPreference = (ListPreference) screen.findPreference(getPreferenceKey()); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + writeUsbConfigurationOption(newValue.toString()); + updateUsbConfigurationValues(); + return true; + } + + @Override + public void updateState(Preference preference) { + updateUsbConfigurationValues(); + } + + @Override + public void onDestroy() { + mContext.unregisterReceiver(mUsbReceiver); + } + + @Override + protected void onDeveloperOptionsSwitchEnabled() { + mPreference.setEnabled(true); + } + + @Override + protected void onDeveloperOptionsSwitchDisabled() { + mPreference.setEnabled(false); + } + + @VisibleForTesting + void setCurrentFunction(String newValue, boolean usbDataUnlocked) { + mUsbManager.setCurrentFunction(newValue, usbDataUnlocked); + } + + private void updateUsbConfigurationValues() { + int index = 0; + for (int i = 0; i < mListValues.length; i++) { + if (mUsbManager.isFunctionEnabled(mListValues[i])) { + index = i; + break; + } + } + mPreference.setValue(mListValues[index]); + mPreference.setSummary(mListSummaries[index]); + } + + private void writeUsbConfigurationOption(String newValue) { + if (TextUtils.equals(newValue, "none")) { + setCurrentFunction(newValue, false); + } else { + setCurrentFunction(newValue, true); + } + } + +} diff --git a/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java new file mode 100644 index 00000000000..8b96af0f7e9 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/SelectUsbConfigPreferenceControllerTest.java @@ -0,0 +1,162 @@ +/* + * 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.settings.development; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.hardware.usb.UsbManager; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class SelectUsbConfigPreferenceControllerTest { + + @Mock + private ListPreference mPreference; + @Mock + private PreferenceScreen mScreen; + @Mock + private UsbManager mUsbManager; + + private Context mContext; + private Lifecycle mLifecycle; + private SelectUsbConfigPreferenceController mController; + + /** + * Array Values Key + * + * 0: Charging + * 1: MTP + * 2: PTP + * 3: RNDIS + * 4: Audio Source + * 5: MIDI + */ + private String[] mValues; + private String[] mSummaries; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mLifecycle = new Lifecycle(); + mContext = spy(RuntimeEnvironment.application); + doReturn(mUsbManager).when(mContext).getSystemService(Context.USB_SERVICE); + mValues = mContext.getResources().getStringArray(R.array.usb_configuration_values); + mSummaries = mContext.getResources().getStringArray(R.array.usb_configuration_titles); + mController = spy(new SelectUsbConfigPreferenceController(mContext, mLifecycle)); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); + mController.displayPreference(mScreen); + + } + + @Test + public void onPreferenceChange_setCharging_shouldEnableCharging() { + when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true); + doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean()); + mController.onPreferenceChange(mPreference, mValues[0]); + + verify(mController).setCurrentFunction(mValues[0], false /* usb data unlock */); + } + + @Test + public void onPreferenceChange_setMtp_shouldEnableMtp() { + when(mUsbManager.isFunctionEnabled(mValues[1])).thenReturn(true); + doNothing().when(mController).setCurrentFunction(anyString(), anyBoolean()); + mController.onPreferenceChange(mPreference, mValues[1]); + + verify(mController).setCurrentFunction(mValues[1], true /* usb data unlock */); + } + + @Test + public void updateState_chargingEnabled_shouldSetPreferenceToCharging() { + when(mUsbManager.isFunctionEnabled(mValues[0])).thenReturn(true); + + mController.updateState(mPreference); + + verify(mPreference).setValue(mValues[0]); + verify(mPreference).setSummary(mSummaries[0]); + } + + @Test + public void updateState_RndisEnabled_shouldEnableRndis() { + when(mUsbManager.isFunctionEnabled(mValues[3])).thenReturn(true); + + mController.updateState(mPreference); + + verify(mPreference).setValue(mValues[3]); + verify(mPreference).setSummary(mSummaries[3]); + } + + @Test + public void updateState_noValueSet_shouldEnableChargingAsDefault() { + mController.updateState(mPreference); + + verify(mPreference).setValue(mValues[0]); + verify(mPreference).setSummary(mSummaries[0]); + } + + @Test + public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference() { + mController.onDeveloperOptionsSwitchDisabled(); + + verify(mPreference).setEnabled(false); + } + + @Test + public void onDeveloperOptionsSwitchEnabled_shouldEnablePreference() { + mController.onDeveloperOptionsSwitchEnabled(); + + verify(mPreference).setEnabled(true); + } + + @Test + public void onCreate_shouldRegisterReceiver() { + mLifecycle.onCreate(null /* bundle */); + + verify(mContext).registerReceiver(any(), any()); + } + + @Test + public void onDestroy_shouldUnregisterReceiver() { + doNothing().when(mContext).unregisterReceiver(any()); + mLifecycle.onDestroy(); + + verify(mContext).unregisterReceiver(any()); + } +}