diff --git a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java index 0fcec05bd43..95e663bf50f 100644 --- a/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java +++ b/src/com/android/settings/development/BluetoothA2dpHwOffloadPreferenceController.java @@ -66,6 +66,25 @@ public class BluetoothA2dpHwOffloadPreferenceController extends DeveloperOptions } } + @Override + protected void onDeveloperOptionsSwitchDisabled() { + super.onDeveloperOptionsSwitchDisabled(); + final boolean offloadSupported = + SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false); + if (offloadSupported) { + ((SwitchPreference) mPreference).setChecked(false); + SystemProperties.set(A2DP_OFFLOAD_DISABLED_PROPERTY, "false"); + } + } + + public boolean isDefaultValue() { + final boolean offloadSupported = + SystemProperties.getBoolean(A2DP_OFFLOAD_SUPPORTED_PROPERTY, false); + final boolean offloadDisabled = + SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false); + return offloadSupported ? !offloadDisabled : true; + } + public void onA2dpHwDialogConfirmed() { final boolean offloadDisabled = SystemProperties.getBoolean(A2DP_OFFLOAD_DISABLED_PROPERTY, false); diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java index 762686ad51f..4ddcc364c6d 100644 --- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java +++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java @@ -221,7 +221,16 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra if (isChecked) { EnableDevelopmentSettingWarningDialog.show(this /* host */); } else { - disableDeveloperOptions(); + final BluetoothA2dpHwOffloadPreferenceController controller = + getDevelopmentOptionsController( + BluetoothA2dpHwOffloadPreferenceController.class); + // If A2DP hardware offload isn't default value, we must reboot after disable + // developer options. Show a dialog for the user to confirm. + if (controller == null || controller.isDefaultValue()) { + disableDeveloperOptions(); + } else { + DisableDevSettingsDialogFragment.show(this /* host */); + } } } } @@ -380,6 +389,15 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra mSwitchBar.setChecked(false); } + void onDisableDevelopmentOptionsConfirmed() { + disableDeveloperOptions(); + } + + void onDisableDevelopmentOptionsRejected() { + // Reset the toggle + mSwitchBar.setChecked(true); + } + private static List buildPreferenceControllers(Context context, Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment, BluetoothA2dpConfigStore bluetoothA2dpConfigStore) { diff --git a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java new file mode 100644 index 00000000000..9b3ba588256 --- /dev/null +++ b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 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.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.PowerManager; +import android.util.Log; +import androidx.annotation.VisibleForTesting; +import androidx.fragment.app.Fragment; + +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentManager; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; + +public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment + implements DialogInterface.OnClickListener { + + public static final String TAG = "DisableDevSettingDlg"; + + @VisibleForTesting + static DisableDevSettingsDialogFragment newInstance() { + final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment(); + return dialog; + } + + public static void show(DevelopmentSettingsDashboardFragment host) { + final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment(); + dialog.setTargetFragment(host, 0 /* requestCode */); + final FragmentManager manager = host.getActivity().getSupportFragmentManager(); + dialog.show(manager, TAG); + } + + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.DIALOG_DISABLE_DEVELOPMENT_OPTIONS; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Reuse the same text of disable_a2dp_hw_offload_dialog. + // The text is generic enough to be used for turning off Dev options. + return new AlertDialog.Builder(getActivity()) + .setMessage(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message) + .setTitle(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title) + .setPositiveButton( + R.string.bluetooth_disable_a2dp_hw_offload_dialog_confirm, this) + .setNegativeButton( + R.string.bluetooth_disable_a2dp_hw_offload_dialog_cancel, this) + .create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + Fragment fragment = getTargetFragment(); + if (!(fragment instanceof DevelopmentSettingsDashboardFragment)){ + Log.e(TAG, "getTargetFragment return unexpected type"); + } + + final DevelopmentSettingsDashboardFragment host = + (DevelopmentSettingsDashboardFragment) fragment; + if (which == DialogInterface.BUTTON_POSITIVE) { + host.onDisableDevelopmentOptionsConfirmed(); + PowerManager pm = getContext().getSystemService(PowerManager.class); + pm.reboot(null); + } else { + host.onDisableDevelopmentOptionsRejected(); + } + } +} diff --git a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java index d2c9b65455d..5eb21bdadc6 100644 --- a/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java +++ b/tests/robotests/src/com/android/settings/development/DevelopmentSettingsDashboardFragmentTest.java @@ -28,10 +28,15 @@ import android.content.Context; import android.provider.SearchIndexableResource; import android.provider.Settings; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.FragmentActivity; + import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.shadow.SettingsShadowResources; +import com.android.settings.testutils.shadow.SettingsShadowResourcesImpl; +import com.android.settings.testutils.shadow.ShadowAlertDialogCompat; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.ToggleSwitch; @@ -47,12 +52,14 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; +import org.robolectric.shadows.androidx.fragment.FragmentController; import org.robolectric.util.ReflectionHelpers; import java.util.List; @RunWith(SettingsRobolectricTestRunner.class) -@Config(shadows = ShadowUserManager.class) +@Config(shadows = {ShadowUserManager.class, ShadowAlertDialogCompat.class, + SettingsShadowResourcesImpl.class}) public class DevelopmentSettingsDashboardFragmentTest { private ToggleSwitch mSwitch; @@ -178,6 +185,29 @@ public class DevelopmentSettingsDashboardFragmentTest { assertThat(DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)).isFalse(); } + @Test + @Config(shadows = ShadowDisableDevSettingsDialogFragment.class) + public void onSwitchChanged_turnOff_andOffloadIsNotDefaultValue_shouldShowWarningDialog() { + final BluetoothA2dpHwOffloadPreferenceController controller = + mock(BluetoothA2dpHwOffloadPreferenceController.class); + when(mDashboard.getContext()).thenReturn(mContext); + when(mDashboard.getDevelopmentOptionsController( + BluetoothA2dpHwOffloadPreferenceController.class)).thenReturn(controller); + when(controller.isDefaultValue()).thenReturn(false); + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 1); + + mDashboard.onSwitchChanged(mSwitch, false /* isChecked */); + + AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog(); + assertThat(dialog).isNotNull(); + ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog); + assertThat(shadowDialog.getTitle()).isEqualTo( + mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_title)); + assertThat(shadowDialog.getMessage()).isEqualTo( + mContext.getString(R.string.bluetooth_disable_a2dp_hw_offload_dialog_message)); + } + @Test public void onOemUnlockDialogConfirmed_shouldCallControllerOemConfirmed() { final OemUnlockPreferenceController controller = mock(OemUnlockPreferenceController.class); @@ -264,6 +294,18 @@ public class DevelopmentSettingsDashboardFragmentTest { } } + @Implements(DisableDevSettingsDialogFragment.class) + public static class ShadowDisableDevSettingsDialogFragment { + + @Implementation + public static void show(DevelopmentSettingsDashboardFragment host) { + DisableDevSettingsDialogFragment mFragment = + spy(DisableDevSettingsDialogFragment.newInstance()); + FragmentController.setupFragment(mFragment, FragmentActivity.class, + 0 /* containerViewId */, null /* bundle */); + } + } + @Implements(PictureColorModePreferenceController.class) public static class ShadowPictureColorModePreferenceController { @Implementation