diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java index 01c1ff61832..a0a87006d71 100644 --- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java @@ -97,6 +97,11 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater if (DBG) { Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile); } + // If device is Hearing Aid, it is compatible with HFP and A2DP. + // It would show in Available Devices group. + if (cachedDevice.isConnectedHearingAidDevice()) { + return true; + } // According to the current audio profile type, // this page will show the bluetooth device that have corresponding profile. // For example: diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java index d43290c9566..b3be18735f7 100644 --- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java +++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java @@ -97,6 +97,11 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { if (DBG) { Log.d(TAG, "isFilterMatched() current audio profile : " + currentAudioProfile); } + // If device is Hearing Aid, it is compatible with HFP and A2DP. + // It would not show in Connected Devices group. + if (cachedDevice.isConnectedHearingAidDevice()) { + return false; + } // According to the current audio profile type, // this page will show the bluetooth device that doesn't have corresponding profile. // For example: diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java index e77c27b6a71..db941a6dd7c 100644 --- a/src/com/android/settings/core/FeatureFlags.java +++ b/src/com/android/settings/core/FeatureFlags.java @@ -26,4 +26,5 @@ public class FeatureFlags { public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving"; public static final String DATA_USAGE_SETTINGS_V2 = "settings_data_usage_v2"; public static final String AUDIO_SWITCHER_SETTINGS = "settings_audio_switcher"; + public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; } diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPersistent.java b/src/com/android/settings/development/featureflags/FeatureFlagPersistent.java new file mode 100644 index 00000000000..457b36219f5 --- /dev/null +++ b/src/com/android/settings/development/featureflags/FeatureFlagPersistent.java @@ -0,0 +1,66 @@ +/* + * 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.featureflags; + +import android.content.Context; +import android.os.SystemProperties; +import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.FeatureFlagUtils; +import android.util.Log; + +import com.android.settings.core.FeatureFlags; + +import java.util.HashSet; + +/** + * Helper class to get feature persistent flag information. + */ +public class FeatureFlagPersistent { + private static final HashSet PERSISTENT_FLAGS; + static { + PERSISTENT_FLAGS = new HashSet<>(); + PERSISTENT_FLAGS.add(FeatureFlags.HEARING_AID_SETTINGS); + } + + public static boolean isEnabled(Context context, String feature) { + String value = SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + feature); + if (!TextUtils.isEmpty(value)) { + return Boolean.parseBoolean(value); + } else { + return FeatureFlagUtils.isEnabled(context, feature); + } + } + + public static void setEnabled(Context context, String feature, boolean enabled) { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + feature, enabled ? "true" : "false"); + FeatureFlagUtils.setEnabled(context, feature, enabled); + } + + public static boolean isPersistent(String feature) { + return PERSISTENT_FLAGS.contains(feature); + } + + /** + * Returns all persistent flags in their raw form. + */ + @VisibleForTesting(otherwise = VisibleForTesting.NONE) + static HashSet getAllPersistentFlags() { + return PERSISTENT_FLAGS; + } +} + diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java index b5a4a158fe3..a6cdbaa7a0a 100644 --- a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java +++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java @@ -19,23 +19,36 @@ package com.android.settings.development.featureflags; import android.content.Context; import android.support.v14.preference.SwitchPreference; import android.util.FeatureFlagUtils; +import android.util.Log; public class FeatureFlagPreference extends SwitchPreference { private final String mKey; + private final boolean mIsPersistent; public FeatureFlagPreference(Context context, String key) { super(context); mKey = key; setKey(key); setTitle(key); - setCheckedInternal(FeatureFlagUtils.isEnabled(context, mKey)); + mIsPersistent = FeatureFlagPersistent.isPersistent(key); + boolean isFeatureEnabled; + if (mIsPersistent) { + isFeatureEnabled = FeatureFlagPersistent.isEnabled(context, key); + } else { + isFeatureEnabled = FeatureFlagUtils.isEnabled(context, key); + } + setCheckedInternal(isFeatureEnabled); } @Override public void setChecked(boolean isChecked) { setCheckedInternal(isChecked); - FeatureFlagUtils.setEnabled(getContext(), mKey, isChecked); + if (mIsPersistent) { + FeatureFlagPersistent.setEnabled(getContext(), mKey, isChecked); + } else { + FeatureFlagUtils.setEnabled(getContext(), mKey, isChecked); + } } private void setCheckedInternal(boolean isChecked) { diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java index ced8fc4a2e4..0b1311d9364 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java @@ -203,6 +203,33 @@ public class AvailableMediaBluetoothDeviceUpdaterTest { verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); } + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_notInCall_addPreference() + { + mShadowAudioManager.setMode(AudioManager.MODE_NORMAL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); + } + + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_inCall_addPreference() { + mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); + } + @Test public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() { mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java index 483df01a6b8..8120d91d28f 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java @@ -204,6 +204,34 @@ public class ConnectedBluetoothDeviceUpdaterTest { verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); } + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_inCall_removePreference() + { + mShadowAudioManager.setMode(AudioManager.MODE_IN_CALL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); + } + + @Test + public void onProfileConnectionStateChanged_hearingAidDeviceConnected_notInCall_removePreference + () { + mShadowAudioManager.setMode(AudioManager.MODE_NORMAL); + when(mBluetoothDeviceUpdater. + isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true); + when(mCachedBluetoothDevice.isConnectedHearingAidDevice()).thenReturn(true); + + mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID); + + verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); + } + @Test public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() { mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, diff --git a/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java new file mode 100644 index 00000000000..c9f452de7d6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/featureflags/FeatureFlagPersistentTest.java @@ -0,0 +1,162 @@ +/* + * 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.featureflags; + +import static com.google.common.truth.Truth.assertThat; + +import com.android.settings.development.featureflags.FeatureFlagPersistent; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import android.content.Context; +import android.os.SystemProperties; +import android.provider.Settings; +import android.util.FeatureFlagUtils; +import android.util.Log; + +@RunWith(SettingsRobolectricTestRunner.class) +public class FeatureFlagPersistentTest { + + private static final String TEST_FEATURE_NAME = "test_feature"; + + private static final String PERSISTENT_FALSE_NAME = "false_persistent"; + private static final String PERSISTENT_TRUE_NAME = "true_persistent"; + private static final String VOLATILE_FALSE_NAME = "volatile_false_volatile"; + private static final String VOLATILE_TRUE_NAME = "volatile_true_volatile"; + + private Context mContext; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + + FeatureFlagPersistent.getAllPersistentFlags().add(TEST_FEATURE_NAME); + FeatureFlagUtils.getAllFeatureFlags().put(TEST_FEATURE_NAME, "false"); + + FeatureFlagUtils.getAllFeatureFlags().put(VOLATILE_FALSE_NAME, "false"); + FeatureFlagUtils.getAllFeatureFlags().put(VOLATILE_TRUE_NAME, "true"); + + FeatureFlagPersistent.getAllPersistentFlags().add(PERSISTENT_FALSE_NAME); + FeatureFlagUtils.getAllFeatureFlags().put(PERSISTENT_FALSE_NAME, "false"); + + FeatureFlagPersistent.getAllPersistentFlags().add(PERSISTENT_TRUE_NAME); + FeatureFlagUtils.getAllFeatureFlags().put(PERSISTENT_TRUE_NAME, "true"); + } + + @After + public void tearDown() { + cleanup(PERSISTENT_FALSE_NAME); + cleanup(PERSISTENT_TRUE_NAME); + cleanup(VOLATILE_FALSE_NAME); + cleanup(VOLATILE_TRUE_NAME); + cleanup(TEST_FEATURE_NAME); + } + + private void cleanup(String flagName) { + Settings.Global.putString(mContext.getContentResolver(), flagName, ""); + SystemProperties.set(FeatureFlagUtils.FFLAG_PREFIX + flagName, ""); + SystemProperties.set(FeatureFlagUtils.FFLAG_OVERRIDE_PREFIX + flagName, ""); + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + flagName, ""); + } + + /** + * Test to verify a non-persistent flag is indeed not persistent. + */ + @Test + public void isPersistent_notPersistent_shouldReturnFalse() { + assertThat(FeatureFlagPersistent.isPersistent(VOLATILE_FALSE_NAME)).isFalse(); + } + + /** + * Test to verify a persistent flag is indeed persistent. + */ + @Test + public void isPersistent_persistent_shouldReturnTrue() { + assertThat(FeatureFlagPersistent.isPersistent(PERSISTENT_TRUE_NAME)).isTrue(); + } + + /** + * Test to verify a persistent flag that is enabled should return true. + */ + @Test + public void isEnabled_enabled_shouldReturnTrue() { + assertThat(FeatureFlagPersistent.isEnabled(mContext, PERSISTENT_TRUE_NAME)).isTrue(); + } + + /** + * Test to verify a persistent flag that is disabled should return false. + */ + @Test + public void isEnabled_disabled_shouldReturnFalse() { + assertThat(FeatureFlagPersistent.isEnabled(mContext, PERSISTENT_FALSE_NAME)).isFalse(); + } + + /** + * Test to verify a persistent flag that has an enabled in system property should return true. + */ + @Test + public void isEnabled_sysPropEnabled_shouldReturnTrue() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, "true"); + FeatureFlagUtils.setEnabled(mContext, TEST_FEATURE_NAME, false); + + assertThat(FeatureFlagPersistent.isEnabled(mContext, TEST_FEATURE_NAME)).isTrue(); + } + + /** + * Test to verify a persistent flag that is disabled in system property should return false. + */ + @Test + public void isEnabled_sysPropDisabled_shouldReturnFalse() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, "false"); + FeatureFlagUtils.setEnabled(mContext, TEST_FEATURE_NAME, true); + + assertThat(FeatureFlagPersistent.isEnabled(mContext, TEST_FEATURE_NAME)).isFalse(); + } + + /** + * Test to verify setting persistent flag to enable works. + */ + @Test + public void setEnabled_sysPropTrue_shouldSetValues() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, ""); + + FeatureFlagPersistent.setEnabled(mContext, TEST_FEATURE_NAME, true); + + assertThat(SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME)).isEqualTo("true"); + assertThat(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME)).isTrue(); + } + + /** + * Test to verify setting persistent flag to disable works. + */ + @Test + public void setEnabled_sysPropFalse_shouldSetValues() { + SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME, ""); + + FeatureFlagPersistent.setEnabled(mContext, TEST_FEATURE_NAME, false); + + assertThat(SystemProperties.get(FeatureFlagUtils.PERSIST_PREFIX + TEST_FEATURE_NAME)).isEqualTo("false"); + assertThat(FeatureFlagUtils.isEnabled(mContext, TEST_FEATURE_NAME)).isFalse(); + } +} +