[Audiosharing] Created test for the name and password preferences.
Test: atest -c com.android.settings.connecteddevice.audiosharing Flag: com.android.settingslib.flags.enable_le_audio_qr_code_private_broadcast_sharing Bug: 345686602 Change-Id: I9036e560e29aba9555c207dce1b2018be010bca8
This commit is contained in:
@@ -95,14 +95,14 @@ public class AudioSharingNamePreference extends ValidatedEditTextPreference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void configureInvisibleStateForQrCodeIcon(ImageButton shareButton, View divider) {
|
private void configureInvisibleStateForQrCodeIcon(ImageButton shareButton, View divider) {
|
||||||
divider.setVisibility(View.INVISIBLE);
|
divider.setVisibility(View.GONE);
|
||||||
shareButton.setVisibility(View.INVISIBLE);
|
shareButton.setVisibility(View.GONE);
|
||||||
shareButton.setOnClickListener(null);
|
shareButton.setOnClickListener(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchAudioSharingQrCodeFragment() {
|
private void launchAudioSharingQrCodeFragment() {
|
||||||
new SubSettingLauncher(getContext())
|
new SubSettingLauncher(getContext())
|
||||||
.setTitleText(getContext().getString(R.string.audio_streams_qr_code_page_title))
|
.setTitleRes(R.string.audio_streams_qr_code_page_title)
|
||||||
.setDestination(AudioStreamsQrCodeFragment.class.getName())
|
.setDestination(AudioStreamsQrCodeFragment.class.getName())
|
||||||
.setSourceMetricsCategory(SettingsEnums.AUDIO_SHARING_SETTINGS)
|
.setSourceMetricsCategory(SettingsEnums.AUDIO_SHARING_SETTINGS)
|
||||||
.launch();
|
.launch();
|
||||||
|
@@ -26,6 +26,7 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
@@ -56,7 +57,8 @@ public class AudioSharingNamePreferenceController extends BasePreferenceControll
|
|||||||
private static final boolean DEBUG = BluetoothUtils.D;
|
private static final boolean DEBUG = BluetoothUtils.D;
|
||||||
private static final String PREF_KEY = "audio_sharing_stream_name";
|
private static final String PREF_KEY = "audio_sharing_stream_name";
|
||||||
|
|
||||||
private final BluetoothLeBroadcast.Callback mBroadcastCallback =
|
@VisibleForTesting
|
||||||
|
final BluetoothLeBroadcast.Callback mBroadcastCallback =
|
||||||
new BluetoothLeBroadcast.Callback() {
|
new BluetoothLeBroadcast.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onBroadcastMetadataChanged(
|
public void onBroadcastMetadataChanged(
|
||||||
|
@@ -19,12 +19,19 @@ package com.android.settings.connecteddevice.audiosharing;
|
|||||||
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.isBroadcasting;
|
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.isBroadcasting;
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
@@ -41,15 +48,19 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
public class AudioSharingPasswordPreferenceController extends BasePreferenceController
|
public class AudioSharingPasswordPreferenceController extends BasePreferenceController
|
||||||
implements ValidatedEditTextPreference.Validator,
|
implements ValidatedEditTextPreference.Validator,
|
||||||
AudioSharingPasswordPreference.OnDialogEventListener {
|
AudioSharingPasswordPreference.OnDialogEventListener,
|
||||||
|
DefaultLifecycleObserver {
|
||||||
private static final String TAG = "AudioSharingPasswordPreferenceController";
|
private static final String TAG = "AudioSharingPasswordPreferenceController";
|
||||||
private static final String PREF_KEY = "audio_sharing_stream_password";
|
private static final String PREF_KEY = "audio_sharing_stream_password";
|
||||||
private static final String SHARED_PREF_NAME = "audio_sharing_settings";
|
private static final String SHARED_PREF_NAME = "audio_sharing_settings";
|
||||||
private static final String SHARED_PREF_KEY = "default_password";
|
private static final String SHARED_PREF_KEY = "default_password";
|
||||||
|
@Nullable private final ContentResolver mContentResolver;
|
||||||
|
@Nullable private final SharedPreferences mSharedPref;
|
||||||
@Nullable private final LocalBluetoothManager mBtManager;
|
@Nullable private final LocalBluetoothManager mBtManager;
|
||||||
@Nullable private final LocalBluetoothLeBroadcast mBroadcast;
|
@Nullable private final LocalBluetoothLeBroadcast mBroadcast;
|
||||||
@Nullable private AudioSharingPasswordPreference mPreference;
|
@Nullable private AudioSharingPasswordPreference mPreference;
|
||||||
|
private final ContentObserver mSettingsObserver;
|
||||||
|
private final SharedPreferences.OnSharedPreferenceChangeListener mSharedPrefChangeListener;
|
||||||
private final AudioSharingPasswordValidator mAudioSharingPasswordValidator;
|
private final AudioSharingPasswordValidator mAudioSharingPasswordValidator;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
|
|
||||||
@@ -61,9 +72,44 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont
|
|||||||
? mBtManager.getProfileManager().getLeAudioBroadcastProfile()
|
? mBtManager.getProfileManager().getLeAudioBroadcastProfile()
|
||||||
: null;
|
: null;
|
||||||
mAudioSharingPasswordValidator = new AudioSharingPasswordValidator();
|
mAudioSharingPasswordValidator = new AudioSharingPasswordValidator();
|
||||||
|
mContentResolver = context.getContentResolver();
|
||||||
|
mSettingsObserver = new PasswordSettingsObserver();
|
||||||
|
mSharedPref = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
|
||||||
|
mSharedPrefChangeListener = new PasswordSharedPrefChangeListener();
|
||||||
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart(@NonNull LifecycleOwner owner) {
|
||||||
|
if (!isAvailable()) {
|
||||||
|
Log.d(TAG, "Feature is not available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mContentResolver != null) {
|
||||||
|
mContentResolver.registerContentObserver(
|
||||||
|
Settings.Secure.getUriFor(Settings.Secure.BLUETOOTH_LE_BROADCAST_CODE),
|
||||||
|
false,
|
||||||
|
mSettingsObserver);
|
||||||
|
}
|
||||||
|
if (mSharedPref != null) {
|
||||||
|
mSharedPref.registerOnSharedPreferenceChangeListener(mSharedPrefChangeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop(@NonNull LifecycleOwner owner) {
|
||||||
|
if (!isAvailable()) {
|
||||||
|
Log.d(TAG, "Feature is not available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mContentResolver != null) {
|
||||||
|
mContentResolver.unregisterContentObserver(mSettingsObserver);
|
||||||
|
}
|
||||||
|
if (mSharedPref != null) {
|
||||||
|
mSharedPref.unregisterOnSharedPreferenceChangeListener(mSharedPrefChangeListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvailabilityStatus() {
|
public int getAvailabilityStatus() {
|
||||||
return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
return AudioSharingUtils.isFeatureEnabled() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
|
||||||
@@ -125,7 +171,6 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont
|
|||||||
persistDefaultPassword(mContext, password);
|
persistDefaultPassword(mContext, password);
|
||||||
mBroadcast.setBroadcastCode(
|
mBroadcast.setBroadcastCode(
|
||||||
isPublicBroadcast ? new byte[0] : password.getBytes());
|
isPublicBroadcast ? new byte[0] : password.getBytes());
|
||||||
updatePreference();
|
|
||||||
mMetricsFeatureProvider.action(
|
mMetricsFeatureProvider.action(
|
||||||
mContext,
|
mContext,
|
||||||
SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED,
|
SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED,
|
||||||
@@ -164,32 +209,52 @@ public class AudioSharingPasswordPreferenceController extends BasePreferenceCont
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void persistDefaultPassword(Context context, String defaultPassword) {
|
private class PasswordSettingsObserver extends ContentObserver {
|
||||||
|
PasswordSettingsObserver() {
|
||||||
|
super(new Handler(Looper.getMainLooper()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange) {
|
||||||
|
Log.d(TAG, "onChange, broadcast password has been changed");
|
||||||
|
updatePreference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PasswordSharedPrefChangeListener
|
||||||
|
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(
|
||||||
|
SharedPreferences sharedPreferences, @Nullable String key) {
|
||||||
|
if (!SHARED_PREF_KEY.equals(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d(TAG, "onSharedPreferenceChanged, default password has been changed");
|
||||||
|
updatePreference();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistDefaultPassword(Context context, String defaultPassword) {
|
||||||
if (getDefaultPassword(context).equals(defaultPassword)) {
|
if (getDefaultPassword(context).equals(defaultPassword)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (mSharedPref == null) {
|
||||||
SharedPreferences sharedPref =
|
|
||||||
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
|
|
||||||
if (sharedPref == null) {
|
|
||||||
Log.w(TAG, "persistDefaultPassword(): sharedPref is empty!");
|
Log.w(TAG, "persistDefaultPassword(): sharedPref is empty!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPreferences.Editor editor = sharedPref.edit();
|
SharedPreferences.Editor editor = mSharedPref.edit();
|
||||||
editor.putString(SHARED_PREF_KEY, defaultPassword);
|
editor.putString(SHARED_PREF_KEY, defaultPassword);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getDefaultPassword(Context context) {
|
private String getDefaultPassword(Context context) {
|
||||||
SharedPreferences sharedPref =
|
if (mSharedPref == null) {
|
||||||
context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
|
|
||||||
if (sharedPref == null) {
|
|
||||||
Log.w(TAG, "getDefaultPassword(): sharedPref is empty!");
|
Log.w(TAG, "getDefaultPassword(): sharedPref is empty!");
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
String value = sharedPref.getString(SHARED_PREF_KEY, "");
|
String value = mSharedPref.getString(SHARED_PREF_KEY, "");
|
||||||
if (value != null && value.isEmpty()) {
|
if (value != null && value.isEmpty()) {
|
||||||
Log.w(TAG, "getDefaultPassword(): default password is empty!");
|
Log.w(TAG, "getDefaultPassword(): default password is empty!");
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothLeBroadcast;
|
||||||
|
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.bluetooth.Utils;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||||
|
import com.android.settingslib.bluetooth.BluetoothEventManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
|
import com.android.settingslib.bluetooth.VolumeControlProfile;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.flags.Flags;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadow.api.Shadow;
|
||||||
|
import org.robolectric.shadows.ShadowLooper;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowBluetoothAdapter.class,
|
||||||
|
ShadowBluetoothUtils.class,
|
||||||
|
})
|
||||||
|
public class AudioSharingNamePreferenceControllerTest {
|
||||||
|
private static final String PREF_KEY = "audio_sharing_stream_name";
|
||||||
|
private static final String BROADCAST_NAME = "broadcast_name";
|
||||||
|
private static final CharSequence UPDATED_NAME = "updated_name";
|
||||||
|
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
@Spy Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
@Mock private LocalBluetoothLeBroadcast mBroadcast;
|
||||||
|
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
|
||||||
|
@Mock private VolumeControlProfile mVolumeControl;
|
||||||
|
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||||
|
@Mock private BluetoothEventManager mEventManager;
|
||||||
|
@Mock private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
@Mock private PreferenceScreen mScreen;
|
||||||
|
private AudioSharingNamePreferenceController mController;
|
||||||
|
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||||
|
private Lifecycle mLifecycle;
|
||||||
|
private LifecycleOwner mLifecycleOwner;
|
||||||
|
private AudioSharingNamePreference mPreference;
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||||
|
mShadowBluetoothAdapter.setEnabled(true);
|
||||||
|
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||||
|
mLocalBtManager = Utils.getLocalBtManager(mContext);
|
||||||
|
when(mLocalBtManager.getEventManager()).thenReturn(mEventManager);
|
||||||
|
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
|
||||||
|
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
|
||||||
|
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
|
||||||
|
when(mProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
|
||||||
|
when(mBroadcast.isProfileReady()).thenReturn(true);
|
||||||
|
when(mAssistant.isProfileReady()).thenReturn(true);
|
||||||
|
when(mVolumeControl.isProfileReady()).thenReturn(true);
|
||||||
|
when(mBroadcast.isProfileReady()).thenReturn(true);
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
mLifecycleOwner = () -> mLifecycle;
|
||||||
|
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||||
|
mController = new AudioSharingNamePreferenceController(mContext, PREF_KEY);
|
||||||
|
mPreference = spy(new AudioSharingNamePreference(mContext));
|
||||||
|
when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_flagOn_available() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_flagOff_unsupported() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mBroadcast, never())
|
||||||
|
.registerServiceCallBack(
|
||||||
|
any(Executor.class), any(BluetoothLeBroadcast.Callback.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOn_registerCallbacks() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mBroadcast)
|
||||||
|
.registerServiceCallBack(
|
||||||
|
any(Executor.class), any(BluetoothLeBroadcast.Callback.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOn_serviceNotReady_registerCallbacks() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
when(mBroadcast.isProfileReady()).thenReturn(false);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mProfileManager)
|
||||||
|
.addServiceListener(any(LocalBluetoothProfileManager.ServiceListener.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onServiceConnected_removeCallbacks() {
|
||||||
|
mController.onServiceConnected();
|
||||||
|
verify(mProfileManager)
|
||||||
|
.removeServiceListener(any(LocalBluetoothProfileManager.ServiceListener.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
mController.onStop(mLifecycleOwner);
|
||||||
|
verify(mBroadcast, never())
|
||||||
|
.unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_flagOn_unregisterCallbacks() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
mController.onStop(mLifecycleOwner);
|
||||||
|
verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_updateName_showIcon() {
|
||||||
|
when(mBroadcast.getBroadcastName()).thenReturn(BROADCAST_NAME);
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(true);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
assertThat(mPreference.getText()).isEqualTo(BROADCAST_NAME);
|
||||||
|
assertThat(mPreference.getSummary()).isEqualTo(BROADCAST_NAME);
|
||||||
|
verify(mPreference).setValidator(any());
|
||||||
|
verify(mPreference).setShowQrCodeIcon(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_updateName_hideIcon() {
|
||||||
|
when(mBroadcast.getBroadcastName()).thenReturn(BROADCAST_NAME);
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(false);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
assertThat(mPreference.getText()).isEqualTo(BROADCAST_NAME);
|
||||||
|
assertThat(mPreference.getSummary()).isEqualTo(BROADCAST_NAME);
|
||||||
|
verify(mPreference).setValidator(any());
|
||||||
|
verify(mPreference).setShowQrCodeIcon(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceChange_noChange_doNothing() {
|
||||||
|
when(mPreference.getSummary()).thenReturn(BROADCAST_NAME);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
boolean changed = mController.onPreferenceChange(mPreference, BROADCAST_NAME);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast, never()).setBroadcastName(anyString());
|
||||||
|
verify(mBroadcast, never()).setProgramInfo(anyString());
|
||||||
|
verify(mBroadcast, never()).updateBroadcast();
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt());
|
||||||
|
|
||||||
|
assertThat(changed).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceChange_changed_updateName_broadcasting() {
|
||||||
|
when(mPreference.getSummary()).thenReturn(BROADCAST_NAME);
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(true);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
boolean changed = mController.onPreferenceChange(mPreference, UPDATED_NAME);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast).setBroadcastName(UPDATED_NAME.toString());
|
||||||
|
verify(mBroadcast).setProgramInfo(UPDATED_NAME.toString());
|
||||||
|
verify(mBroadcast).updateBroadcast();
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider)
|
||||||
|
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_NAME_UPDATED, 1);
|
||||||
|
assertThat(changed).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceChange_changed_updateName_notBroadcasting() {
|
||||||
|
when(mPreference.getSummary()).thenReturn(BROADCAST_NAME);
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(false);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
boolean changed = mController.onPreferenceChange(mPreference, UPDATED_NAME);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast).setBroadcastName(UPDATED_NAME.toString());
|
||||||
|
verify(mBroadcast).setProgramInfo(UPDATED_NAME.toString());
|
||||||
|
verify(mBroadcast, never()).updateBroadcast();
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider)
|
||||||
|
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_NAME_UPDATED, 0);
|
||||||
|
assertThat(changed).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void unrelatedCallbacks_doNotUpdateIcon() {
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 0);
|
||||||
|
mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 0, /* broadcastId= */ 0);
|
||||||
|
mController.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 0);
|
||||||
|
mController.mBroadcastCallback.onBroadcastUpdateFailed(
|
||||||
|
/* reason= */ 0, /* broadcastId= */ 0);
|
||||||
|
mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 0, /* broadcastId= */ 0);
|
||||||
|
mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 0, /* broadcastId= */ 0);
|
||||||
|
mController.mBroadcastCallback.onPlaybackStopped(/* reason= */ 0, /* broadcastId= */ 0);
|
||||||
|
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
// Should be called once in displayPreference, but not called after callbacks
|
||||||
|
verify(mPreference).setShowQrCodeIcon(anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void broadcastOnCallback_updateIcon() {
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.mBroadcastCallback.onBroadcastMetadataChanged(
|
||||||
|
/* broadcastId= */ 0, mock(BluetoothLeBroadcastMetadata.class));
|
||||||
|
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
// Should be called twice, in displayPreference and also after callback
|
||||||
|
verify(mPreference, times(2)).setShowQrCodeIcon(anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void broadcastStopCallback_updateIcon() {
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 0, /* broadcastId= */ 0);
|
||||||
|
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
// Should be called twice, in displayPreference and also after callback
|
||||||
|
verify(mPreference, times(2)).setShowQrCodeIcon(anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void idTextValid_emptyString() {
|
||||||
|
boolean valid = mController.isTextValid("");
|
||||||
|
|
||||||
|
assertThat(valid).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void idTextValid_validName() {
|
||||||
|
boolean valid = mController.isTextValid("valid name");
|
||||||
|
|
||||||
|
assertThat(valid).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsQrCodeFragment;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioSharingNamePreferenceTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
private Context mContext;
|
||||||
|
private AudioSharingNamePreference mPreference;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mPreference = spy(new AudioSharingNamePreference(mContext, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void initialize_correctLayout() {
|
||||||
|
assertThat(mPreference.getLayoutResource())
|
||||||
|
.isEqualTo(
|
||||||
|
com.android.settingslib.widget.preference.twotarget.R.layout
|
||||||
|
.preference_two_target);
|
||||||
|
assertThat(mPreference.getWidgetLayoutResource())
|
||||||
|
.isEqualTo(R.layout.preference_widget_qrcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_correctLayout_noQrCodeButton() {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||||
|
View view = inflater.inflate(mPreference.getLayoutResource(), null);
|
||||||
|
LinearLayout widgetView = view.findViewById(android.R.id.widget_frame);
|
||||||
|
assertThat(widgetView).isNotNull();
|
||||||
|
inflater.inflate(mPreference.getWidgetLayoutResource(), widgetView, true);
|
||||||
|
|
||||||
|
var holder = PreferenceViewHolder.createInstanceForTests(view);
|
||||||
|
mPreference.setShowQrCodeIcon(false);
|
||||||
|
mPreference.onBindViewHolder(holder);
|
||||||
|
|
||||||
|
ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon);
|
||||||
|
View divider =
|
||||||
|
holder.findViewById(
|
||||||
|
com.android.settingslib.widget.preference.twotarget.R.id
|
||||||
|
.two_target_divider);
|
||||||
|
|
||||||
|
assertThat(shareButton).isNotNull();
|
||||||
|
assertThat(shareButton.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
assertThat(shareButton.hasOnClickListeners()).isFalse();
|
||||||
|
assertThat(divider).isNotNull();
|
||||||
|
assertThat(divider.getVisibility()).isEqualTo(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindViewHolder_correctLayout_showQrCodeButton() {
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||||
|
View view = inflater.inflate(mPreference.getLayoutResource(), null);
|
||||||
|
LinearLayout widgetView = view.findViewById(android.R.id.widget_frame);
|
||||||
|
assertThat(widgetView).isNotNull();
|
||||||
|
inflater.inflate(mPreference.getWidgetLayoutResource(), widgetView, true);
|
||||||
|
|
||||||
|
var holder = PreferenceViewHolder.createInstanceForTests(view);
|
||||||
|
mPreference.setShowQrCodeIcon(true);
|
||||||
|
mPreference.onBindViewHolder(holder);
|
||||||
|
|
||||||
|
ImageButton shareButton = (ImageButton) holder.findViewById(R.id.button_icon);
|
||||||
|
View divider =
|
||||||
|
holder.findViewById(
|
||||||
|
com.android.settingslib.widget.preference.twotarget.R.id
|
||||||
|
.two_target_divider);
|
||||||
|
|
||||||
|
assertThat(shareButton).isNotNull();
|
||||||
|
assertThat(shareButton.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
assertThat(shareButton.getDrawable()).isNotNull();
|
||||||
|
assertThat(shareButton.hasOnClickListeners()).isTrue();
|
||||||
|
assertThat(divider).isNotNull();
|
||||||
|
assertThat(divider.getVisibility()).isEqualTo(View.VISIBLE);
|
||||||
|
|
||||||
|
// mContext is not an Activity context, calling startActivity() from outside of an Activity
|
||||||
|
// context requires the FLAG_ACTIVITY_NEW_TASK flag, create a mock to avoid this
|
||||||
|
// AndroidRuntimeException.
|
||||||
|
Context activityContext = mock(Context.class);
|
||||||
|
when(mPreference.getContext()).thenReturn(activityContext);
|
||||||
|
shareButton.callOnClick();
|
||||||
|
|
||||||
|
ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||||
|
verify(activityContext).startActivity(argumentCaptor.capture());
|
||||||
|
|
||||||
|
Intent intent = argumentCaptor.getValue();
|
||||||
|
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(AudioStreamsQrCodeFragment.class.getName());
|
||||||
|
assertThat(intent.getIntExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, 0))
|
||||||
|
.isEqualTo(R.string.audio_streams_qr_code_page_title);
|
||||||
|
assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 0))
|
||||||
|
.isEqualTo(SettingsEnums.AUDIO_SHARING_SETTINGS);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioSharingNameTextValidatorTest {
|
||||||
|
private AudioSharingNameTextValidator mValidator;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mValidator = new AudioSharingNameTextValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidNames() {
|
||||||
|
assertThat(mValidator.isTextValid("ValidName")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("12345678")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("Name_With_Underscores")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("ÄÖÜß")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("ThisNameIsExactly32Characters!")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidNames() {
|
||||||
|
assertThat(mValidator.isTextValid(null)).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("")).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("abc")).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("ThisNameIsWayTooLongForAnAudioSharingName")).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("Invalid\uDC00")).isFalse();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,335 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||||
|
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothStatusCodes;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule;
|
||||||
|
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.bluetooth.Utils;
|
||||||
|
import com.android.settings.testutils.FakeFeatureFactory;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||||
|
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||||
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
|
import com.android.settingslib.flags.Flags;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
import org.robolectric.shadow.api.Shadow;
|
||||||
|
import org.robolectric.shadows.ShadowLooper;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(
|
||||||
|
shadows = {
|
||||||
|
ShadowBluetoothAdapter.class,
|
||||||
|
ShadowBluetoothUtils.class,
|
||||||
|
})
|
||||||
|
public class AudioSharingPasswordPreferenceControllerTest {
|
||||||
|
private static final String PREF_KEY = "audio_sharing_stream_password";
|
||||||
|
private static final String SHARED_PREF_KEY = "default_password";
|
||||||
|
private static final String BROADCAST_PASSWORD = "password";
|
||||||
|
private static final String EDITTEXT_PASSWORD = "edittext_password";
|
||||||
|
private static final String HIDDEN_PASSWORD = "********";
|
||||||
|
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||||
|
@Spy Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
@Mock private LocalBluetoothLeBroadcast mBroadcast;
|
||||||
|
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||||
|
@Mock private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
@Mock private SharedPreferences mSharedPreferences;
|
||||||
|
@Mock private SharedPreferences.Editor mEditor;
|
||||||
|
@Mock private ContentResolver mContentResolver;
|
||||||
|
@Mock private PreferenceScreen mScreen;
|
||||||
|
private AudioSharingPasswordPreferenceController mController;
|
||||||
|
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||||
|
private Lifecycle mLifecycle;
|
||||||
|
private LifecycleOwner mLifecycleOwner;
|
||||||
|
private AudioSharingPasswordPreference mPreference;
|
||||||
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||||
|
mShadowBluetoothAdapter.setEnabled(true);
|
||||||
|
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
|
||||||
|
BluetoothStatusCodes.FEATURE_SUPPORTED);
|
||||||
|
mLocalBtManager = Utils.getLocalBtManager(mContext);
|
||||||
|
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
|
||||||
|
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
|
||||||
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
|
when(mContext.getContentResolver()).thenReturn(mContentResolver);
|
||||||
|
when(mContext.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
|
||||||
|
when(mSharedPreferences.edit()).thenReturn(mEditor);
|
||||||
|
when(mEditor.putString(anyString(), anyString())).thenReturn(mEditor);
|
||||||
|
mLifecycleOwner = () -> mLifecycle;
|
||||||
|
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||||
|
mController = new AudioSharingPasswordPreferenceController(mContext, PREF_KEY);
|
||||||
|
mPreference = spy(new AudioSharingPasswordPreference(mContext));
|
||||||
|
when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_flagOn_available() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAvailabilityStatus_flagOff_unsupported() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mContentResolver, never()).registerContentObserver(any(), anyBoolean(), any());
|
||||||
|
verify(mSharedPreferences, never()).registerOnSharedPreferenceChangeListener(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStart_flagOn_registerCallbacks() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
verify(mContentResolver).registerContentObserver(any(), anyBoolean(), any());
|
||||||
|
verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_flagOff_doNothing() {
|
||||||
|
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStop(mLifecycleOwner);
|
||||||
|
verify(mContentResolver, never()).unregisterContentObserver(any());
|
||||||
|
verify(mSharedPreferences, never()).unregisterOnSharedPreferenceChangeListener(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onStop_flagOn_registerCallbacks() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
mController.onStop(mLifecycleOwner);
|
||||||
|
verify(mContentResolver).unregisterContentObserver(any());
|
||||||
|
verify(mSharedPreferences).unregisterOnSharedPreferenceChangeListener(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_setupPreference_noPassword() {
|
||||||
|
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
|
||||||
|
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
|
||||||
|
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
assertThat(mPreference.isPassword()).isTrue();
|
||||||
|
assertThat(mPreference.getDialogLayoutResource())
|
||||||
|
.isEqualTo(R.layout.audio_sharing_password_dialog);
|
||||||
|
assertThat(mPreference.getText()).isEqualTo(EDITTEXT_PASSWORD);
|
||||||
|
assertThat(mPreference.getSummary())
|
||||||
|
.isEqualTo(mContext.getString(R.string.audio_streams_no_password_summary));
|
||||||
|
verify(mPreference).setValidator(any());
|
||||||
|
verify(mPreference).setOnDialogEventListener(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contentObserver_updatePreferenceOnChange() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
when(mBroadcast.getBroadcastCode())
|
||||||
|
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
ArgumentCaptor<ContentObserver> observerCaptor =
|
||||||
|
ArgumentCaptor.forClass(ContentObserver.class);
|
||||||
|
verify(mContentResolver)
|
||||||
|
.registerContentObserver(any(), anyBoolean(), observerCaptor.capture());
|
||||||
|
|
||||||
|
var observer = observerCaptor.getValue();
|
||||||
|
assertThat(observer).isNotNull();
|
||||||
|
observer.onChange(true);
|
||||||
|
verify(mPreference).setText(anyString());
|
||||||
|
verify(mPreference).setSummary(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sharedPrefChangeListener_updatePreferenceOnChange() {
|
||||||
|
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
|
||||||
|
when(mBroadcast.getBroadcastCode())
|
||||||
|
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
|
||||||
|
mController.onStart(mLifecycleOwner);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
ArgumentCaptor<SharedPreferences.OnSharedPreferenceChangeListener> captor =
|
||||||
|
ArgumentCaptor.forClass(SharedPreferences.OnSharedPreferenceChangeListener.class);
|
||||||
|
verify(mSharedPreferences).registerOnSharedPreferenceChangeListener(captor.capture());
|
||||||
|
|
||||||
|
var observer = captor.getValue();
|
||||||
|
assertThat(captor).isNotNull();
|
||||||
|
observer.onSharedPreferenceChanged(mSharedPreferences, SHARED_PREF_KEY);
|
||||||
|
verify(mPreference).setText(anyString());
|
||||||
|
verify(mPreference).setSummary(anyString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void displayPreference_setupPreference_hasPassword() {
|
||||||
|
when(mBroadcast.getBroadcastCode())
|
||||||
|
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
assertThat(mPreference.isPassword()).isTrue();
|
||||||
|
assertThat(mPreference.getDialogLayoutResource())
|
||||||
|
.isEqualTo(R.layout.audio_sharing_password_dialog);
|
||||||
|
assertThat(mPreference.getText()).isEqualTo(BROADCAST_PASSWORD);
|
||||||
|
assertThat(mPreference.getSummary()).isEqualTo(HIDDEN_PASSWORD);
|
||||||
|
verify(mPreference).setValidator(any());
|
||||||
|
verify(mPreference).setOnDialogEventListener(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindDialogView_updatePreference_isBroadcasting_noPassword() {
|
||||||
|
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(true);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.onBindDialogView();
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mPreference).setEditable(false);
|
||||||
|
verify(mPreference).setChecked(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindDialogView_updatePreference_isNotBroadcasting_hasPassword() {
|
||||||
|
when(mBroadcast.getBroadcastCode())
|
||||||
|
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.onBindDialogView();
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mPreference).setEditable(true);
|
||||||
|
verify(mPreference).setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceDataChanged_isBroadcasting_doNothing() {
|
||||||
|
when(mBroadcast.isEnabled(any())).thenReturn(true);
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.onPreferenceDataChanged(BROADCAST_PASSWORD, /* isPublicBroadcast= */ false);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast, never()).setBroadcastCode(any());
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceDataChanged_noChange_doNothing() {
|
||||||
|
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
|
||||||
|
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.onPreferenceDataChanged(EDITTEXT_PASSWORD, /* isPublicBroadcast= */ true);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast, never()).setBroadcastCode(any());
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider, never()).action(any(), anyInt(), anyInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceDataChanged_updateToNonPublicBroadcast() {
|
||||||
|
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
|
||||||
|
when(mBroadcast.getBroadcastCode()).thenReturn(new byte[] {});
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.onPreferenceDataChanged(BROADCAST_PASSWORD, /* isPublicBroadcast= */ false);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast).setBroadcastCode(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
|
||||||
|
verify(mEditor).putString(anyString(), eq(BROADCAST_PASSWORD));
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider)
|
||||||
|
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPreferenceDataChanged_updateToPublicBroadcast() {
|
||||||
|
when(mSharedPreferences.getString(anyString(), anyString())).thenReturn(EDITTEXT_PASSWORD);
|
||||||
|
when(mBroadcast.getBroadcastCode())
|
||||||
|
.thenReturn(BROADCAST_PASSWORD.getBytes(StandardCharsets.UTF_8));
|
||||||
|
mController.displayPreference(mScreen);
|
||||||
|
mController.onPreferenceDataChanged(EDITTEXT_PASSWORD, /* isPublicBroadcast= */ true);
|
||||||
|
ShadowLooper.idleMainLooper();
|
||||||
|
|
||||||
|
verify(mBroadcast).setBroadcastCode("".getBytes(StandardCharsets.UTF_8));
|
||||||
|
verify(mEditor, never()).putString(anyString(), eq(EDITTEXT_PASSWORD));
|
||||||
|
verify(mFeatureFactory.metricsFeatureProvider)
|
||||||
|
.action(mContext, SettingsEnums.ACTION_AUDIO_STREAM_PASSWORD_UPDATED, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void idTextValid_emptyString() {
|
||||||
|
boolean valid = mController.isTextValid("");
|
||||||
|
|
||||||
|
assertThat(valid).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void idTextValid_validPassword() {
|
||||||
|
boolean valid = mController.isTextValid(BROADCAST_PASSWORD);
|
||||||
|
|
||||||
|
assertThat(valid).isTrue();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static android.view.View.GONE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.junit.MockitoJUnit;
|
||||||
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioSharingPasswordPreferenceTest {
|
||||||
|
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||||
|
private static final String EDIT_TEXT_CONTENT = "text";
|
||||||
|
private Context mContext;
|
||||||
|
private AudioSharingPasswordPreference mPreference;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
mPreference = new AudioSharingPasswordPreference(mContext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onBindDialogView_correctLayout() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
var editText = view.findViewById(android.R.id.edit);
|
||||||
|
var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
|
||||||
|
var dialogMessage = view.findViewById(android.R.id.message);
|
||||||
|
|
||||||
|
assertThat(editText).isNotNull();
|
||||||
|
assertThat(checkBox).isNotNull();
|
||||||
|
assertThat(dialogMessage).isNotNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setEditable_true() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
var editText = view.findViewById(android.R.id.edit);
|
||||||
|
var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
|
||||||
|
var dialogMessage = view.findViewById(android.R.id.message);
|
||||||
|
|
||||||
|
mPreference.setEditable(true);
|
||||||
|
|
||||||
|
assertThat(editText).isNotNull();
|
||||||
|
assertThat(editText.isEnabled()).isTrue();
|
||||||
|
assertThat(editText.getAlpha()).isEqualTo(1.0f);
|
||||||
|
assertThat(checkBox).isNotNull();
|
||||||
|
assertThat(checkBox.isEnabled()).isTrue();
|
||||||
|
assertThat(dialogMessage).isNotNull();
|
||||||
|
assertThat(dialogMessage.getVisibility()).isEqualTo(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setEditable_false() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
var editText = view.findViewById(android.R.id.edit);
|
||||||
|
var checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
|
||||||
|
var dialogMessage = view.findViewById(android.R.id.message);
|
||||||
|
|
||||||
|
mPreference.setEditable(false);
|
||||||
|
|
||||||
|
assertThat(editText).isNotNull();
|
||||||
|
assertThat(editText.isEnabled()).isFalse();
|
||||||
|
assertThat(editText.getAlpha()).isLessThan(1.0f);
|
||||||
|
assertThat(checkBox).isNotNull();
|
||||||
|
assertThat(checkBox.isEnabled()).isFalse();
|
||||||
|
assertThat(dialogMessage).isNotNull();
|
||||||
|
assertThat(dialogMessage.getVisibility()).isEqualTo(VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setChecked_true() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
CheckBox checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
|
||||||
|
|
||||||
|
mPreference.setChecked(true);
|
||||||
|
|
||||||
|
assertThat(checkBox).isNotNull();
|
||||||
|
assertThat(checkBox.isChecked()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setChecked_false() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
CheckBox checkBox = view.findViewById(R.id.audio_sharing_stream_password_checkbox);
|
||||||
|
|
||||||
|
mPreference.setChecked(false);
|
||||||
|
|
||||||
|
assertThat(checkBox).isNotNull();
|
||||||
|
assertThat(checkBox.isChecked()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDialogEventListener_onClick_positiveButton() {
|
||||||
|
AudioSharingPasswordPreference.OnDialogEventListener listener =
|
||||||
|
mock(AudioSharingPasswordPreference.OnDialogEventListener.class);
|
||||||
|
mPreference.setOnDialogEventListener(listener);
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
EditText editText = view.findViewById(android.R.id.edit);
|
||||||
|
assertThat(editText).isNotNull();
|
||||||
|
editText.setText(EDIT_TEXT_CONTENT);
|
||||||
|
|
||||||
|
mPreference.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_POSITIVE);
|
||||||
|
|
||||||
|
verify(listener).onBindDialogView();
|
||||||
|
verify(listener).onPreferenceDataChanged(eq(EDIT_TEXT_CONTENT), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onDialogEventListener_onClick_negativeButton_doNothing() {
|
||||||
|
AudioSharingPasswordPreference.OnDialogEventListener listener =
|
||||||
|
mock(AudioSharingPasswordPreference.OnDialogEventListener.class);
|
||||||
|
mPreference.setOnDialogEventListener(listener);
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
|
||||||
|
EditText editText = view.findViewById(android.R.id.edit);
|
||||||
|
assertThat(editText).isNotNull();
|
||||||
|
editText.setText(EDIT_TEXT_CONTENT);
|
||||||
|
|
||||||
|
mPreference.onClick(mock(DialogInterface.class), DialogInterface.BUTTON_NEGATIVE);
|
||||||
|
|
||||||
|
verify(listener).onBindDialogView();
|
||||||
|
verify(listener, never()).onPreferenceDataChanged(anyString(), anyBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPrepareDialogBuilder_editable_doNothing() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
mPreference.setEditable(true);
|
||||||
|
|
||||||
|
var dialogBuilder = mock(AlertDialog.Builder.class);
|
||||||
|
mPreference.onPrepareDialogBuilder(
|
||||||
|
dialogBuilder, mock(DialogInterface.OnClickListener.class));
|
||||||
|
|
||||||
|
verify(dialogBuilder, never()).setPositiveButton(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onPrepareDialogBuilder_notEditable_disableButton() {
|
||||||
|
View view =
|
||||||
|
LayoutInflater.from(mContext).inflate(R.layout.audio_sharing_password_dialog, null);
|
||||||
|
mPreference.onBindDialogView(view);
|
||||||
|
mPreference.setEditable(false);
|
||||||
|
|
||||||
|
var dialogBuilder = mock(AlertDialog.Builder.class);
|
||||||
|
mPreference.onPrepareDialogBuilder(
|
||||||
|
dialogBuilder, mock(DialogInterface.OnClickListener.class));
|
||||||
|
|
||||||
|
verify(dialogBuilder).setPositiveButton(any(), any());
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.connecteddevice.audiosharing;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class AudioSharingPasswordValidatorTest {
|
||||||
|
private AudioSharingPasswordValidator mValidator;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mValidator = new AudioSharingPasswordValidator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidPasswords() {
|
||||||
|
assertThat(mValidator.isTextValid("1234")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("Password")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("SecurePass123!")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("ÄÖÜß")).isTrue();
|
||||||
|
assertThat(mValidator.isTextValid("1234567890abcdef")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidPasswords() {
|
||||||
|
assertThat(mValidator.isTextValid(null)).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("")).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("abc")).isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("ThisIsAVeryLongPasswordThatExceedsSixteenOctets"))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(mValidator.isTextValid("Invalid\uDC00")).isFalse();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user