Merge 24Q4 (ab/12406339) into aosp-main-future

Bug: 370570306
Merged-In: Ie90e7495dd4a134538bae6e3e08eea0d02134b14
Change-Id: I20517e9ee410e95f2cbeb1247c0c0288ed9f006f
This commit is contained in:
Xin Li
2024-11-11 21:38:40 -08:00
1597 changed files with 77072 additions and 46403 deletions

View File

@@ -33,7 +33,6 @@ android_test {
],
platform_apis: true,
certificate: "platform",
test_suites: ["device-tests"],
libs: [
"android.test.runner.stubs.system",
"android.test.base.stubs.system",
@@ -57,6 +56,6 @@ java_test_host {
device_common_data: [
":test_16kb_app",
],
test_suites: ["device-tests"],
test_suites: ["general-tests"],
test_config: "AndroidTest.xml",
}

View File

@@ -69,6 +69,9 @@ android_robolectric_test {
"com_android_server_accessibility_flags_lib",
"flag-junit",
"flag-junit-base",
"kotlin-test",
"mockito-robolectric-prebuilt", // mockito deps order matters!
"mockito-kotlin2",
"notification_flags_lib",
"platform-test-annotations",
"testables",

View File

@@ -2,4 +2,5 @@ sdk=NEWEST_SDK
shadows=\
com.android.settings.testutils.shadow.ShadowThreadUtils \
com.android.settings.network.ShadowServiceManagerExtend
instrumentedPackages=androidx.preference
instrumentedPackages=androidx.preference
sqliteMode=native

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference android:key="pref_id" />
<Preference android:key="pref_name" />
<Preference android:key="pref_enabled" />
</PreferenceScreen>

View File

@@ -31,10 +31,11 @@ import android.platform.test.flag.junit.SetFlagsRule;
import android.security.Flags;
import android.service.persistentdata.PersistentDataBlockManager;
import android.view.LayoutInflater;
import android.widget.TextView;
import androidx.fragment.app.FragmentActivity;
import com.google.android.setupdesign.GlifLayout;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -77,6 +78,7 @@ public class MainClearConfirmTest {
when(mMockActivity.getSystemService(Context.DEVICE_POLICY_SERVICE))
.thenReturn(mDevicePolicyManager);
when(mPersistentDataBlockManager.isFactoryResetProtectionActive()).thenReturn(false);
}
@Test
@@ -84,12 +86,12 @@ public class MainClearConfirmTest {
MainClearConfirm mainClearConfirm = new MainClearConfirm();
mainClearConfirm.mEraseEsims = true;
mainClearConfirm.mContentView =
LayoutInflater.from(mActivity).inflate(R.layout.main_clear_confirm, null);
(GlifLayout) LayoutInflater.from(mActivity)
.inflate(R.layout.main_clear_confirm, null);
mainClearConfirm.setSubtitle();
assertThat(((TextView) mainClearConfirm.mContentView
.findViewById(R.id.sud_layout_description)).getText())
assertThat(mainClearConfirm.mContentView.getDescriptionText())
.isEqualTo(mActivity.getString(R.string.main_clear_final_desc_esim));
}
@@ -98,12 +100,12 @@ public class MainClearConfirmTest {
MainClearConfirm mainClearConfirm = new MainClearConfirm();
mainClearConfirm.mEraseEsims = false;
mainClearConfirm.mContentView =
LayoutInflater.from(mActivity).inflate(R.layout.main_clear_confirm, null);
(GlifLayout) LayoutInflater.from(mActivity)
.inflate(R.layout.main_clear_confirm, null);
mainClearConfirm.setSubtitle();
assertThat(((TextView) mainClearConfirm.mContentView
.findViewById(R.id.sud_layout_description)).getText())
assertThat(mainClearConfirm.mContentView.getDescriptionText())
.isEqualTo(mActivity.getString(R.string.main_clear_final_desc));
}
@@ -112,6 +114,13 @@ public class MainClearConfirmTest {
assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(null)).isFalse();
}
@Test
public void shouldWipePersistentDataBlock_frpIsAlive_shouldReturnFalse() {
when(mPersistentDataBlockManager.isFactoryResetProtectionActive()).thenReturn(true);
assertThat(mMainClearConfirm.shouldWipePersistentDataBlock(mPersistentDataBlockManager))
.isFalse();
}
@Test
public void shouldWipePersistentDataBlock_deviceIsStillBeingProvisioned_shouldReturnFalse() {
doReturn(true).when(mMainClearConfirm).isDeviceStillBeingProvisioned();

View File

@@ -18,6 +18,7 @@ package com.android.settings;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -39,7 +40,12 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.os.UserManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
@@ -49,7 +55,11 @@ import android.widget.LinearLayout;
import android.widget.ScrollView;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog;
import com.android.settings.password.ConfirmDeviceCredentialActivity;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.development.DevelopmentSettingsEnabler;
@@ -57,6 +67,7 @@ import com.android.settingslib.development.DevelopmentSettingsEnabler;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -77,6 +88,9 @@ import org.robolectric.shadows.ShadowActivity;
})
public class MainClearTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TEST_ACCOUNT_TYPE = "android.test.account.type";
private static final String TEST_CONFIRMATION_PACKAGE = "android.test.conf.pkg";
private static final String TEST_CONFIRMATION_CLASS = "android.test.conf.pkg.ConfActivity";
@@ -95,9 +109,19 @@ public class MainClearTest {
@Mock
private FragmentActivity mMockActivity;
@Mock
private BiometricManager mBiometricManager;
@Mock
private Resources mResources;
@Mock
private Context mContext;
@Mock
private Intent mMockIntent;
@Mock
private FragmentManager mMockFragmentManager;
@Mock
private FragmentTransaction mMockFragmentTransaction;
private MainClear mMainClear;
private ShadowActivity mShadowActivity;
@@ -122,6 +146,11 @@ public class MainClearTest {
// Make scrollView only have one child
when(mScrollView.getChildAt(0)).thenReturn(mLinearLayout);
when(mScrollView.getChildCount()).thenReturn(1);
doReturn(mMockActivity).when(mMainClear).getActivity();
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
}
@After
@@ -343,6 +372,114 @@ public class MainClearTest {
verify(mMainClear, times(1)).showFinalConfirmation();
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testOnActivityResultInternal_keyguardRequestTriggeringBiometricPrompt() {
when(mContext.getResources()).thenReturn(mResources);
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mResources.getString(anyInt())).thenReturn(TEST_ACCOUNT_NAME);
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
doNothing().when(mMainClear).startActivityForResult(any(), anyInt());
doReturn(mMockActivity).when(mMainClear).getActivity();
doReturn(mContext).when(mMainClear).getContext();
mMainClear
.onActivityResultInternal(MainClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null);
verify(mMainClear, times(1)).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
verify(mMainClear).startActivityForResult(any(), eq(MainClear.BIOMETRICS_REQUEST));
verify(mMainClear, times(0)).establishInitialState();
verify(mMainClear, times(0)).getAccountConfirmationIntent();
verify(mMainClear, times(0)).showFinalConfirmation();
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testOnActivityResultInternal_keyguardRequestNotTriggeringBiometricPrompt_lockoutError() {
final ArgumentCaptor<IdentityCheckBiometricErrorDialog> argumentCaptor =
ArgumentCaptor.forClass(IdentityCheckBiometricErrorDialog.class);
when(mContext.getResources()).thenReturn(mResources);
when(mMockActivity.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
when(mResources.getString(anyInt())).thenReturn(TEST_ACCOUNT_NAME);
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_LOCKOUT);
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
doNothing().when(mMainClear).startActivityForResult(any(), anyInt());
doReturn(mMockActivity).when(mMainClear).getActivity();
doReturn(mMockFragmentManager).when(mMockActivity).getSupportFragmentManager();
doReturn(mMockFragmentTransaction).when(mMockFragmentManager).beginTransaction();
doReturn(mContext).when(mMainClear).getContext();
mMainClear
.onActivityResultInternal(MainClear.KEYGUARD_REQUEST, Activity.RESULT_OK, null);
verify(mMainClear).isValidRequestCode(eq(MainClear.KEYGUARD_REQUEST));
verify(mMainClear.getActivity().getSupportFragmentManager().beginTransaction()).add(
argumentCaptor.capture(), any());
assertThat(argumentCaptor.getValue()).isInstanceOf(IdentityCheckBiometricErrorDialog.class);
verify(mMainClear, never()).startActivityForResult(any(), eq(MainClear.BIOMETRICS_REQUEST));
verify(mMainClear, never()).establishInitialState();
verify(mMainClear, never()).getAccountConfirmationIntent();
verify(mMainClear, never()).showFinalConfirmation();
}
@Test
public void testOnActivityResultInternal_biometricRequestTriggeringFinalConfirmation() {
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.BIOMETRICS_REQUEST));
doReturn(null).when(mMainClear).getAccountConfirmationIntent();
doNothing().when(mMainClear).showFinalConfirmation();
mMainClear
.onActivityResultInternal(MainClear.BIOMETRICS_REQUEST, Activity.RESULT_OK, null);
verify(mMainClear).isValidRequestCode(eq(MainClear.BIOMETRICS_REQUEST));
verify(mMainClear, never()).establishInitialState();
verify(mMainClear).getAccountConfirmationIntent();
verify(mMainClear).showFinalConfirmation();
}
@Test
public void testOnActivityResultInternal_biometricRequestTriggeringBiometricErrorDialog() {
final ArgumentCaptor<IdentityCheckBiometricErrorDialog> argumentCaptor =
ArgumentCaptor.forClass(IdentityCheckBiometricErrorDialog.class);
doReturn(true).when(mMainClear).isValidRequestCode(
eq(MainClear.BIOMETRICS_REQUEST));
doNothing().when(mMainClear).establishInitialState();
doReturn(mMockActivity).when(mMainClear).getActivity();
doReturn(mMockFragmentManager).when(mMockActivity).getSupportFragmentManager();
doReturn(mMockFragmentTransaction).when(mMockFragmentManager).beginTransaction();
doReturn(mContext).when(mMainClear).getContext();
mMainClear
.onActivityResultInternal(MainClear.BIOMETRICS_REQUEST,
ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT, null);
verify(mMainClear).isValidRequestCode(eq(MainClear.BIOMETRICS_REQUEST));
verify(mMainClear.getActivity().getSupportFragmentManager().beginTransaction()).add(
argumentCaptor.capture(), any());
verify(mMainClear).establishInitialState();
}
@Test
public void testOnActivityResultInternal_biometricRequestTriggeringInitialState() {
doReturn(true).when(mMainClear).isValidRequestCode(eq(MainClear.BIOMETRICS_REQUEST));
doNothing().when(mMainClear).establishInitialState();
mMainClear.onActivityResultInternal(MainClear.BIOMETRICS_REQUEST, Activity.RESULT_CANCELED,
null);
verify(mMainClear, times(1)).isValidRequestCode(eq(MainClear.BIOMETRICS_REQUEST));
verify(mMainClear, times(1)).establishInitialState();
verify(mMainClear, times(0)).getAccountConfirmationIntent();
verify(mMainClear, times(0)).showFinalConfirmation();
}
@Test
public void testOnActivityResultInternal_confirmRequestTriggeringShowFinal() {
doReturn(true).when(mMainClear)

View File

@@ -1,124 +0,0 @@
/*
* 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;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.view.LayoutInflater;
import android.widget.TextView;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowRecoverySystem;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.util.concurrent.PausedExecutorService;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPausedAsyncTask;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowRecoverySystem.class, ShadowBluetoothAdapter.class})
public class ResetNetworkConfirmTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final String TEST_PACKAGE = "com.android.settings";
private FragmentActivity mActivity;
@Mock
private ResetNetworkConfirm mResetNetworkConfirm;
private PausedExecutorService mExecutorService;
@Before
public void setUp() {
mExecutorService = new PausedExecutorService();
ShadowPausedAsyncTask.overrideExecutor(mExecutorService);
mResetNetworkConfirm = new ResetNetworkConfirm();
mActivity = spy(Robolectric.setupActivity(FragmentActivity.class));
mResetNetworkConfirm.mActivity = mActivity;
}
@After
public void tearDown() {
ShadowRecoverySystem.reset();
}
@Test
public void testResetNetworkData_notResetEsim() {
mResetNetworkConfirm.mResetNetworkRequest =
new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
mResetNetworkConfirm.mResetSubscriptionContract =
new ResetSubscriptionContract(mActivity,
mResetNetworkConfirm.mResetNetworkRequest) {
@Override
public void onSubscriptionInactive(int subscriptionId) {
mActivity.onBackPressed();
}
};
mResetNetworkConfirm.mFinalClickListener.onClick(null /* View */);
mExecutorService.runAll();
ShadowLooper.idleMainLooper();
assertThat(ShadowRecoverySystem.getWipeEuiccCalledCount()).isEqualTo(0);
}
@Test
public void setSubtitle_eraseEsim() {
mResetNetworkConfirm.mResetNetworkRequest =
new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
mResetNetworkConfirm.mResetNetworkRequest.setResetEsim(TEST_PACKAGE);
mResetNetworkConfirm.mContentView =
LayoutInflater.from(mActivity).inflate(R.layout.reset_network_confirm, null);
mResetNetworkConfirm.setSubtitle();
assertThat(((TextView) mResetNetworkConfirm.mContentView
.findViewById(R.id.reset_network_confirm)).getText())
.isEqualTo(mActivity.getString(R.string.reset_network_final_desc_esim));
}
@Test
public void setSubtitle_notEraseEsim() {
mResetNetworkConfirm.mResetNetworkRequest =
new ResetNetworkRequest(ResetNetworkRequest.RESET_NONE);
mResetNetworkConfirm.mContentView =
LayoutInflater.from(mActivity).inflate(R.layout.reset_network_confirm, null);
mResetNetworkConfirm.setSubtitle();
assertThat(((TextView) mResetNetworkConfirm.mContentView
.findViewById(R.id.reset_network_confirm)).getText())
.isEqualTo(mActivity.getString(R.string.reset_network_final_desc));
}
}

View File

@@ -20,6 +20,11 @@ import static android.hardware.biometrics.SensorProperties.STRENGTH_CONVENIENCE;
import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.hardware.biometrics.SensorProperties.STRENGTH_WEAK;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import static com.android.settings.password.ConfirmDeviceCredentialActivity.BIOMETRIC_PROMPT_AUTHENTICATORS;
import static com.android.settings.password.ConfirmDeviceCredentialActivity.BIOMETRIC_PROMPT_HIDE_BACKGROUND;
import static com.android.settings.password.ConfirmDeviceCredentialActivity.BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNull;
@@ -35,10 +40,12 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActionBar;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -47,6 +54,8 @@ import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.VectorDrawable;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -61,21 +70,28 @@ import android.os.UserManager;
import android.os.storage.DiskInfo;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.IconDrawableFactory;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmDeviceCredentialActivity;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import org.junit.After;
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.MockitoAnnotations;
import org.robolectric.Robolectric;
@@ -92,6 +108,9 @@ import java.util.List;
@Config(shadows = ShadowLockPatternUtils.class)
public class UtilsTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PACKAGE_NAME = "com.android.app";
private static final int USER_ID = 1;
@@ -113,6 +132,11 @@ public class UtilsTest {
private IconDrawableFactory mIconDrawableFactory;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock
private BiometricManager mBiometricManager;
@Mock
private Fragment mFragment;
private Context mContext;
private UserManager mUserManager;
private static final int FLAG_SYSTEM = 0x00000000;
@@ -128,6 +152,7 @@ public class UtilsTest {
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn(connectivityManager);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(BiometricManager.class)).thenReturn(mBiometricManager);
}
@After
@@ -503,6 +528,81 @@ public class UtilsTest {
assertThat(Utils.isFaceNotConvenienceBiometric(mContext)).isFalse();
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testRequestBiometricAuthentication_biometricManagerNull_shouldReturnNotActive() {
when(mContext.getSystemService(BiometricManager.class)).thenReturn(null);
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
false /* biometricsAuthenticationRequested */, USER_ID)).isEqualTo(
Utils.BiometricStatus.NOT_ACTIVE);
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testRequestBiometricAuthentication_biometricManagerReturnsSuccess_shouldReturnOk() {
when(mBiometricManager.canAuthenticate(USER_ID,
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
final Utils.BiometricStatus requestBiometricAuthenticationForMandatoryBiometrics =
Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
false /* biometricsAuthenticationRequested */, USER_ID);
assertThat(requestBiometricAuthenticationForMandatoryBiometrics).isEqualTo(
Utils.BiometricStatus.OK);
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testRequestBiometricAuthentication_biometricManagerReturnsError_shouldReturnError() {
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
false /* biometricsAuthenticationRequested */, USER_ID)).isEqualTo(
Utils.BiometricStatus.ERROR);
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testRequestBiometricAuthentication_biometricManagerReturnsSuccessForDifferentUser_shouldReturnError() {
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
when(mBiometricManager.canAuthenticate(0 /* userId */,
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
assertThat(Utils.requestBiometricAuthenticationForMandatoryBiometrics(mContext,
false /* biometricsAuthenticationRequested */, USER_ID)).isEqualTo(
Utils.BiometricStatus.ERROR);
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testLaunchBiometricPrompt_checkIntentValues() {
when(mFragment.getContext()).thenReturn(mContext);
final int requestCode = 1;
final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
Utils.launchBiometricPromptForMandatoryBiometrics(mFragment, requestCode, USER_ID,
false /* hideBackground */);
verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(), eq(requestCode));
final Intent intent = intentArgumentCaptor.getValue();
assertThat(intent.getExtra(BIOMETRIC_PROMPT_AUTHENTICATORS)).isEqualTo(
BiometricManager.Authenticators.MANDATORY_BIOMETRICS);
assertThat(intent.getExtra(BIOMETRIC_PROMPT_NEGATIVE_BUTTON_TEXT)).isNotNull();
assertThat(intent.getExtra(KeyguardManager.EXTRA_DESCRIPTION)).isNotNull();
assertThat(intent.getBooleanExtra(ChooseLockSettingsHelper.EXTRA_KEY_ALLOW_ANY_USER, false))
.isTrue();
assertThat(intent.getBooleanExtra(BIOMETRIC_PROMPT_HIDE_BACKGROUND, true))
.isFalse();
assertThat(intent.getIntExtra(Intent.EXTRA_USER_ID, 0)).isEqualTo(USER_ID);
assertThat(intent.getComponent().getPackageName()).isEqualTo(SETTINGS_PACKAGE_NAME);
assertThat(intent.getComponent().getClassName()).isEqualTo(
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
}
private void setUpForConfirmCredentialString(boolean isEffectiveUserManagedProfile) {
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mMockUserManager);
when(mMockUserManager.getCredentialOwnerProfile(USER_ID)).thenReturn(USER_ID);

View File

@@ -28,6 +28,10 @@ import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Flags;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
@@ -56,6 +60,8 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class AccessibilityButtonFragmentTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
@@ -84,6 +90,7 @@ public class AccessibilityButtonFragmentTest {
}
@Test
@DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void onCreate_navigationGestureEnabled_setCorrectTitle() {
when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
.thenReturn(NAV_BAR_MODE_GESTURAL);
@@ -96,7 +103,20 @@ public class AccessibilityButtonFragmentTest {
}
@Test
public void onCreate_navigationGestureDisabled_setCorrectTitle() {
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void onCreate_navigationGestureEnabled_gestureFlag_setCorrectTitle() {
when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
.thenReturn(NAV_BAR_MODE_GESTURAL);
mFragment.onAttach(mContext);
mFragment.onCreate(Bundle.EMPTY);
assertThat(mFragment.getActivity().getTitle().toString()).isEqualTo(
mContext.getString(R.string.accessibility_button_title));
}
@Test
public void onCreate_navigationBarEnabled_setCorrectTitle() {
when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
.thenReturn(NAV_BAR_MODE_2BUTTON);

View File

@@ -30,6 +30,10 @@ import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Flags;
import android.provider.Settings;
import androidx.preference.ListPreference;
@@ -48,6 +52,8 @@ import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AccessibilityButtonGesturePreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@@ -67,6 +73,7 @@ public class AccessibilityButtonGesturePreferenceControllerTest {
}
@Test
@DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void getAvailabilityStatus_navigationGestureEnabled_returnAvailable() {
when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
.thenReturn(NAV_BAR_MODE_GESTURAL);
@@ -74,6 +81,16 @@ public class AccessibilityButtonGesturePreferenceControllerTest {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void
getAvailabilityStatus_navigationGestureEnabled_gestureFlag_conditionallyUnavailable() {
when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
.thenReturn(NAV_BAR_MODE_GESTURAL);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_navigationGestureDisabled_returnConditionallyUnavailable() {
when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))

View File

@@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.Dialog;
import android.content.Context;
import androidx.appcompat.app.AlertDialog;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
@@ -40,24 +39,6 @@ public class AccessibilityDialogUtilsTest {
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
}
@Test
public void updateShortcutInDialog_correctDialogType_success() {
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC, "Title",
null);
assertThat(
AccessibilityDialogUtils.updateShortcutInDialog(mContext, dialog)).isTrue();
}
@Test
public void updateShortcutInDialog_useNotSupportedDialog_fail() {
final AlertDialog dialog = new AlertDialog.Builder(mContext).setTitle("Title").show();
assertThat(AccessibilityDialogUtils.updateShortcutInDialog(mContext,
dialog)).isFalse();
}
@Test
public void showDialog_createCustomDialog_isShowing() {
final Dialog dialog = AccessibilityDialogUtils.createCustomDialog(mContext,

View File

@@ -0,0 +1,70 @@
/*
* 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.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.widget.IllustrationPreference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/** Tests for {@link AccessibilityFragmentUtils} */
@RunWith(RobolectricTestRunner.class)
public class AccessibilityFragmentUtilsTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
@Test
public void isPreferenceImportantToA11y_basicPreference_isImportant() {
final Preference pref = new ShortcutPreference(mContext, /* attrs= */ null);
assertThat(AccessibilityFragmentUtils.isPreferenceImportantToA11y(pref)).isTrue();
}
@Test
public void isPreferenceImportantToA11y_illustrationPreference_hasContentDesc_isImportant() {
final IllustrationPreference pref =
new IllustrationPreference(mContext, /* attrs= */ null);
pref.setContentDescription("content desc");
assertThat(AccessibilityFragmentUtils.isPreferenceImportantToA11y(pref)).isTrue();
}
@Test
public void isPreferenceImportantToA11y_illustrationPreference_noContentDesc_notImportant() {
final IllustrationPreference pref =
new IllustrationPreference(mContext, /* attrs= */ null);
pref.setContentDescription(null);
assertThat(AccessibilityFragmentUtils.isPreferenceImportantToA11y(pref)).isFalse();
}
@Test
public void isPreferenceImportantToA11y_paletteListPreference_notImportant() {
final PaletteListPreference pref =
new PaletteListPreference(mContext, /* attrs= */ null);
assertThat(AccessibilityFragmentUtils.isPreferenceImportantToA11y(pref)).isFalse();
}
}

View File

@@ -32,6 +32,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
@@ -41,7 +42,6 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.Flags;
import androidx.fragment.app.Fragment;
import androidx.test.core.app.ApplicationProvider;
@@ -49,7 +49,9 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowApplicationPackageManager;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
@@ -75,8 +77,8 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAccessibilityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowLooper;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
@@ -87,6 +89,7 @@ import java.util.List;
/** Test for {@link AccessibilitySettings}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowAccessibilityManager.class,
ShadowBluetoothAdapter.class,
ShadowUserManager.class,
ShadowColorDisplayManager.class,
@@ -95,8 +98,10 @@ import java.util.List;
})
public class AccessibilitySettingsTest {
private static final String PACKAGE_NAME = "com.android.test";
private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
private static final ComponentName COMPONENT_NAME = new ComponentName(PACKAGE_NAME, CLASS_NAME);
private static final ComponentName SERVICE_COMPONENT_NAME =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_service");
private static final ComponentName ACTIVITY_COMPONENT_NAME =
new ComponentName(PACKAGE_NAME, PACKAGE_NAME + ".test_a11y_activity");
private static final String EMPTY_STRING = "";
private static final String DEFAULT_SUMMARY = "default summary";
private static final String DEFAULT_DESCRIPTION = "default description";
@@ -110,9 +115,7 @@ public class AccessibilitySettingsTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy
private final AccessibilityServiceInfo mServiceInfo = getMockAccessibilityServiceInfo(
PACKAGE_NAME, CLASS_NAME);
@Mock
private AccessibilityShortcutInfo mShortcutInfo;
SERVICE_COMPONENT_NAME);
private ShadowAccessibilityManager mShadowAccessibilityManager;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@@ -121,11 +124,11 @@ public class AccessibilitySettingsTest {
@Before
public void setup() {
mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext));
mShadowAccessibilityManager = Shadow.extract(
mContext.getSystemService(AccessibilityManager.class));
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(new ArrayList<>());
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
setMockAccessibilityShortcutInfo(mShortcutInfo);
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
@@ -153,6 +156,53 @@ public class AccessibilitySettingsTest {
assertThat(indexableRawList).isNull();
}
@DisableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
@Test
public void getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOff_returnEmpty() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(mServiceInfo));
mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
List.of(getMockAccessibilityShortcutInfo()));
assertThat(AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
mContext, /* enabled= */ true))
.isEmpty();
}
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
@Test
public void getDynamicRawDataToIndex_hasInstalledA11yFeatures_flagOn_returnRawDataForInstalledA11yFeatures() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(mServiceInfo));
mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
List.of(getMockAccessibilityShortcutInfo()));
final AccessibilitySearchFeatureProvider featureProvider =
FakeFeatureFactory.setupForTest().getAccessibilitySearchFeatureProvider();
final String synonyms = "fake keyword1, fake keyword2";
when(featureProvider.getSynonymsForComponent(mContext, ACTIVITY_COMPONENT_NAME))
.thenReturn("");
when(featureProvider.getSynonymsForComponent(mContext, SERVICE_COMPONENT_NAME))
.thenReturn(synonyms);
final List<SearchIndexableRaw> indexableRawDataList =
AccessibilitySettings.SEARCH_INDEX_DATA_PROVIDER.getDynamicRawDataToIndex(
mContext, /* enabled= */ true);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
assertThat(indexableRawDataList).hasSize(2);
SearchIndexableRaw a11yActivityIndexableData = indexableRawDataList.get(0);
assertThat(a11yActivityIndexableData.key).isEqualTo(
ACTIVITY_COMPONENT_NAME.flattenToString());
assertThat(a11yActivityIndexableData.title).isEqualTo(DEFAULT_LABEL);
assertThat(a11yActivityIndexableData.keywords).isEmpty();
SearchIndexableRaw a11yServiceIndexableData = indexableRawDataList.get(1);
assertThat(a11yServiceIndexableData.key).isEqualTo(
SERVICE_COMPONENT_NAME.flattenToString());
assertThat(a11yServiceIndexableData.title).isEqualTo(DEFAULT_LABEL);
assertThat(a11yServiceIndexableData.keywords).isEqualTo(synonyms);
}
@Test
public void getServiceSummary_serviceCrash_showsStopped() {
mServiceInfo.crashed = true;
@@ -174,7 +224,8 @@ public class AccessibilitySettingsTest {
mServiceInfo, SERVICE_ENABLED);
assertThat(summary).isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
mContext.getString(R.string.accessibility_summary_shortcut_enabled),
DEFAULT_SUMMARY));
}
@@ -189,7 +240,8 @@ public class AccessibilitySettingsTest {
mServiceInfo, SERVICE_ENABLED);
assertThat(summary).isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
mContext.getString(R.string.generic_accessibility_feature_shortcut_off),
DEFAULT_SUMMARY));
}
@@ -251,7 +303,8 @@ public class AccessibilitySettingsTest {
mServiceInfo, SERVICE_ENABLED).toString();
assertThat(summary).isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
mContext.getString(R.string.generic_accessibility_service_on),
DEFAULT_SUMMARY));
}
@@ -265,7 +318,8 @@ public class AccessibilitySettingsTest {
mServiceInfo, SERVICE_ENABLED).toString();
assertThat(summary).isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
mContext.getString(R.string.generic_accessibility_service_on),
DEFAULT_SUMMARY));
}
@@ -279,7 +333,8 @@ public class AccessibilitySettingsTest {
mServiceInfo, SERVICE_DISABLED).toString();
assertThat(summary).isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
mContext.getString(R.string.generic_accessibility_service_off),
DEFAULT_SUMMARY));
}
@@ -293,7 +348,8 @@ public class AccessibilitySettingsTest {
mServiceInfo, SERVICE_DISABLED).toString();
assertThat(summary).isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
mContext.getString(R.string.generic_accessibility_service_off),
DEFAULT_SUMMARY));
}
@@ -320,7 +376,7 @@ public class AccessibilitySettingsTest {
}
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
@DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void onCreate_flagDisabled_haveRegisterToSpecificUrisAndActions() {
setupFragment();
@@ -333,7 +389,7 @@ public class AccessibilitySettingsTest {
}
@Test
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void onCreate_flagEnabled_haveRegisterToSpecificUrisAndActions() {
setupFragment();
@@ -368,7 +424,7 @@ public class AccessibilitySettingsTest {
mFragment.onContentChanged();
RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
COMPONENT_NAME.flattenToString());
SERVICE_COMPONENT_NAME.flattenToString());
assertThat(preference).isNotNull();
@@ -388,7 +444,7 @@ public class AccessibilitySettingsTest {
mFragment.onResume();
RestrictedPreference preference = mFragment.getPreferenceScreen().findPreference(
COMPONENT_NAME.flattenToString());
SERVICE_COMPONENT_NAME.flattenToString());
assertThat(preference).isNotNull();
@@ -398,14 +454,25 @@ public class AccessibilitySettingsTest {
public void testAccessibilityMenuInSystem_IncludedInInteractionControl() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(getMockAccessibilityServiceInfo(
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM)));
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
/*isSystemApp=*/true)));
setupFragment();
final RestrictedPreference pref = mFragment.getPreferenceScreen().findPreference(
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM.flattenToString());
final String prefCategory = mFragment.mServicePreferenceToPreferenceCategoryMap.get(
pref).getKey();
assertThat(prefCategory).isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL);
assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
.isEqualTo(AccessibilitySettings.CATEGORY_INTERACTION_CONTROL);
}
@Test
@EnableFlags(Flags.FLAG_CHECK_PREBUNDLED_IS_PREINSTALLED)
public void testNonPreinstalledApp_IncludedInDownloadedCategory() {
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(getMockAccessibilityServiceInfo(
AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM,
/*isSystemApp=*/false)));
setupFragment();
assertThat(getPreferenceCategory(AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM))
.isEqualTo(AccessibilitySettings.CATEGORY_DOWNLOADED_SERVICES);
}
@Test
@@ -418,18 +485,56 @@ public class AccessibilitySettingsTest {
assertThat(pref).isNull();
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
String className) {
return getMockAccessibilityServiceInfo(new ComponentName(packageName, className));
@Test
public void testSameNamedServiceAndActivity_bothPreferencesExist() {
final PackageManager pm = mContext.getPackageManager();
AccessibilityServiceInfo a11yServiceInfo = mServiceInfo;
AccessibilityShortcutInfo a11yShortcutInfo = getMockAccessibilityShortcutInfo();
// Ensure the test service and activity have the same package name and label.
// Before this change, any service and activity with the same package name and
// label would cause the service to be hidden.
assertThat(a11yServiceInfo.getComponentName())
.isNotEqualTo(a11yShortcutInfo.getComponentName());
assertThat(a11yServiceInfo.getComponentName().getPackageName())
.isEqualTo(a11yShortcutInfo.getComponentName().getPackageName());
assertThat(a11yServiceInfo.getResolveInfo().serviceInfo.loadLabel(pm))
.isEqualTo(a11yShortcutInfo.getActivityInfo().loadLabel(pm));
// Prepare A11yManager with the test service and activity.
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
List.of(mServiceInfo));
mShadowAccessibilityManager.setInstalledAccessibilityShortcutListAsUser(
List.of(getMockAccessibilityShortcutInfo()));
setupFragment();
// Both service and activity preferences should exist on the page.
RestrictedPreference servicePref = mFragment.getPreferenceScreen().findPreference(
a11yServiceInfo.getComponentName().flattenToString());
RestrictedPreference activityPref = mFragment.getPreferenceScreen().findPreference(
a11yShortcutInfo.getComponentName().flattenToString());
assertThat(servicePref).isNotNull();
assertThat(activityPref).isNotNull();
}
private String getPreferenceCategory(ComponentName componentName) {
return mFragment.mServicePreferenceToPreferenceCategoryMap.get(
mFragment.getPreferenceScreen().findPreference(
componentName.flattenToString())).getKey();
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
final ServiceInfo serviceInfo = new ServiceInfo();
return getMockAccessibilityServiceInfo(componentName, true);
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(ComponentName componentName,
boolean isSystemApp) {
final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
when(applicationInfo.isSystemApp()).thenReturn(isSystemApp);
final ServiceInfo serviceInfo = Mockito.spy(new ServiceInfo());
applicationInfo.packageName = componentName.getPackageName();
serviceInfo.packageName = componentName.getPackageName();
serviceInfo.name = componentName.getClassName();
serviceInfo.applicationInfo = applicationInfo;
when(serviceInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
final ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.serviceInfo = serviceInfo;
@@ -445,14 +550,16 @@ public class AccessibilitySettingsTest {
return null;
}
private void setMockAccessibilityShortcutInfo(AccessibilityShortcutInfo mockInfo) {
private AccessibilityShortcutInfo getMockAccessibilityShortcutInfo() {
AccessibilityShortcutInfo mockInfo = Mockito.mock(AccessibilityShortcutInfo.class);
final ActivityInfo activityInfo = Mockito.mock(ActivityInfo.class);
activityInfo.applicationInfo = new ApplicationInfo();
when(mockInfo.getActivityInfo()).thenReturn(activityInfo);
when(activityInfo.loadLabel(any())).thenReturn(DEFAULT_LABEL);
when(mockInfo.loadSummary(any())).thenReturn(DEFAULT_SUMMARY);
when(mockInfo.loadDescription(any())).thenReturn(DEFAULT_DESCRIPTION);
when(mockInfo.getComponentName()).thenReturn(COMPONENT_NAME);
when(mockInfo.getComponentName()).thenReturn(ACTIVITY_COMPONENT_NAME);
return mockInfo;
}
private void setInvisibleToggleFragmentType(AccessibilityServiceInfo info) {

View File

@@ -16,10 +16,11 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_QS_TOOLTIP_RESHOW;
import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE;
import static com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import static com.google.common.truth.Truth.assertThat;
@@ -34,7 +35,6 @@ import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.icu.text.CaseMap;
import android.os.Bundle;
@@ -49,7 +49,6 @@ import android.view.accessibility.Flags;
import android.widget.PopupWindow;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
@@ -128,7 +127,7 @@ public class AccessibilityShortcutPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.getComponentName().flattenToString());
// Compare to default UserShortcutType
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE);
assertThat(expectedType).isEqualTo(SOFTWARE);
}
@Test
@@ -140,85 +139,20 @@ public class AccessibilityShortcutPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.getComponentName().flattenToString());
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
assertThat(expectedType).isEqualTo(SOFTWARE | HARDWARE);
}
@Test
public void updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable() {
final PreferredShortcut hardwareShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE);
PLACEHOLDER_COMPONENT_NAME.flattenToString(), HARDWARE);
putUserShortcutTypeIntoSharedPreference(mContext, hardwareShortcut);
mFragment.updateShortcutPreferenceData();
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.getComponentName().flattenToString());
assertThat(expectedType).isEqualTo(UserShortcutType.HARDWARE);
}
@Test
public void setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() {
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC,
PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mShortcutPreference = shortcutPreference;
mFragment.mShortcutPreference.setChecked(false);
mFragment.setupEditShortcutDialog(dialog);
final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.EMPTY);
}
@Test
public void setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() {
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC,
PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
final PreferredShortcut hardwareShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE);
mFragment.mShortcutPreference = shortcutPreference;
PreferredShortcuts.saveUserShortcutType(mContext, hardwareShortcut);
mFragment.mShortcutPreference.setChecked(true);
mFragment.setupEditShortcutDialog(dialog);
final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.HARDWARE);
}
@Test
@Config(shadows = ShadowFragment.class)
public void restoreValueFromSavedInstanceState_assignShortcutTypeToVariable() {
mContext.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, AccessibilityDialogUtils.DialogType.EDIT_SHORTCUT_GENERIC,
PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final Bundle savedInstanceState = new Bundle();
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mShortcutPreference = shortcutPreference;
savedInstanceState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE,
UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
mFragment.onAttach(mContext);
mFragment.onCreate(savedInstanceState);
mFragment.setupEditShortcutDialog(dialog);
final int value = mFragment.getShortcutTypeCheckBoxValue();
mFragment.saveNonEmptyUserShortcutType(value);
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.getComponentName().flattenToString());
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
assertThat(expectedType).isEqualTo(HARDWARE);
}
@Test
@@ -265,8 +199,7 @@ public class AccessibilityShortcutPreferenceFragmentTest {
}
@Test
@EnableFlags(com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN)
public void onSettingsClicked_editShortcutsFullScreenFlagOn_showFullScreenEditShortcutScreen() {
public void onSettingsClicked_showFullScreenEditShortcutScreen() {
Activity activity = Robolectric.setupActivity(FragmentActivity.class);
when(mFragment.getContext()).thenReturn(activity);
Context context = mFragment.getContext();
@@ -290,7 +223,7 @@ public class AccessibilityShortcutPreferenceFragmentTest {
public void getShortcutTypeSummary_shortcutSummaryIsCorrectlySet() {
final PreferredShortcut userPreferredShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(),
UserShortcutType.HARDWARE | UserShortcutType.QUICK_SETTINGS);
HARDWARE | QUICK_SETTINGS);
putUserShortcutTypeIntoSharedPreference(mContext, userPreferredShortcut);
final ShortcutPreference shortcutPreference =
new ShortcutPreference(mContext, /* attrs= */ null);
@@ -308,8 +241,6 @@ public class AccessibilityShortcutPreferenceFragmentTest {
assertThat(summary).isEqualTo(expected);
}
private void callEmptyOnClicked(DialogInterface dialog, int which) {}
private void putStringIntoSettings(String key, String componentName) {
Settings.Secure.putString(mContext.getContentResolver(), key, componentName);
}

View File

@@ -16,11 +16,15 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import static com.android.settings.accessibility.AccessibilityShortcutsTutorial.createAccessibilityTutorialDialog;
import static com.android.settings.accessibility.AccessibilityShortcutsTutorial.createAccessibilityTutorialDialogForSetupWizard;
import static com.android.settings.accessibility.AccessibilityShortcutsTutorial.createShortcutTutorialPages;
import static com.android.settings.accessibility.AccessibilityShortcutsTutorial.showGestureNavigationTutorialDialog;
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import static com.google.common.truth.Truth.assertThat;
@@ -99,7 +103,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnTripleTapShortcut_hasOnePage() {
mShortcutTypes |= UserShortcutType.TRIPLETAP;
mShortcutTypes |= TRIPLETAP;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
@@ -114,7 +118,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void createTutorialPages_turnOnTwoFingerTripleTapShortcut_hasOnePage() {
mShortcutTypes |= UserShortcutType.TWOFINGER_DOUBLETAP;
mShortcutTypes |= TWOFINGER_DOUBLETAP;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
@@ -129,7 +133,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void createTutorialPages_turnOnQuickSettingShortcut_hasOnePage() {
mShortcutTypes |= UserShortcutType.QUICK_SETTINGS;
mShortcutTypes |= QUICK_SETTINGS;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
@@ -143,7 +147,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnSoftwareShortcut_hasOnePage() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
@@ -157,8 +161,8 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnSoftwareAndHardwareShortcuts_hasTwoPages() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= UserShortcutType.HARDWARE;
mShortcutTypes |= SOFTWARE;
mShortcutTypes |= HARDWARE;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
@@ -172,7 +176,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnA11yGestureShortcut_linkButtonShownWithText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
@@ -191,7 +195,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnA11yNavButtonShortcut_linkButtonShownWithText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
@@ -210,7 +214,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnFloatingButtonShortcut_linkButtonShownWithText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true);
@@ -228,7 +232,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnHardwareShortcut_linkButtonGone() {
mShortcutTypes |= UserShortcutType.HARDWARE;
mShortcutTypes |= HARDWARE;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
@@ -241,7 +245,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createTutorialPages_turnOnSoftwareShortcut_showFromSuW_linkButtonGone() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
final AlertDialog alertDialog =
createAccessibilityTutorialDialogForSetupWizard(
@@ -256,7 +260,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void createAccessibilityTutorialDialog_qsShortcut_inSuwTalkbackOn_verifyText() {
mShortcutTypes |= UserShortcutType.QUICK_SETTINGS;
mShortcutTypes |= QUICK_SETTINGS;
setTouchExplorationEnabled(true);
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_quick_setting);
@@ -288,7 +292,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void createAccessibilityTutorialDialog_qsShortcut_notInSuwTalkbackOn_verifyText() {
mShortcutTypes |= UserShortcutType.QUICK_SETTINGS;
mShortcutTypes |= QUICK_SETTINGS;
setTouchExplorationEnabled(true);
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_quick_setting);
@@ -314,7 +318,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void createAccessibilityTutorialDialog_qsShortcut_inSuwTalkbackOff_verifyText() {
mShortcutTypes |= UserShortcutType.QUICK_SETTINGS;
mShortcutTypes |= QUICK_SETTINGS;
setTouchExplorationEnabled(false);
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_quick_setting);
@@ -345,7 +349,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void createAccessibilityTutorialDialog_qsShortcut_notInSuwTalkbackOff_verifyText() {
mShortcutTypes |= UserShortcutType.QUICK_SETTINGS;
mShortcutTypes |= QUICK_SETTINGS;
setTouchExplorationEnabled(false);
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_quick_setting);
@@ -370,7 +374,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createAccessibilityTutorialDialog_volumeKeysShortcut_verifyText() {
mShortcutTypes |= UserShortcutType.HARDWARE;
mShortcutTypes |= HARDWARE;
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_volume);
final CharSequence expectedInstruction = mContext.getString(
@@ -390,7 +394,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createAccessibilityTutorialDialog_tripleTapShortcut_verifyText() {
mShortcutTypes |= UserShortcutType.TRIPLETAP;
mShortcutTypes |= TRIPLETAP;
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_triple);
final CharSequence expectedInstruction = mContext.getString(
@@ -411,7 +415,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void createAccessibilityTutorialDialog_twoFingerDoubleTapShortcut_verifyText() {
mShortcutTypes |= UserShortcutType.TWOFINGER_DOUBLETAP;
mShortcutTypes |= TWOFINGER_DOUBLETAP;
final int numFingers = 2;
final String expectedTitle = mContext.getString(
R.string.accessibility_tutorial_dialog_title_two_finger_double, numFingers);
@@ -432,7 +436,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createAccessibilityTutorialDialog_floatingButtonShortcut_verifyText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true);
final String expectedTitle = mContext.getString(
@@ -454,7 +458,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createAccessibilityTutorialDialog_navA11yButtonShortcut_verifyText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
final String expectedTitle = mContext.getString(
@@ -476,7 +480,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createAccessibilityTutorialDialog_gestureShortcut_talkbackOn_verifyText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
setTouchExplorationEnabled(true);
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
@@ -501,7 +505,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void createAccessibilityTutorialDialog_gestureShortcut_talkbackOff_verifyText() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
setTouchExplorationEnabled(false);
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
@@ -526,7 +530,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void performClickOnPositiveButton_turnOnSoftwareShortcut_dismiss() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(mContext, mShortcutTypes, FAKE_FEATURE_NAME);
alertDialog.show();
@@ -540,7 +544,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void performClickOnPositiveButton_turnOnSoftwareShortcut_callOnClickListener() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(
mContext, mShortcutTypes, mOnClickListener, FAKE_FEATURE_NAME);
@@ -555,7 +559,7 @@ public final class AccessibilityShortcutsTutorialTest {
@Test
public void performClickOnNegativeButton_turnOnSoftwareShortcut_directToSettingsPage() {
mShortcutTypes |= UserShortcutType.SOFTWARE;
mShortcutTypes |= SOFTWARE;
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
final AlertDialog alertDialog =
createAccessibilityTutorialDialog(activity, mShortcutTypes, FAKE_FEATURE_NAME);

View File

@@ -16,6 +16,12 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
@@ -39,9 +45,9 @@ import android.view.accessibility.Flags;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.testutils.AccessibilityTestUtils;
import org.junit.Before;
@@ -159,52 +165,52 @@ public final class AccessibilityUtilTest {
@Test
public void hasValueInSettings_putValue_hasValue() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
assertThat(AccessibilityUtil.hasValueInSettings(mContext, UserShortcutType.SOFTWARE,
assertThat(AccessibilityUtil.hasValueInSettings(mContext, SOFTWARE,
MOCK_COMPONENT_NAME)).isTrue();
}
@Test
public void getUserShortcutTypeFromSettings_putOneValue_hasValue() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
MOCK_COMPONENT_NAME);
assertThat(shortcutTypes).isEqualTo(
UserShortcutType.SOFTWARE
SOFTWARE
);
}
@Test
public void getUserShortcutTypeFromSettings_putTwoValues_hasValue() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
MOCK_COMPONENT_NAME);
assertThat(shortcutTypes).isEqualTo(
UserShortcutType.SOFTWARE
| UserShortcutType.HARDWARE
SOFTWARE
| HARDWARE
);
}
@Test
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void getUserShortcutTypeFromSettings_threeShortcutTypesChosen() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(UserShortcutType.QUICK_SETTINGS, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(QUICK_SETTINGS, MOCK_COMPONENT_NAME.flattenToString());
final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
MOCK_COMPONENT_NAME);
assertThat(shortcutTypes).isEqualTo(
UserShortcutType.SOFTWARE
| UserShortcutType.HARDWARE
| UserShortcutType.QUICK_SETTINGS
SOFTWARE
| HARDWARE
| QUICK_SETTINGS
);
}
@@ -212,7 +218,7 @@ public final class AccessibilityUtilTest {
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void optInAllValuesToSettings_optInValue_haveMatchString() {
clearShortcuts();
int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE;
int shortcutTypes = SOFTWARE | HARDWARE;
AccessibilityUtil.optInAllValuesToSettings(mContext, shortcutTypes, MOCK_COMPONENT_NAME);
@@ -229,8 +235,8 @@ public final class AccessibilityUtilTest {
AccessibilityManager a11yManager =
AccessibilityTestUtils.setupMockAccessibilityManager(mContext);
Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME.flattenToString());
int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE
| UserShortcutType.QUICK_SETTINGS;
int shortcutTypes = SOFTWARE | HARDWARE
| QUICK_SETTINGS;
AccessibilityUtil.optInAllValuesToSettings(mContext, shortcutTypes, MOCK_COMPONENT_NAME);
@@ -243,9 +249,9 @@ public final class AccessibilityUtilTest {
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void optInValueToSettings_optInValue_haveMatchString() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE,
AccessibilityUtil.optInValueToSettings(mContext, SOFTWARE,
MOCK_COMPONENT_NAME2);
assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
@@ -261,10 +267,10 @@ public final class AccessibilityUtilTest {
Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME2.flattenToString());
AccessibilityUtil.optInValueToSettings(
mContext, UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME2);
mContext, HARDWARE, MOCK_COMPONENT_NAME2);
verify(a11yManager).enableShortcutsForTargets(
/* enable= */ true, UserShortcutType.HARDWARE,
/* enable= */ true, HARDWARE,
shortcutTargets, UserHandle.myUserId());
verifyNoMoreInteractions(a11yManager);
}
@@ -272,11 +278,11 @@ public final class AccessibilityUtilTest {
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void optInValueToSettings_optInTwoValues_haveMatchString() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE,
AccessibilityUtil.optInValueToSettings(mContext, SOFTWARE,
MOCK_COMPONENT_NAME2);
AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE,
AccessibilityUtil.optInValueToSettings(mContext, SOFTWARE,
MOCK_COMPONENT_NAME2);
assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
@@ -287,10 +293,10 @@ public final class AccessibilityUtilTest {
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void optOutAllValuesToSettings_optOutValue_emptyString() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
int shortcutTypes =
UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP;
SOFTWARE | HARDWARE | TRIPLETAP;
AccessibilityUtil.optOutAllValuesFromSettings(mContext, shortcutTypes,
MOCK_COMPONENT_NAME);
@@ -305,8 +311,8 @@ public final class AccessibilityUtilTest {
AccessibilityManager a11yManager =
AccessibilityTestUtils.setupMockAccessibilityManager(mContext);
int shortcutTypes =
UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE
| UserShortcutType.QUICK_SETTINGS;
SOFTWARE | HARDWARE
| QUICK_SETTINGS;
Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME.flattenToString());
AccessibilityUtil.optOutAllValuesFromSettings(mContext, shortcutTypes,
@@ -322,9 +328,9 @@ public final class AccessibilityUtilTest {
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void optOutValueFromSettings_optOutValue_emptyString() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
AccessibilityUtil.optOutValueFromSettings(mContext, UserShortcutType.SOFTWARE,
AccessibilityUtil.optOutValueFromSettings(mContext, SOFTWARE,
MOCK_COMPONENT_NAME);
assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEmpty();
@@ -333,10 +339,10 @@ public final class AccessibilityUtilTest {
@Test
@DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void optOutValueFromSettings_optOutValue_haveMatchString() {
setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString(),
setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString(),
MOCK_COMPONENT_NAME2.flattenToString());
AccessibilityUtil.optOutValueFromSettings(mContext, UserShortcutType.SOFTWARE,
AccessibilityUtil.optOutValueFromSettings(mContext, SOFTWARE,
MOCK_COMPONENT_NAME2);
assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
@@ -351,36 +357,36 @@ public final class AccessibilityUtilTest {
Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME.flattenToString());
AccessibilityUtil.optOutValueFromSettings(
mContext, UserShortcutType.QUICK_SETTINGS, MOCK_COMPONENT_NAME);
mContext, QUICK_SETTINGS, MOCK_COMPONENT_NAME);
verify(a11yManager).enableShortcutsForTargets(
/* enable= */ false, UserShortcutType.QUICK_SETTINGS,
/* enable= */ false, QUICK_SETTINGS,
shortcutTargets, UserHandle.myUserId());
verifyNoMoreInteractions(a11yManager);
}
@Test
public void convertKeyFromSettings_shortcutTypeSoftware() {
assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.SOFTWARE))
assertThat(AccessibilityUtil.convertKeyFromSettings(SOFTWARE))
.isEqualTo(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
}
@Test
public void convertKeyFromSettings_shortcutTypeHardware() {
assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.HARDWARE))
assertThat(AccessibilityUtil.convertKeyFromSettings(HARDWARE))
.isEqualTo(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
}
@Test
public void convertKeyFromSettings_shortcutTypeTripleTap() {
assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.TRIPLETAP))
assertThat(AccessibilityUtil.convertKeyFromSettings(TRIPLETAP))
.isEqualTo(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
}
@Test
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void convertKeyFromSettings_shortcutTypeMultiFingersMultiTap() {
assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.TWOFINGER_DOUBLETAP))
assertThat(AccessibilityUtil.convertKeyFromSettings(TWOFINGER_DOUBLETAP))
.isEqualTo(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED);
}
@@ -388,7 +394,7 @@ public final class AccessibilityUtilTest {
@Test
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void convertKeyFromSettings_shortcutTypeQuickSettings() {
assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.QUICK_SETTINGS))
assertThat(AccessibilityUtil.convertKeyFromSettings(QUICK_SETTINGS))
.isEqualTo(Settings.Secure.ACCESSIBILITY_QS_TARGETS);
}

View File

@@ -18,66 +18,97 @@ package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
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.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ApplicationProvider;
import androidx.fragment.app.FragmentFactory;
import androidx.fragment.app.testing.FragmentScenario;
import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settingslib.widget.FooterPreference;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupdesign.GlifLayout;
import com.google.android.setupdesign.GlifPreferenceLayout;
import org.junit.After;
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;
/** Tests for {@link AutoBrightnessPreferenceFragmentForSetupWizard}. */
@RunWith(RobolectricTestRunner.class)
public class AutoBrightnessPreferenceFragmentForSetupWizardTest {
@Rule
public final MockitoRule mMockito = MockitoJUnit.rule();
// Same as AutoBrightnessPreferenceFragmentForSetupWizard#FOOTER_PREFERENCE_KEY
private static final String FOOTER_PREFERENCE_KEY = "auto_brightness_footer";
private FragmentScenario<AutoBrightnessPreferenceFragmentForSetupWizard> mFragmentScenario;
@Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private GlifPreferenceLayout mGlifLayoutView;
@Mock
private FooterBarMixin mFooterBarMixin;
private AutoBrightnessPreferenceFragmentForSetupWizard mFragment;
private GlifLayout mGlifLayout;
@Before
public void setUp() {
mFragment = spy(new AutoBrightnessPreferenceFragmentForSetupWizard());
doReturn(mock(LifecycleOwner.class)).when(mFragment).getViewLifecycleOwner();
doReturn(mContext).when(mFragment).getContext();
when(mGlifLayoutView.getMixin(eq(FooterBarMixin.class))).thenReturn(mFooterBarMixin);
mFragmentScenario = FragmentScenario
.launch(
AutoBrightnessPreferenceFragmentForSetupWizard.class,
/* fragmentArgs= */ (Bundle) null,
R.style.GlifTheme,
/* factory= */ (FragmentFactory) null)
.moveToState(Lifecycle.State.RESUMED);
mFragmentScenario.onFragment(fragment -> mFragment = fragment);
View view = mFragment.getView();
assertThat(view).isInstanceOf(GlifPreferenceLayout.class);
mGlifLayout = (GlifLayout) view;
}
@After
public void tearDown() {
mFragmentScenario.close();
}
@Test
public void setHeaderText_onViewCreated_verifyAction() {
final String title = "title";
doReturn(title).when(mContext).getString(R.string.auto_brightness_title);
public void onViewCreated_verifyGlifHerderText() {
assertThat(mGlifLayout.getHeaderText())
.isEqualTo(mFragment.getString(R.string.auto_brightness_title));
}
mFragment.onViewCreated(mGlifLayoutView, null);
@Test
public void onViewCreated_verifyGlifFooter() {
FooterBarMixin footerMixin = mGlifLayout.getMixin(FooterBarMixin.class);
assertThat(footerMixin).isNotNull();
verify(mGlifLayoutView).setHeaderText(title);
Button footerButton = footerMixin.getPrimaryButtonView();
assertThat(footerButton).isNotNull();
assertThat(footerButton.getText().toString()).isEqualTo(mFragment.getString(R.string.done));
footerButton.performClick();
assertThat(mFragment.getActivity().isFinishing()).isTrue();
}
@Test
public void onViewCreated_verifyFooterPreference() {
Preference pref = mFragment.findPreference(FOOTER_PREFERENCE_KEY);
assertThat(pref).isInstanceOf(FooterPreference.class);
FooterPreference footerPref = (FooterPreference) pref;
String exactTitle = footerPref.getTitle().toString();
assertThat(exactTitle).isEqualTo(mFragment.getString(R.string.auto_brightness_description));
// Ensure that footer content description has "About XXX" prefix for consistency with other
// accessibility suw pages
String expectedContentDescription =
mFragment.getString(R.string.auto_brightness_content_description_title)
+ "\n\n" + exactTitle;
assertThat(footerPref.getContentDescription().toString())
.isEqualTo(expectedContentDescription);
}
@Test
@@ -85,11 +116,4 @@ public class AutoBrightnessPreferenceFragmentForSetupWizardTest {
assertThat(mFragment.getMetricsCategory()).isEqualTo(
SettingsEnums.SUW_ACCESSIBILITY_AUTO_BRIGHTNESS);
}
@Test
public void onViewCreated_verifyAction() {
mFragment.onViewCreated(mGlifLayoutView, null);
verify(mFooterBarMixin).setPrimaryButton(any());
}
}

View File

@@ -28,23 +28,33 @@ import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AttributeSet;
import android.widget.SeekBar;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.testutils.shadow.ShadowSystemSettings;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.Locale;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowSystemSettings.class,
})
public class BalanceSeekBarTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
// Fix the maximum process value to 200 for testing the BalanceSeekBar.
// It affects the SeekBar value of center(100) and snapThreshold(200 * SNAP_TO_PERCENTAGE).
private static final int MAX_PROGRESS_VALUE = 200;
@@ -143,6 +153,62 @@ public class BalanceSeekBarTest {
assertThat(mSeekBar.getProgress()).isEqualTo(progressWithoutThreshold);
}
@Test
@EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
public void onProgressChanged_getStateDescription_centered_leftFirst() {
// Seek bar centered
int progress = (int) (0.50f * MAX_PROGRESS_VALUE);
mSeekBar.setMax(MAX_PROGRESS_VALUE);
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
assertThat(mSeekBar.getStateDescription()).isEqualTo(
mContext.getString(R.string.audio_seek_bar_state_left_first,
Utils.formatPercentage(50), Utils.formatPercentage(50)));
}
@Test
@EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
public void onProgressChanged_getStateDescription_centered_rtl_rightFirst() {
// RTL layout
mContext.getResources().getConfiguration().setLayoutDirection(new Locale("iw", "IL"));
// Seek bar centered
int progress = (int) (0.50f * MAX_PROGRESS_VALUE);
mSeekBar.setMax(MAX_PROGRESS_VALUE);
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
assertThat(mSeekBar.getStateDescription()).isEqualTo(
mContext.getString(R.string.audio_seek_bar_state_right_first,
Utils.formatPercentage(50), Utils.formatPercentage(50)));
}
@Test
@EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
public void onProgressChanged_getStateDescription_25percent_leftFirst() {
// Seek bar 3/4th toward the left
int progress = (int) (0.25f * MAX_PROGRESS_VALUE);
mSeekBar.setMax(MAX_PROGRESS_VALUE);
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
assertThat(mSeekBar.getStateDescription()).isEqualTo(
mContext.getString(R.string.audio_seek_bar_state_left_first,
Utils.formatPercentage(75), Utils.formatPercentage(25)));
}
@Test
@EnableFlags(Flags.FLAG_AUDIO_BALANCE_STATE_DESCRIPTION)
public void onProgressChanged_getStateDescription_75percent_rightFirst() {
// Seek bar 3/4th toward the right
int progress = (int) (0.75f * MAX_PROGRESS_VALUE);
mSeekBar.setMax(MAX_PROGRESS_VALUE);
mProxySeekBarListener.onProgressChanged(mSeekBar, progress, true);
assertThat(mSeekBar.getStateDescription()).isEqualTo(
mContext.getString(R.string.audio_seek_bar_state_right_first,
Utils.formatPercentage(75), Utils.formatPercentage(25)));
}
// method to get the center from BalanceSeekBar for testing setMax().
private int getBalanceSeekBarCenter(BalanceSeekBar seekBar) {
return seekBar.getMax() / 2;

View File

@@ -17,6 +17,7 @@
package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.content.Context;
@@ -188,7 +189,8 @@ public class CaptioningAppearancePreferenceControllerTest {
R.array.captioning_font_size_selector_titles);
final String[] presetArray = mContext.getResources().getStringArray(
R.array.captioning_preset_selector_titles);
return mContext.getString(R.string.preference_summary_default_combination,
return mContext.getString(
com.android.settingslib.R.string.preference_summary_default_combination,
fontScaleArray[fontScaleIndex], presetArray[presetIndex]);
}
}

View File

@@ -20,6 +20,9 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
@@ -27,15 +30,23 @@ import com.android.settings.R;
import com.android.settings.testutils.XmlTestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/** Tests for {@link CaptioningMoreOptionsFragment}. */
@RunWith(RobolectricTestRunner.class)
public class CaptioningMoreOptionsFragmentTest {
// Language/locale preference key, from captioning_more_options.xml
private static final String CAPTIONING_LOCALE_KEY = "captioning_locale";
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private CaptioningMoreOptionsFragment mFragment;
@@ -65,11 +76,40 @@ public class CaptioningMoreOptionsFragmentTest {
@Test
public void getNonIndexableKeys_existInXmlLayout() {
final List<String> niks = CaptioningMoreOptionsFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
.getNonIndexableKeys(mContext)
.stream().filter(Objects::nonNull).collect(Collectors.toList());
final List<String> keys =
XmlTestUtils.getKeysFromPreferenceXml(mContext,
R.xml.captioning_more_options);
XmlTestUtils.getKeysFromPreferenceXml(mContext, R.xml.captioning_more_options);
assertThat(keys).containsAtLeastElementsIn(niks);
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void getNonIndexableKeys_captioningEnabled_localeIsSearchable() {
setCaptioningEnabled(true);
final List<String> niks = CaptioningMoreOptionsFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
// Not in NonIndexableKeys == searchable
assertThat(niks).doesNotContain(CAPTIONING_LOCALE_KEY);
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void getNonIndexableKeys_captioningDisabled_localeIsNotSearchable() {
setCaptioningEnabled(false);
final List<String> niks = CaptioningMoreOptionsFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
// In NonIndexableKeys == not searchable
assertThat(niks).contains(CAPTIONING_LOCALE_KEY);
}
private void setCaptioningEnabled(boolean enabled) {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED, enabled ? 1 : 0);
}
}

View File

@@ -18,61 +18,65 @@ package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowLooper;
/**
* Tests for {@link DaltonizerRadioButtonPreferenceController}
*/
@RunWith(RobolectricTestRunner.class)
public class DaltonizerRadioButtonPreferenceControllerTest implements
DaltonizerRadioButtonPreferenceController.OnChangeListener {
private static final String PREF_KEY = "daltonizer_mode_protanomaly";
private static final String PREF_VALUE = "11";
private static final String PREF_FAKE_VALUE = "-1";
public class DaltonizerRadioButtonPreferenceControllerTest {
private static final int DALTONIZER_MODE_INDEX = 0;
private static final String PREF_INVALID_VALUE = "-1";
private final Context mContext = ApplicationProvider.getApplicationContext();
private final String mPrefKey =
mContext.getResources()
.getStringArray(R.array.daltonizer_mode_keys)[DALTONIZER_MODE_INDEX];
private final String mPrefValue =
String.valueOf(mContext.getResources()
.getIntArray(R.array.daltonizer_type_values)[DALTONIZER_MODE_INDEX]);
private DaltonizerRadioButtonPreferenceController mController;
@Mock
private SelectorWithWidgetPreference mMockPref;
private Context mContext;
@Mock
private SelectorWithWidgetPreference mPreference;
private PreferenceScreen mScreen;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new DaltonizerRadioButtonPreferenceController(mContext, mock(Lifecycle.class),
PREF_KEY);
mController.setOnChangeListener(this);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mMockPref);
when(mMockPref.getKey()).thenReturn(PREF_KEY);
// initialize the value as unchecked
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, PREF_INVALID_VALUE);
mController = new DaltonizerRadioButtonPreferenceController(mContext, mPrefKey);
mPreference = new SelectorWithWidgetPreference(mContext);
mPreference.setKey(mPrefKey);
mScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
mScreen.addPreference(mPreference);
mController.displayPreference(mScreen);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
}
@Override
public void onCheckedChanged(Preference preference) {
mController.updateState(preference);
@After
public void cleanUp() {
mLifecycle.removeObserver(mController);
}
@Test
@@ -81,36 +85,62 @@ public class DaltonizerRadioButtonPreferenceControllerTest implements
}
@Test
public void updateState_notChecked() {
public void updateState_valueNotMatched_notChecked() {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, PREF_FAKE_VALUE);
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, PREF_INVALID_VALUE);
mController.updateState(mMockPref);
mController.updateState(mPreference);
// the first checked state is set to false by control
verify(mMockPref, atLeastOnce()).setChecked(false);
verify(mMockPref, never()).setChecked(true);
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void updateState_checked() {
public void updateState_valueMatched_checked() {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, PREF_VALUE);
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, mPrefValue);
mController.updateState(mMockPref);
mController.updateState(mPreference);
// the first checked state is set to false by control
verify(mMockPref, atLeastOnce()).setChecked(false);
verify(mMockPref, atLeastOnce()).setChecked(true);
assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void onRadioButtonClick_shouldReturnDaltonizerValue() {
mController.onRadioButtonClicked(mMockPref);
mController.onRadioButtonClicked(mPreference);
final String accessibilityDaltonizerValue = Settings.Secure.getString(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);
assertThat(accessibilityDaltonizerValue).isEqualTo(mPrefValue);
}
assertThat(accessibilityDaltonizerValue).isEqualTo(PREF_VALUE);
@Test
public void onResume_settingsValueChangedToUnmatch_preferenceBecomesUnchecked() {
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, mPrefValue);
mController.updateState(mPreference);
assertThat(mPreference.isChecked()).isTrue();
mLifecycle.addObserver(mController);
mLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_RESUME);
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, PREF_INVALID_VALUE);
ShadowLooper.idleMainLooper();
assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void onPause_settingsValueChangedAndMatch_preferenceStateNotUpdated() {
assertThat(mPreference.isChecked()).isFalse();
mLifecycle.addObserver(mController);
mLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_RESUME);
mLifecycle.handleLifecycleEvent(androidx.lifecycle.Lifecycle.Event.ON_PAUSE);
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, mPrefValue);
ShadowLooper.idleMainLooper();
assertThat(mPreference.isChecked()).isFalse();
}
}

View File

@@ -16,38 +16,39 @@
package com.android.settings.accessibility;
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
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 static org.robolectric.Shadows.shadowOf;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.server.accessibility.Flags;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
/** Tests for {@link DaltonizerSaturationSeekbarPreferenceController}. */
@@ -57,11 +58,10 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
private ContentResolver mContentResolver;
private DaltonizerSaturationSeekbarPreferenceController mController;
private int mOriginalSaturationLevel = -1;
private PreferenceScreen mScreen;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Mock
private SeekBarPreference mPreference;
@Rule
@@ -69,18 +69,16 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
Context context = ApplicationProvider.getApplicationContext();
mContentResolver = context.getContentResolver();
mOriginalSaturationLevel = Settings.Secure.getInt(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
7);
mScreen = spy(new PreferenceScreen(context, /* attrs= */ null));
when(mScreen.findPreference(ToggleDaltonizerPreferenceFragment.KEY_SATURATION))
.thenReturn(mPreference);
mPreference = new SeekBarPreference(context);
mPreference.setKey(ToggleDaltonizerPreferenceFragment.KEY_SATURATION);
mScreen = new PreferenceManager(context).createPreferenceScreen(context);
mScreen.addPreference(mPreference);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new DaltonizerSaturationSeekbarPreferenceController(
context,
ToggleDaltonizerPreferenceFragment.KEY_SATURATION);
@@ -88,12 +86,26 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@After
public void cleanup() {
Settings.Secure.putInt(
Settings.Secure.putString(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
null);
Settings.Secure.putString(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
null);
Settings.Secure.putString(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
mOriginalSaturationLevel);
null);
}
@Test
public void constructor_defaultValuesMatch() {
assertThat(mController.getSliderPosition()).isEqualTo(7);
assertThat(mController.getMax()).isEqualTo(10);
assertThat(mController.getMin()).isEqualTo(1);
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
@@ -103,28 +115,88 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabled_available() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void constructor_defaultValuesMatch() {
assertThat(mController.getSliderPosition()).isEqualTo(7);
assertThat(mController.getMax()).isEqualTo(10);
assertThat(mController.getMin()).isEqualTo(0);
public void getAvailabilityStatus_defaultSettings_unavailable() {
// By default enabled == false.
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void displayPreference_enabled_visible() {
public void getAvailabilityStatus_enabledDefaultDisplayMode_available() {
setDaltonizerEnabled(1);
// By default display mode is deuteranomaly.
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabledProtanEnabled_available() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabledDeutranEnabled_available() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 12);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabledTritanEnabled_available() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 13);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabledGrayScale_disabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 0);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabledColorCorrectionDisabled_disabled() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void getAvailabilityStatus_flagEnabledColorCorrectionDisabledGrayScale_disabled() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void displayPreference_flagEnabledColorCorrectionEnabled_enabledWithDefaultValues() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
mController.displayPreference(mScreen);
verify(mPreference).setMax(eq(10));
verify(mPreference).setMin(eq(0));
verify(mPreference).setProgress(eq(7));
verify(mPreference).setContinuousUpdates(eq(true));
verify(mPreference).setOnPreferenceChangeListener(eq(mController));
verify(mPreference).setVisible(eq(true));
assertThat(mPreference.isEnabled()).isTrue();
assertThat(mPreference.getMax()).isEqualTo(10);
assertThat(mPreference.getMin()).isEqualTo(1);
assertThat(mPreference.getProgress()).isEqualTo(7);
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getOnPreferenceChangeListener()).isEqualTo(mController);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_COLOR_CORRECTION_SATURATION)
public void displayPreference_flagEnabledColorCorrectionDisabled_disabledWithDefaultValues() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
mController.displayPreference(mScreen);
assertThat(mPreference.isEnabled()).isFalse();
assertThat(mPreference.getMax()).isEqualTo(10);
assertThat(mPreference.getMin()).isEqualTo(1);
assertThat(mPreference.getProgress()).isEqualTo(7);
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getOnPreferenceChangeListener()).isEqualTo(mController);
}
@Test
@@ -132,12 +204,8 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
public void displayPreference_disabled_notVisible() {
mController.displayPreference(mScreen);
verify(mPreference).setMax(eq(10));
verify(mPreference).setMin(eq(0));
verify(mPreference).setProgress(eq(7));
verify(mPreference).setContinuousUpdates(eq(true));
verify(mPreference, never()).setOnPreferenceChangeListener(any());
verify(mPreference).setVisible(eq(false));
assertThat(mPreference.isVisible()).isFalse();
assertThat(mPreference.getOnPreferenceChangeListener()).isNull();
}
@Test
@@ -153,13 +221,13 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
@Test
public void setSliderPosition_min_secureSettingsUpdated() {
var isSliderSet = mController.setSliderPosition(0);
var isSliderSet = mController.setSliderPosition(1);
assertThat(isSliderSet).isTrue();
assertThat(Settings.Secure.getInt(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
7)).isEqualTo(0);
7)).isEqualTo(1);
}
@Test
@@ -194,4 +262,136 @@ public class DaltonizerSaturationSeekbarPreferenceControllerTest {
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
7)).isEqualTo(7);
}
@Test
public void updateState_enabledProtan_preferenceEnabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void updateState_enabledDeuteran_preferenceEnabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 12);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void updateState_enabledTritan_preferenceEnabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 13);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void updateState_disabledGrayScale_preferenceDisabled() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void updateState_nullPreference_noError() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 0);
mController.updateState(null);
}
@Test
public void updateState_enabledGrayScale_preferenceDisabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 0);
mController.updateState(mPreference);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void onResume_daltonizerEnabledAfterResumed_preferenceEnabled() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
mController.displayPreference(mScreen);
assertThat(mPreference.isEnabled()).isFalse();
mLifecycle.addObserver(mController);
mLifecycle.handleLifecycleEvent(ON_RESUME);
setDaltonizerEnabled(1);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void onResume_daltonizerDisabledAfterResumed_preferenceDisabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
mController.displayPreference(mScreen);
assertThat(mPreference.isEnabled()).isTrue();
mLifecycle.addObserver(mController);
mLifecycle.handleLifecycleEvent(ON_RESUME);
setDaltonizerEnabled(0);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void onResume_daltonizerGrayScaledAfterResumed_preferenceDisabled() {
setDaltonizerMode(/* enabled= */ 1, /* mode= */ 11);
mController.displayPreference(mScreen);
assertThat(mPreference.isEnabled()).isTrue();
mLifecycle.addObserver(mController);
mLifecycle.handleLifecycleEvent(ON_RESUME);
setDaltonizerDisplay(0);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isEnabled()).isFalse();
}
@Test
public void onStop_daltonizerEnabledAfterOnStop_preferenceNotChanged() {
setDaltonizerMode(/* enabled= */ 0, /* mode= */ 11);
mController.displayPreference(mScreen);
assertThat(mPreference.isEnabled()).isFalse();
mLifecycle.addObserver(mController);
mLifecycle.handleLifecycleEvent(ON_STOP);
// enabled.
setDaltonizerEnabled(1);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isEnabled()).isFalse();
}
private void setDaltonizerMode(int enabled, int mode) {
setDaltonizerEnabled(enabled);
setDaltonizerDisplay(mode);
}
private void setDaltonizerEnabled(int enabled) {
Settings.Secure.putInt(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
enabled);
}
private void setDaltonizerDisplay(int mode) {
Settings.Secure.putString(
mContentResolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
Integer.toString(mode));
}
}

View File

@@ -67,22 +67,23 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private Preference mPreference;
@Spy
private ContentResolver mContentResolver = mContext.getContentResolver();
@Mock
private PreferenceScreen mPreferenceScreen;
private Preference mPreference;
private FlashNotificationsPreviewPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mPreferenceScreen.findPreference(PREFERENCE_KEY)).thenReturn(mPreference);
when(mPreference.getKey()).thenReturn(PREFERENCE_KEY);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
mPreference = new Preference(mContext);
mPreference.setKey(PREFERENCE_KEY);
when(mPreferenceScreen.findPreference(PREFERENCE_KEY)).thenReturn(mPreference);
mController = new FlashNotificationsPreviewPreferenceController(mContext, PREFERENCE_KEY);
}
@@ -97,40 +98,45 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
}
@Test
public void testDisplayPreference_torchPresent_cameraOff_screenOff_verifyDisabled() {
public void testDisplayPreference_torchPresent_cameraOff_screenOff_notVisible() {
setFlashNotificationsState(FlashNotificationsUtil.State.OFF);
mController.displayPreference(mPreferenceScreen);
verify(mPreference).setEnabled(eq(false));
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void testDisplayPreference_torchPresent_cameraOn_screenOff_verifyEnabled() {
public void testDisplayPreference_torchPresent_cameraOn_screenOff_isVisible() {
setFlashNotificationsState(FlashNotificationsUtil.State.CAMERA);
mController.displayPreference(mPreferenceScreen);
verify(mPreference).setEnabled(eq(true));
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void testDisplayPreference_torchPresent_cameraOff_screenOn_verifyEnabled() {
public void testDisplayPreference_torchPresent_cameraOff_screenOn_isVisible() {
setFlashNotificationsState(FlashNotificationsUtil.State.SCREEN);
mController.displayPreference(mPreferenceScreen);
verify(mPreference).setEnabled(eq(true));
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void testDisplayPreference_torchPresent_cameraOn_screenOn_verifyEnabled() {
public void testDisplayPreference_torchPresent_cameraOn_screenOn_isVisible() {
setFlashNotificationsState(FlashNotificationsUtil.State.CAMERA_SCREEN);
mController.displayPreference(mPreferenceScreen);
verify(mPreference).setEnabled(eq(true));
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void testHandlePreferenceTreeClick_invalidPreference() {
mController.handlePreferenceTreeClick(mock(Preference.class));
verify(mContext, never()).sendBroadcastAsUser(any(), any());
}
@@ -160,6 +166,7 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
@Test
public void onStateChanged_onResume_cameraUri_verifyRegister() {
mController.onStateChanged(mock(LifecycleOwner.class), Lifecycle.Event.ON_RESUME);
verify(mContentResolver).registerContentObserver(
eq(Settings.System.getUriFor(Settings.System.CAMERA_FLASH_NOTIFICATION)),
anyBoolean(), eq(mController.mContentObserver));
@@ -168,6 +175,7 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
@Test
public void onStateChanged_onResume_screenUri_verifyRegister() {
mController.onStateChanged(mock(LifecycleOwner.class), Lifecycle.Event.ON_RESUME);
verify(mContentResolver).registerContentObserver(
eq(Settings.System.getUriFor(Settings.System.SCREEN_FLASH_NOTIFICATION)),
anyBoolean(), eq(mController.mContentObserver));
@@ -176,6 +184,7 @@ public class FlashNotificationsPreviewPreferenceControllerTest {
@Test
public void onStateChanged_onPause_verifyUnregister() {
mController.onStateChanged(mock(LifecycleOwner.class), Lifecycle.Event.ON_PAUSE);
verify(mContentResolver).unregisterContentObserver(eq(mController.mContentObserver));
}
}

View File

@@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.preference.PreferenceManager;
@@ -34,19 +36,24 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class KeyboardBounceKeyPreferenceControllerTest {
private static final String KEY_ACCESSIBILITY_BOUNCE_KEYS =
Settings.Secure.ACCESSIBILITY_BOUNCE_KEYS;
private static final int UNKNOWN = -1;
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
private final KeyboardBounceKeyPreferenceController mController =
@@ -131,4 +138,26 @@ public class KeyboardBounceKeyPreferenceControllerTest {
mContext.getContentResolver(), KEY_ACCESSIBILITY_BOUNCE_KEYS,
UNKNOWN)).isNotEqualTo(OFF);
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void updateNonIndexableKeys_physicalKeyboardExists_returnEmptyList() {
Assume.assumeTrue(AccessibilitySettings.isAnyHardKeyboardsExist());
List<String> nonIndexableKeys = new ArrayList<>();
mController.updateNonIndexableKeys(nonIndexableKeys);
assertThat(nonIndexableKeys).isEmpty();
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void updateNonIndexableKeys_noPhysicalKeyboard_returnPreKey() {
Assume.assumeFalse(AccessibilitySettings.isAnyHardKeyboardsExist());
List<String> nonIndexableKeys = new ArrayList<>();
mController.updateNonIndexableKeys(nonIndexableKeys);
assertThat(nonIndexableKeys).contains(mController.getPreferenceKey());
}
}

View File

@@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.preference.PreferenceManager;
@@ -34,19 +36,24 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class KeyboardSlowKeyPreferenceControllerTest {
private static final String KEY_ACCESSIBILITY_SLOW_KEYS =
Settings.Secure.ACCESSIBILITY_SLOW_KEYS;
private static final int UNKNOWN = -1;
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
private final KeyboardSlowKeyPreferenceController mController =
@@ -131,4 +138,26 @@ public class KeyboardSlowKeyPreferenceControllerTest {
mContext.getContentResolver(), KEY_ACCESSIBILITY_SLOW_KEYS, UNKNOWN)).isNotEqualTo(
OFF);
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void updateNonIndexableKeys_physicalKeyboardExists_returnEmptyList() {
Assume.assumeTrue(AccessibilitySettings.isAnyHardKeyboardsExist());
List<String> nonIndexableKeys = new ArrayList<>();
mController.updateNonIndexableKeys(nonIndexableKeys);
assertThat(nonIndexableKeys).isEmpty();
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void updateNonIndexableKeys_noPhysicalKeyboard_returnPreKey() {
Assume.assumeFalse(AccessibilitySettings.isAnyHardKeyboardsExist());
List<String> nonIndexableKeys = new ArrayList<>();
mController.updateNonIndexableKeys(nonIndexableKeys);
assertThat(nonIndexableKeys).contains(mController.getPreferenceKey());
}
}

View File

@@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.preference.PreferenceManager;
@@ -34,19 +36,24 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class KeyboardStickyKeyPreferenceControllerTest {
private static final String KEY_ACCESSIBILITY_STICKY_KEYS =
Settings.Secure.ACCESSIBILITY_STICKY_KEYS;
private static final int UNKNOWN = -1;
@Rule
public final SetFlagsRule mSetFlagRule = new SetFlagsRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private final SwitchPreference mSwitchPreference = spy(new SwitchPreference(mContext));
private final KeyboardStickyKeyPreferenceController mController =
@@ -129,4 +136,26 @@ public class KeyboardStickyKeyPreferenceControllerTest {
assertThat(Settings.Secure.getInt(
mContext.getContentResolver(), KEY_ACCESSIBILITY_STICKY_KEYS, UNKNOWN)).isEqualTo(ON);
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void updateNonIndexableKeys_physicalKeyboardExists_returnEmptyList() {
Assume.assumeTrue(AccessibilitySettings.isAnyHardKeyboardsExist());
List<String> nonIndexableKeys = new ArrayList<>();
mController.updateNonIndexableKeys(nonIndexableKeys);
assertThat(nonIndexableKeys).isEmpty();
}
@Test
@EnableFlags(Flags.FLAG_FIX_A11Y_SETTINGS_SEARCH)
public void updateNonIndexableKeys_noPhysicalKeyboard_returnPreKey() {
Assume.assumeFalse(AccessibilitySettings.isAnyHardKeyboardsExist());
List<String> nonIndexableKeys = new ArrayList<>();
mController.updateNonIndexableKeys(nonIndexableKeys);
assertThat(nonIndexableKeys).contains(mController.getPreferenceKey());
}
}

View File

@@ -33,7 +33,6 @@ import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.os.vibrator.Flags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -41,7 +40,6 @@ import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -87,30 +85,22 @@ public class KeyboardVibrationTogglePreferenceControllerTest {
@Test
public void getAvailabilityStatus_featureSupported_available() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(true);
when(mResources.getFloat(
com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude))
.thenReturn(0.8f);
when(mResources.getBoolean(
com.android.internal.R.bool.config_keyboardVibrationSettingsSupported))
.thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_featureNotSupported_unavailable() {
mSetFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(false);
when(mResources.getBoolean(
com.android.internal.R.bool.config_keyboardVibrationSettingsSupported))
.thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_keyboardCategoryDisabled_unavailable() {
mSetFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_CATEGORY_ENABLED);
when(mResources.getBoolean(R.bool.config_keyboard_vibration_supported)).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void updateState_mainVibrateDisabled_shouldReturnFalseForCheckedAndEnabled() {

View File

@@ -133,23 +133,23 @@ public class MagnificationOneFingerPanningPreferenceControllerTest {
}
@Test
public void getSummary_switchModeAndSettingsOff_disabledSummaryTextUsed() {
public void getSummary_switchModeAndSettingsOff_defaultSummaryTextUsed() {
MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, OFF);
mController.updateState(mSwitchPreference);
assertThat(mController.getSummary()).isEqualTo(disabledSummary());
assertThat(mController.getSummary().toString()).isEqualTo(defaultSummary());
}
@Test
public void getSummary_switchModeAndSettingsOn_enabledSummaryTextUsed() {
public void getSummary_switchModeAndSettingsOn_defaultSummaryTextUsed() {
MagnificationCapabilities.setCapabilities(mContext, MagnificationMode.ALL);
Settings.Secure.putInt(mContext.getContentResolver(), ONE_FINGER_PANNING_KEY, ON);
mController.updateState(mSwitchPreference);
assertThat(mController.getSummary()).isEqualTo(enabledSummary());
assertThat(mController.getSummary().toString()).isEqualTo(defaultSummary());
}
@Test
@@ -158,7 +158,7 @@ public class MagnificationOneFingerPanningPreferenceControllerTest {
mController.updateState(mSwitchPreference);
assertThat(mController.getSummary()).isEqualTo(unavailableSummary());
assertThat(mController.getSummary().toString()).isEqualTo(unavailableSummary());
}
@Test
@@ -199,14 +199,9 @@ public class MagnificationOneFingerPanningPreferenceControllerTest {
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
private String enabledSummary() {
private String defaultSummary() {
return mContext.getString(
R.string.accessibility_magnification_one_finger_panning_summary_on);
}
private String disabledSummary() {
return mContext.getString(
R.string.accessibility_magnification_one_finger_panning_summary_off);
R.string.accessibility_magnification_one_finger_panning_summary);
}
private String unavailableSummary() {

View File

@@ -31,9 +31,8 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
@@ -83,8 +82,6 @@ public class RestrictedPreferenceHelperTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Test
public void createAccessibilityServicePreferenceList_hasOneInfo_containsSameKey() {
@@ -100,7 +97,7 @@ public class RestrictedPreferenceHelperTest {
}
@Test
@RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
@EnableFlags(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
public void createAccessibilityServicePreferenceList_ecmRestricted_prefIsEcmRestricted() {
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(
@@ -116,7 +113,7 @@ public class RestrictedPreferenceHelperTest {
}
@Test
@RequiresFlagsEnabled(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
@EnableFlags(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
public void createAccessibilityServicePreferenceList_ecmNotRestricted_prefIsNotEcmRestricted() {
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs();
@@ -144,6 +141,40 @@ public class RestrictedPreferenceHelperTest {
assertThat(preference.getKey()).isEqualTo(key);
}
@Test
@EnableFlags(value = {android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED})
@DisableFlags(Flags.FLAG_NEVER_RESTRICT_ACCESSIBILITY_ACTIVITY)
public void createAccessibilityActivityPreference_ecmRestricted_prefIsEcmRestricted() {
setMockAccessibilityShortcutInfo(mShortcutInfo);
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(PACKAGE_NAME);
final List<AccessibilityActivityPreference> preferenceList =
mHelper.createAccessibilityActivityPreferenceList(List.of(mShortcutInfo));
assertThat(preferenceList).hasSize(1);
final RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.isDisabledByEcm()).isTrue();
}
@Test
@EnableFlags(value = {
android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS,
android.permission.flags.Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED,
Flags.FLAG_NEVER_RESTRICT_ACCESSIBILITY_ACTIVITY,
})
public void createAccessibilityActivityPreference_ecmRestricted_prefIsNotEcmRestricted() {
setMockAccessibilityShortcutInfo(mShortcutInfo);
ShadowRestrictedLockUtilsInternal.setEcmRestrictedPkgs(PACKAGE_NAME);
final List<AccessibilityActivityPreference> preferenceList =
mHelper.createAccessibilityActivityPreferenceList(List.of(mShortcutInfo));
assertThat(preferenceList).hasSize(1);
final RestrictedPreference preference = preferenceList.get(0);
assertThat(preference.isDisabledByEcm()).isFalse();
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
String className) {
final ApplicationInfo applicationInfo = new ApplicationInfo();

View File

@@ -37,10 +37,13 @@ import static org.robolectric.Shadows.shadowOf;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.provider.Settings;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.testing.FragmentScenario;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.FakeTimer;
@@ -56,23 +59,26 @@ import org.robolectric.util.ReflectionHelpers;
import java.util.List;
import java.util.Timer;
import java.util.function.Consumer;
@RunWith(RobolectricTestRunner.class)
public class ScreenFlashNotificationColorDialogFragmentTest {
private static final int DEFAULT_COLOR = ROSE.mColorInt;
private FragmentScenario<TestScreenFlashNotificationColorDialogFragment> mFragmentScenario;
private ScreenFlashNotificationColorDialogFragment mDialogFragment;
private AlertDialog mAlertDialog;
private ColorSelectorLayout mColorSelectorLayout;
private int mCurrentColor;
@Before
public void setUp() {
mCurrentColor = ROSE.mColorInt;
Settings.System.putInt(ApplicationProvider.getApplicationContext().getContentResolver(),
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, DEFAULT_COLOR);
Bundle fragmentArgs = new Bundle();
fragmentArgs.putInt(ScreenFlashNotificationColorDialogFragment.EXTRA_COLOR, DEFAULT_COLOR);
mFragmentScenario = FragmentScenario.launch(
TestScreenFlashNotificationColorDialogFragment.class,
/* fragmentArgs= */ null,
fragmentArgs,
R.style.Theme_AlertDialog_SettingsLib,
Lifecycle.State.INITIALIZED);
setupFragment();
@@ -99,7 +105,7 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
performClickOnDialog(BUTTON_NEUTRAL);
getTimerFromFragment().runOneTask();
assertStartPreview(ROSE.mColorInt);
assertStartPreview(DEFAULT_COLOR);
}
@Test
@@ -168,20 +174,26 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
}
@Test
public void clickColorAndClickNegative_assertColor() {
public void clickColorAndClickNegative_assertDefaultColor() {
checkColorButton(AZURE);
performClickOnDialog(BUTTON_NEGATIVE);
assertThat(getTimerFromFragment()).isNull();
assertThat(mCurrentColor).isEqualTo(ROSE.mColorInt);
assertThat(Settings.System.getInt(
ApplicationProvider.getApplicationContext().getContentResolver(),
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, AZURE.mColorInt)).isEqualTo(
DEFAULT_COLOR);
}
@Test
public void clickColorAndClickPositive_assertColor() {
public void clickColorAndClickPositive_assertSameColor() {
checkColorButton(BLUE);
performClickOnDialog(BUTTON_POSITIVE);
assertThat(mCurrentColor).isEqualTo(BLUE.mColorInt);
assertThat(Settings.System.getInt(
ApplicationProvider.getApplicationContext().getContentResolver(),
Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, DEFAULT_COLOR)).isEqualTo(
BLUE.mColorInt);
}
private void checkColorButton(ScreenFlashNotificationColor color) {
@@ -201,11 +213,6 @@ public class ScreenFlashNotificationColorDialogFragmentTest {
}
private void setupFragment() {
mFragmentScenario.onFragment(fragment -> {
ReflectionHelpers.setField(fragment, "mCurrentColor", mCurrentColor);
ReflectionHelpers.setField(fragment, "mConsumer",
(Consumer<Integer>) selectedColor -> mCurrentColor = selectedColor);
});
mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
mFragmentScenario.onFragment(fragment -> {

View File

@@ -57,8 +57,6 @@ import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.function.Consumer;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ScreenFlashNotificationPreferenceControllerTest
@@ -83,7 +81,6 @@ public class ScreenFlashNotificationPreferenceControllerTest {
private FragmentManager mFragmentManager;
@Mock
private ScreenFlashNotificationColorDialogFragment mDialogFragment;
private ScreenFlashNotificationPreferenceController mController;
private ContentResolver mContentResolver;
@@ -92,6 +89,7 @@ public class ScreenFlashNotificationPreferenceControllerTest {
MockitoAnnotations.initMocks(this);
FragmentActivity fragmentActivity = Robolectric.setupActivity(FragmentActivity.class);
Context context = fragmentActivity.getApplicationContext();
ShadowScreenFlashNotificationColorDialogFragment.setInstance(mDialogFragment);
ShadowFlashNotificationsUtils.setColorDescriptionText(COLOR_DESCRIPTION_TEXT);
@@ -99,8 +97,9 @@ public class ScreenFlashNotificationPreferenceControllerTest {
mController = new ScreenFlashNotificationPreferenceController(context, PREFERENCE_KEY);
when(mPreferenceScreen.findPreference(PREFERENCE_KEY)).thenReturn(mPreference);
when(mPreference.getKey()).thenReturn(PREFERENCE_KEY);
mController.setParentFragment(mParentFragment);
when(mParentFragment.getParentFragmentManager()).thenReturn(mFragmentManager);
mController.setParentFragment(mParentFragment);
}
@After
@@ -181,6 +180,7 @@ public class ScreenFlashNotificationPreferenceControllerTest {
@Test
public void handlePreferenceTreeClick() {
mController.handlePreferenceTreeClick(mPreference);
verify(mDialogFragment).show(any(FragmentManager.class), anyString());
}
@@ -194,7 +194,7 @@ public class ScreenFlashNotificationPreferenceControllerTest {
@Implementation
protected static ScreenFlashNotificationColorDialogFragment getInstance(
int initialColor, Consumer<Integer> colorConsumer) {
int initialColor) {
return sInstance;
}

View File

@@ -299,21 +299,6 @@ public class ToggleAccessibilityServicePreferenceFragmentTest {
}
@Test
@DisableFlags(
com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN)
public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_showDialog()
throws Throwable {
setupServiceWarningRequired(false);
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null);
mFragment.onSettingsClicked(mFragment.mShortcutPreference);
assertThat(mFragment.mLastShownDialogId).isEqualTo(
AccessibilityDialogUtils.DialogEnums.EDIT_SHORTCUT);
}
@Test
@EnableFlags(com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN)
public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_launchActivity()
throws Throwable {
setupServiceWarningRequired(false);

View File

@@ -18,53 +18,34 @@ package com.android.settings.accessibility;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.KEY_DEUTERANOMALY;
import static com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.KEY_GRAYSCALE;
import static com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.KEY_PROTANOMALY;
import static com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.KEY_TRITANOMEALY;
import static com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.KEY_USE_SERVICE_PREFERENCE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.Flags;
import android.widget.PopupWindow;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.fragment.app.Fragment;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.XmlTestUtils;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowApplication;
@@ -72,89 +53,52 @@ import java.util.List;
/** Tests for {@link ToggleDaltonizerPreferenceFragment} */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowFragment.class)
public class ToggleDaltonizerPreferenceFragmentTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private final Context mContext = ApplicationProvider.getApplicationContext();
private TestToggleDaltonizerPreferenceFragment mFragment;
private PreferenceScreen mScreen;
private SettingsMainSwitchPreference mSwitchPreference;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
@Mock
private SelectorWithWidgetPreference mMockDeuteranomalyPref;
@Mock
private SelectorWithWidgetPreference mMockProtanomalyPref;
@Mock
private SelectorWithWidgetPreference mMockTritanomalyPref;
@Mock
private SelectorWithWidgetPreference mMockGrayscalePref;
@Mock
private FragmentActivity mActivity;
private ActivityController<SettingsActivity> mActivityController;
@Before
public void setUpTestFragment() {
MockitoAnnotations.initMocks(this);
public void setUp() {
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
ToggleDaltonizerPreferenceFragment.class.getName());
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, new Bundle());
mFragment = spy(new TestToggleDaltonizerPreferenceFragment(mContext));
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getActivity()).thenReturn(mActivity);
when(mActivity.getContentResolver()).thenReturn(mContext.getContentResolver());
mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
when(mScreen.findPreference(KEY_USE_SERVICE_PREFERENCE))
.thenReturn(mFragment.mToggleServiceSwitchPreference);
when(mScreen.findPreference(KEY_DEUTERANOMALY)).thenReturn(mMockDeuteranomalyPref);
when(mMockDeuteranomalyPref.getKey()).thenReturn(KEY_DEUTERANOMALY);
when(mScreen.findPreference(KEY_PROTANOMALY)).thenReturn(mMockProtanomalyPref);
when(mMockProtanomalyPref.getKey()).thenReturn(KEY_PROTANOMALY);
when(mScreen.findPreference(KEY_TRITANOMEALY)).thenReturn(mMockTritanomalyPref);
when(mMockTritanomalyPref.getKey()).thenReturn(KEY_TRITANOMEALY);
when(mScreen.findPreference(KEY_GRAYSCALE)).thenReturn(mMockGrayscalePref);
when(mMockGrayscalePref.getKey()).thenReturn(KEY_GRAYSCALE);
when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
doReturn(mScreen).when(mFragment).getPreferenceScreen();
mSwitchPreference = mScreen.findPreference(KEY_USE_SERVICE_PREFERENCE);
mActivityController = ActivityController.of(new SettingsActivity(), intent);
}
@Test
public void onResume_colorCorrectEnabled_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, ON);
mFragment.onAttach(mContext);
mFragment.onCreate(Bundle.EMPTY);
mFragment.onResume();
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
assertThat(mSwitchPreference.isChecked()).isTrue();
SettingsMainSwitchPreference switchPreference = getMainFeatureToggle(fragment);
assertThat(switchPreference.isChecked()).isTrue();
}
@Test
public void onResume_colorCorrectDisabled_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, OFF);
mFragment.onAttach(mContext);
mFragment.onCreate(Bundle.EMPTY);
mFragment.onResume();
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
assertThat(mSwitchPreference.isChecked()).isFalse();
SettingsMainSwitchPreference switchPreference = getMainFeatureToggle(fragment);
assertThat(switchPreference.isChecked()).isFalse();
}
@Test
public void onResume_colorCorrectEnabled_switchPreferenceChecked_notShowTooltips() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, ON);
mSwitchPreference.setChecked(true);
mFragment.onAttach(mContext);
mFragment.onCreate(Bundle.EMPTY);
mFragment.onResume();
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
SettingsMainSwitchPreference switchPreference = getMainFeatureToggle(fragment);
assertThat(switchPreference.isChecked()).isTrue();
assertThat(getLatestPopupWindow()).isNull();
}
@@ -164,12 +108,10 @@ public class ToggleDaltonizerPreferenceFragmentTest {
public void onPreferenceToggled_colorCorrectDisabled_shouldReturnTrueAndShowTooltipView() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, OFF);
mSwitchPreference.setChecked(false);
mFragment.onAttach(mContext);
mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
SettingsMainSwitchPreference switchPreference = getMainFeatureToggle(fragment);
mFragment.onPreferenceToggled(mSwitchPreference.getKey(), true);
fragment.onPreferenceToggled(switchPreference.getKey(), true);
final boolean isEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, OFF) == ON;
@@ -182,11 +124,10 @@ public class ToggleDaltonizerPreferenceFragmentTest {
public void onPreferenceToggled_colorCorrectEnabled_shouldReturnFalseAndNotShowTooltipView() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, ON);
mSwitchPreference.setChecked(true);
mFragment.onAttach(mContext);
mFragment.onCreate(Bundle.EMPTY);
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
SettingsMainSwitchPreference switchPreference = getMainFeatureToggle(fragment);
mFragment.onPreferenceToggled(mSwitchPreference.getKey(), false);
fragment.onPreferenceToggled(switchPreference.getKey(), false);
final boolean isEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, OFF) == ON;
@@ -196,19 +137,25 @@ public class ToggleDaltonizerPreferenceFragmentTest {
@Test
public void getMetricsCategory_returnsCorrectCategory() {
assertThat(mFragment.getMetricsCategory()).isEqualTo(
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
assertThat(fragment.getMetricsCategory()).isEqualTo(
SettingsEnums.ACCESSIBILITY_TOGGLE_DALTONIZER);
}
@Test
public void getPreferenceScreenResId_returnsCorrectXml() {
assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
assertThat(fragment.getPreferenceScreenResId()).isEqualTo(
R.xml.accessibility_daltonizer_settings);
}
@Test
public void getHelpResource_returnsCorrectHelpResource() {
assertThat(mFragment.getHelpResource()).isEqualTo(R.string.help_url_color_correction);
ToggleDaltonizerPreferenceFragment fragment = getFragmentInResumedState();
assertThat(fragment.getHelpResource()).isEqualTo(R.string.help_url_color_correction);
}
@Test
@@ -228,55 +175,20 @@ public class ToggleDaltonizerPreferenceFragmentTest {
return shadowApplication.getLatestPopupWindow();
}
private static class TestToggleDaltonizerPreferenceFragment extends
ToggleDaltonizerPreferenceFragment {
private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example";
private static final String PLACEHOLDER_CLASS_NAME =
PLACEHOLDER_PACKAGE_NAME + ".placeholder";
private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName(
PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME);
private static final String PLACEHOLDER_TILE_TOOLTIP_CONTENT =
PLACEHOLDER_PACKAGE_NAME + "tooltip_content";
private ToggleDaltonizerPreferenceFragment getFragmentInResumedState() {
TestToggleDaltonizerPreferenceFragment(Context context) {
super();
mComponentName = PLACEHOLDER_COMPONENT_NAME;
final SettingsMainSwitchPreference switchPreference =
new SettingsMainSwitchPreference(context);
switchPreference.setKey(KEY_USE_SERVICE_PREFERENCE);
mToggleServiceSwitchPreference = switchPreference;
setArguments(new Bundle());
}
mActivityController.create().start().resume();
Fragment fragment = mActivityController.get().getSupportFragmentManager().findFragmentById(
R.id.main_content);
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// do nothing
}
assertThat(fragment).isNotNull();
assertThat(fragment).isInstanceOf(ToggleDaltonizerPreferenceFragment.class);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return mock(View.class);
}
return (ToggleDaltonizerPreferenceFragment) fragment;
}
@Override
protected void updateShortcutPreference() {
// UI related function, do nothing in tests
}
@Override
ComponentName getTileComponentName() {
return PLACEHOLDER_COMPONENT_NAME;
}
@Override
protected CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
return PLACEHOLDER_TILE_TOOLTIP_CONTENT;
}
@Override
public View getView() {
return mock(View.class);
}
private SettingsMainSwitchPreference getMainFeatureToggle(
ToggleDaltonizerPreferenceFragment fragment) {
return fragment.findPreference(KEY_USE_SERVICE_PREFERENCE);
}
}

View File

@@ -16,7 +16,9 @@
package com.android.settings.accessibility;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.google.common.truth.Truth.assertThat;
@@ -32,7 +34,6 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.icu.text.CaseMap;
@@ -44,10 +45,8 @@ import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.PopupWindow;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
@@ -55,9 +54,7 @@ import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.widget.TopIntroPreference;
@@ -76,7 +73,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowApplication;
import org.robolectric.shadows.ShadowLooper;
import java.util.Locale;
@@ -204,7 +200,7 @@ public class ToggleFeaturePreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.mComponentName.flattenToString());
// Compare to default UserShortcutType
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE);
assertThat(expectedType).isEqualTo(SOFTWARE);
}
@Test
@@ -219,104 +215,21 @@ public class ToggleFeaturePreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.mComponentName.flattenToString());
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
assertThat(expectedType).isEqualTo(SOFTWARE | HARDWARE);
}
@Test
public void updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable() {
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
final PreferredShortcut hardwareShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE);
PLACEHOLDER_COMPONENT_NAME.flattenToString(), HARDWARE);
putUserShortcutTypeIntoSharedPreference(mContext, hardwareShortcut);
mFragment.updateShortcutPreferenceData();
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.mComponentName.flattenToString());
assertThat(expectedType).isEqualTo(UserShortcutType.HARDWARE);
}
@Test
public void dialogCheckboxClicked_hardwareType_skipTimeoutRestriction() {
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
mFragment.mShortcutPreference = shortcutPreference;
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
mFragment::callOnAlertDialogCheckboxClicked);
mFragment.setupEditShortcutDialog(dialog);
final View dialogHardwareView = dialog.findViewById(R.id.hardware_shortcut);
final CheckBox hardwareTypeCheckBox = dialogHardwareView.findViewById(R.id.checkbox);
hardwareTypeCheckBox.setChecked(true);
dialog.getButton(DialogInterface.BUTTON_POSITIVE).callOnClick();
ShadowLooper.idleMainLooper();
final boolean skipTimeoutRestriction = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.SKIP_ACCESSIBILITY_SHORTCUT_DIALOG_TIMEOUT_RESTRICTION, 0) != 0;
assertThat(skipTimeoutRestriction).isTrue();
}
@Test
public void setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() {
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
mFragment.mShortcutPreference = shortcutPreference;
mFragment.mShortcutPreference.setChecked(false);
mFragment.setupEditShortcutDialog(dialog);
final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.EMPTY);
}
@Test
public void setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() {
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
final PreferredShortcut hardwareShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(), UserShortcutType.HARDWARE);
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
mFragment.mShortcutPreference = shortcutPreference;
PreferredShortcuts.saveUserShortcutType(mContext, hardwareShortcut);
mFragment.mShortcutPreference.setChecked(true);
mFragment.setupEditShortcutDialog(dialog);
final int checkboxValue = mFragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.HARDWARE);
}
@Test
@Config(shadows = ShadowFragment.class)
public void restoreValueFromSavedInstanceState_assignShortcutTypeToVariable() {
final AlertDialog dialog = AccessibilityDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final Bundle savedInstanceState = new Bundle();
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
mFragment.mShortcutPreference = shortcutPreference;
savedInstanceState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE,
UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
mFragment.onCreate(savedInstanceState);
mFragment.setupEditShortcutDialog(dialog);
final int value = mFragment.getShortcutTypeCheckBoxValue();
mFragment.saveNonEmptyUserShortcutType(value);
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
mFragment.mComponentName.flattenToString());
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE);
assertThat(expectedType).isEqualTo(HARDWARE);
}
@Test
@@ -470,7 +383,7 @@ public class ToggleFeaturePreferenceFragmentTest {
public void getShortcutTypeSummary_shortcutSummaryIsCorrectlySet() {
final PreferredShortcut userPreferredShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(),
UserShortcutType.HARDWARE | UserShortcutType.QUICK_SETTINGS);
HARDWARE | QUICK_SETTINGS);
putUserShortcutTypeIntoSharedPreference(mContext, userPreferredShortcut);
final ShortcutPreference shortcutPreference =
new ShortcutPreference(mContext, /* attrs= */ null);
@@ -502,9 +415,6 @@ public class ToggleFeaturePreferenceFragmentTest {
PreferredShortcut shortcut) {
PreferredShortcuts.saveUserShortcutType(context, shortcut);
}
private void callEmptyOnClicked(DialogInterface dialog, int which) {}
private static PopupWindow getLatestPopupWindow() {
final ShadowApplication shadowApplication =
Shadow.extract(ApplicationProvider.getApplicationContext());

View File

@@ -16,14 +16,18 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TRIPLETAP;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.TWOFINGER_DOUBLETAP;
import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import static com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -36,7 +40,6 @@ import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -50,17 +53,18 @@ import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import androidx.appcompat.app.AlertDialog;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.core.app.ApplicationProvider;
import com.android.server.accessibility.Flags;
import com.android.settings.DialogCreatable;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
import com.android.settings.testutils.AccessibilityTestUtils;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import com.android.settings.testutils.shadow.ShadowStorageManager;
@@ -393,12 +397,46 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
Settings.Secure.ACCESSIBILITY_QS_TARGETS))).hasSize(0);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
public void onResume_oneFingerPanningFlagOn_registerToSpecificUri() {
ShadowContentResolver shadowContentResolver = Shadows.shadowOf(
mContext.getContentResolver());
Uri observedUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED);
// verify no one finger panning settings observer registered before launching the fragment
assertThat(shadowContentResolver.getContentObservers(observedUri)).isEmpty();
mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
Collection<ContentObserver> observers =
shadowContentResolver.getContentObservers(observedUri);
assertThat(observers.size()).isEqualTo(1);
assertThat(observers.stream().findFirst().get()).isInstanceOf(
AccessibilitySettingsContentObserver.class);
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
public void onResume_oneFingerPanningFlagOff_notRegisterToSpecificUri() {
ShadowContentResolver shadowContentResolver = Shadows.shadowOf(
mContext.getContentResolver());
Uri observedUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SINGLE_FINGER_PANNING_ENABLED);
// verify no one finger panning settings observer registered before launching the fragment
assertThat(shadowContentResolver.getContentObservers(observedUri)).isEmpty();
mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
// verify no one finger panning settings observer registered after launching the fragment
assertThat(shadowContentResolver.getContentObservers(observedUri)).isEmpty();
}
@Test
public void hasValueInSettings_putValue_hasValue() {
setMagnificationTripleTapEnabled(/* enabled= */ true);
assertThat(ToggleScreenMagnificationPreferenceFragment.hasMagnificationValuesInSettings(
mContext, UserShortcutType.TRIPLETAP)).isTrue();
mContext, TRIPLETAP)).isTrue();
}
@Test
@@ -408,7 +446,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
mContext.getContentResolver(), TWO_FINGER_TRIPLE_TAP_SHORTCUT_KEY, ON);
assertThat(ToggleScreenMagnificationPreferenceFragment.hasMagnificationValuesInSettings(
mContext, UserShortcutType.TWOFINGER_DOUBLETAP)).isTrue();
mContext, TWOFINGER_DOUBLETAP)).isTrue();
}
@Test
@@ -418,13 +456,13 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
mContext.getContentResolver(), TWO_FINGER_TRIPLE_TAP_SHORTCUT_KEY, OFF);
assertThat(ToggleScreenMagnificationPreferenceFragment.hasMagnificationValuesInSettings(
mContext, UserShortcutType.TWOFINGER_DOUBLETAP)).isFalse();
mContext, TWOFINGER_DOUBLETAP)).isFalse();
}
@Test
@DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void optInAllValuesToSettings_optInValue_haveMatchString() {
int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.TRIPLETAP;
int shortcutTypes = SOFTWARE | TRIPLETAP;
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(mContext,
shortcutTypes);
@@ -438,24 +476,24 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
@EnableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void optInAllValuesToSettings_optInValue_callA11yManager() {
int shortcutTypes =
UserShortcutType.SOFTWARE | UserShortcutType.TRIPLETAP | UserShortcutType.HARDWARE
| UserShortcutType.QUICK_SETTINGS;
SOFTWARE | TRIPLETAP | HARDWARE
| QUICK_SETTINGS;
Set<String> shortcutTargets = Set.of(MAGNIFICATION_CONTROLLER_NAME);
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(mContext,
shortcutTypes);
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ true, UserShortcutType.SOFTWARE,
/* enable= */ true, SOFTWARE,
shortcutTargets, UserHandle.myUserId());
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ true, UserShortcutType.HARDWARE,
/* enable= */ true, HARDWARE,
shortcutTargets, UserHandle.myUserId());
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ true, UserShortcutType.QUICK_SETTINGS,
/* enable= */ true, QUICK_SETTINGS,
shortcutTargets, UserHandle.myUserId());
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ true, UserShortcutType.TRIPLETAP,
/* enable= */ true, TRIPLETAP,
shortcutTargets, UserHandle.myUserId());
verifyNoMoreInteractions(mAccessibilityManager);
}
@@ -464,7 +502,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
@DisableFlags(android.view.accessibility.Flags.FLAG_A11Y_QS_SHORTCUT)
public void optInAllValuesToSettings_twoFingerTripleTap_haveMatchString() {
int shortcutTypes = UserShortcutType.TWOFINGER_DOUBLETAP;
int shortcutTypes = TWOFINGER_DOUBLETAP;
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(mContext,
shortcutTypes);
@@ -479,7 +517,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString());
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(mContext,
UserShortcutType.SOFTWARE);
SOFTWARE);
assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
PLACEHOLDER_COMPONENT_NAME.flattenToString() + ":" + MAGNIFICATION_CONTROLLER_NAME);
@@ -491,7 +529,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
ShadowSettings.ShadowSecure.reset();
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(mContext,
UserShortcutType.SOFTWARE);
SOFTWARE);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
@@ -508,7 +546,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(
mContext,
UserShortcutType.SOFTWARE);
SOFTWARE);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
@@ -527,7 +565,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(
mContext,
UserShortcutType.HARDWARE);
HARDWARE);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, size + 1)).isEqualTo(
@@ -545,7 +583,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
ToggleScreenMagnificationPreferenceFragment.optInAllMagnificationValuesToSettings(
mContext,
UserShortcutType.TRIPLETAP);
TRIPLETAP);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, size + 1)).isEqualTo(
@@ -560,7 +598,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
putStringIntoSettings(HARDWARE_SHORTCUT_KEY, MAGNIFICATION_CONTROLLER_NAME);
setMagnificationTripleTapEnabled(/* enabled= */ true);
int shortcutTypes =
UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP;
SOFTWARE | HARDWARE | TRIPLETAP;
ToggleScreenMagnificationPreferenceFragment.optOutAllMagnificationValuesFromSettings(
mContext, shortcutTypes);
@@ -578,19 +616,19 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
putStringIntoSettings(HARDWARE_SHORTCUT_KEY, MAGNIFICATION_CONTROLLER_NAME);
setMagnificationTripleTapEnabled(/* enabled= */ true);
int shortcutTypes =
UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP;
SOFTWARE | HARDWARE | TRIPLETAP;
ToggleScreenMagnificationPreferenceFragment.optOutAllMagnificationValuesFromSettings(
mContext, shortcutTypes);
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ false, UserShortcutType.SOFTWARE,
/* enable= */ false, SOFTWARE,
shortcutTargets, UserHandle.myUserId());
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ false, UserShortcutType.HARDWARE,
/* enable= */ false, HARDWARE,
shortcutTargets, UserHandle.myUserId());
verify(mAccessibilityManager).enableShortcutsForTargets(
/* enable= */ false, UserShortcutType.TRIPLETAP,
/* enable= */ false, TRIPLETAP,
shortcutTargets, UserHandle.myUserId());
verifyNoMoreInteractions(mAccessibilityManager);
}
@@ -603,7 +641,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
TWO_FINGER_TRIPLE_TAP_SHORTCUT_KEY, ON);
ToggleScreenMagnificationPreferenceFragment.optOutAllMagnificationValuesFromSettings(
mContext, UserShortcutType.TWOFINGER_DOUBLETAP);
mContext, TWOFINGER_DOUBLETAP);
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
TWO_FINGER_TRIPLE_TAP_SHORTCUT_KEY, ON)).isEqualTo(OFF);
@@ -616,7 +654,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
PLACEHOLDER_COMPONENT_NAME.flattenToString() + ":" + MAGNIFICATION_CONTROLLER_NAME);
putStringIntoSettings(HARDWARE_SHORTCUT_KEY,
PLACEHOLDER_COMPONENT_NAME.flattenToString() + ":" + MAGNIFICATION_CONTROLLER_NAME);
int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE;
int shortcutTypes = SOFTWARE | HARDWARE;
ToggleScreenMagnificationPreferenceFragment.optOutAllMagnificationValuesFromSettings(
mContext, shortcutTypes);
@@ -636,7 +674,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
// Compare to default UserShortcutType
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE);
assertThat(expectedType).isEqualTo(SOFTWARE);
}
@Test
@@ -649,13 +687,13 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
assertThat(expectedType).isEqualTo(UserShortcutType.SOFTWARE | UserShortcutType.TRIPLETAP);
assertThat(expectedType).isEqualTo(SOFTWARE | TRIPLETAP);
}
@Test
public void updateShortcutPreferenceData_hasValueInSharedPreference_assignToVariable() {
final PreferredShortcut tripleTapShortcut = new PreferredShortcut(
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.TRIPLETAP);
MAGNIFICATION_CONTROLLER_NAME, TRIPLETAP);
putUserShortcutTypeIntoSharedPreference(mContext, tripleTapShortcut);
mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
@@ -663,7 +701,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
assertThat(expectedType).isEqualTo(UserShortcutType.TRIPLETAP);
assertThat(expectedType).isEqualTo(TRIPLETAP);
}
@Test
@@ -677,14 +715,14 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
assertThat(expectedType).isEqualTo(UserShortcutType.TWOFINGER_DOUBLETAP);
assertThat(expectedType).isEqualTo(TWOFINGER_DOUBLETAP);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void updateShortcutPreferenceData_hasTwoFingerTripleTapInSharedPref_assignToVariable() {
final PreferredShortcut tripleTapShortcut = new PreferredShortcut(
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.TWOFINGER_DOUBLETAP);
MAGNIFICATION_CONTROLLER_NAME, TWOFINGER_DOUBLETAP);
putUserShortcutTypeIntoSharedPreference(mContext, tripleTapShortcut);
mFragController.create(R.id.main_content, /* bundle= */ null).start().resume();
@@ -692,106 +730,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
assertThat(expectedType).isEqualTo(UserShortcutType.TWOFINGER_DOUBLETAP);
}
@Test
public void setupMagnificationEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(R.id.main_content, /* bundle= */
null).start().resume().get();
fragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);
fragment.mShortcutPreference.setChecked(false);
fragment.setupMagnificationEditShortcutDialog(
createEditShortcutDialog(fragment.getActivity()));
final int checkboxValue = fragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.EMPTY);
}
@Test
public void setupMagnificationEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(R.id.main_content, /* bundle= */
null).start().resume().get();
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
final PreferredShortcut tripletapShortcut = new PreferredShortcut(
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.TRIPLETAP);
fragment.mShortcutPreference = shortcutPreference;
PreferredShortcuts.saveUserShortcutType(mContext, tripletapShortcut);
fragment.mShortcutPreference.setChecked(true);
fragment.setupMagnificationEditShortcutDialog(
createEditShortcutDialog(fragment.getActivity()));
final int checkboxValue = fragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.TRIPLETAP);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void setupMagnificationEditShortcutDialog_twoFingerTripleTapOn_checkboxIsSavedValue() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(R.id.main_content, /* bundle= */
null).start().resume().get();
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
final PreferredShortcut twoFingerTripleTapShortcut = new PreferredShortcut(
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.TWOFINGER_DOUBLETAP);
fragment.mShortcutPreference = shortcutPreference;
PreferredShortcuts.saveUserShortcutType(mContext, twoFingerTripleTapShortcut);
fragment.mShortcutPreference.setChecked(true);
fragment.setupMagnificationEditShortcutDialog(
createEditShortcutDialog(fragment.getActivity()));
final int checkboxValue = fragment.getShortcutTypeCheckBoxValue();
assertThat(checkboxValue).isEqualTo(UserShortcutType.TWOFINGER_DOUBLETAP);
}
@Test
public void restoreValueFromSavedInstanceState_assignToVariable() {
final Bundle fragmentState = createFragmentSavedInstanceState(
UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP);
ToggleScreenMagnificationPreferenceFragment fragment = mFragController.get();
// Had to use reflection to pass the savedInstanceState when launching the fragment
ReflectionHelpers.setField(fragment, "mSavedFragmentState", fragmentState);
FragmentController.of(fragment, SettingsActivity.class).create(
R.id.main_content, /* bundle= */ null).start().resume().get();
fragment.setupMagnificationEditShortcutDialog(
createEditShortcutDialog(fragment.getActivity()));
final int value = fragment.getShortcutTypeCheckBoxValue();
fragment.saveNonEmptyUserShortcutType(value);
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
assertThat(value).isEqualTo(6);
assertThat(expectedType).isEqualTo(UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
public void restoreValueFromSavedInstanceState_twoFingerTripleTap_assignToVariable() {
final Bundle fragmentState =
createFragmentSavedInstanceState(UserShortcutType.TWOFINGER_DOUBLETAP);
ToggleScreenMagnificationPreferenceFragment fragment = mFragController.get();
// Had to use reflection to pass the savedInstanceState when launching the fragment
ReflectionHelpers.setField(fragment, "mSavedFragmentState", fragmentState);
FragmentController.of(fragment, SettingsActivity.class).create(
R.id.main_content, /* bundle= */ null).start().resume().get();
fragment.setupMagnificationEditShortcutDialog(
createEditShortcutDialog(fragment.getActivity()));
final int value = fragment.getShortcutTypeCheckBoxValue();
fragment.saveNonEmptyUserShortcutType(value);
final int expectedType = PreferredShortcuts.retrieveUserShortcutType(mContext,
MAGNIFICATION_CONTROLLER_NAME);
assertThat(value).isEqualTo(UserShortcutType.TWOFINGER_DOUBLETAP);
assertThat(expectedType).isEqualTo(UserShortcutType.TWOFINGER_DOUBLETAP);
assertThat(expectedType).isEqualTo(TWOFINGER_DOUBLETAP);
}
@Test
@@ -934,7 +873,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
}
@Test
public void onProcessArguments_defaultArgumentUnavailable_shouldSetDefaultArguments() {
@DisableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
public void
onProcessArguments_defaultArgumentUnavailableAndFlagOff_shouldSetDefaultArguments() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(
R.id.main_content, /* bundle= */ null).start().resume().get();
@@ -947,6 +888,32 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertTrue(arguments.containsKey(AccessibilitySettings.EXTRA_HTML_DESCRIPTION));
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_MAGNIFICATION_ONE_FINGER_PANNING_GESTURE)
public void
onProcessArguments_defaultArgumentUnavailableAndFlagOn_shouldSetDefaultArguments() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(
R.id.main_content, /* bundle= */ null).start().resume().get();
Bundle arguments = new Bundle();
fragment.onProcessArguments(arguments);
assertTrue(arguments.containsKey(AccessibilitySettings.EXTRA_PREFERENCE_KEY));
assertTrue(arguments.containsKey(AccessibilitySettings.EXTRA_INTRO));
// If OneFingerPanning flag is on, the EXTRA_HTML_DESCRIPTION should not be set. The html
// description would be decided dynamically based on the OneFingerPanning preference state.
assertFalse(arguments.containsKey(AccessibilitySettings.EXTRA_HTML_DESCRIPTION));
}
@Test
public void getCurrentHtmlDescription_shouldNotBeEmpty() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(
R.id.main_content, /* bundle= */ null).start().resume().get();
assertThat(fragment.getCurrentHtmlDescription().toString()).isNotEmpty();
}
@Test
public void getSummary_magnificationEnabled_returnShortcutOnWithSummary() {
setMagnificationTripleTapEnabled(true);
@@ -954,7 +921,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertThat(
ToggleScreenMagnificationPreferenceFragment.getServiceSummary(mContext).toString())
.isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string
.preference_summary_default_combination,
mContext.getText(R.string.accessibility_summary_shortcut_enabled),
mContext.getText(R.string.magnification_feature_summary)));
}
@@ -966,7 +935,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertThat(
ToggleScreenMagnificationPreferenceFragment.getServiceSummary(mContext).toString())
.isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string
.preference_summary_default_combination,
mContext.getText(
R.string.generic_accessibility_feature_shortcut_off),
mContext.getText(R.string.magnification_feature_summary)));
@@ -981,7 +952,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertThat(
ToggleScreenMagnificationPreferenceFragment.getServiceSummary(mContext).toString())
.isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string
.preference_summary_default_combination,
mContext.getText(R.string.accessibility_summary_shortcut_enabled),
mContext.getText(R.string.magnification_feature_summary)));
}
@@ -995,7 +968,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertThat(
ToggleScreenMagnificationPreferenceFragment.getServiceSummary(mContext).toString())
.isEqualTo(
mContext.getString(R.string.preference_summary_default_combination,
mContext.getString(
com.android.settingslib.R.string
.preference_summary_default_combination,
mContext.getText(
R.string.generic_accessibility_feature_shortcut_off),
mContext.getText(R.string.magnification_feature_summary)));
@@ -1006,7 +981,7 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
public void getShortcutTypeSummary_shortcutSummaryIsCorrectlySet() {
final PreferredShortcut userPreferredShortcut = new PreferredShortcut(
MAGNIFICATION_CONTROLLER_NAME,
UserShortcutType.HARDWARE | UserShortcutType.QUICK_SETTINGS);
HARDWARE | QUICK_SETTINGS);
putUserShortcutTypeIntoSharedPreference(mContext, userPreferredShortcut);
final ShortcutPreference shortcutPreference =
new ShortcutPreference(mContext, /* attrs= */ null);
@@ -1028,6 +1003,28 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
assertThat(summary).isEqualTo(expected);
}
@Test
@EnableFlags(
com.android.settings.accessibility.Flags.FLAG_TOGGLE_FEATURE_FRAGMENT_COLLECTION_INFO)
public void fragmentRecyclerView_getCollectionInfo_hasCorrectCounts() {
ToggleScreenMagnificationPreferenceFragment fragment =
mFragController.create(R.id.main_content, /* bundle= */
null).start().resume().get();
RecyclerView rv = fragment.getListView();
AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
rv.getCompatAccessibilityDelegate().onInitializeAccessibilityNodeInfo(rv, node);
AccessibilityNodeInfo.CollectionInfo collectionInfo = node.unwrap().getCollectionInfo();
// Asserting against specific item counts will be brittle to changes to the preferences
// included on this page, so instead just check some properties of these counts.
assertThat(collectionInfo.getColumnCount()).isEqualTo(1);
assertThat(collectionInfo.getRowCount()).isEqualTo(collectionInfo.getItemCount());
assertThat(collectionInfo.getItemCount())
// One unimportant item: the illustration preference
.isEqualTo(collectionInfo.getImportantForAccessibilityItemCount() + 1);
}
private void putStringIntoSettings(String key, String componentName) {
Settings.Secure.putString(mContext.getContentResolver(), key, componentName);
}
@@ -1091,9 +1088,6 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
== ON;
}
private void callEmptyOnClicked(DialogInterface dialog, int which) {
}
private void setWindowMagnificationSupported(boolean magnificationAreaSupported,
boolean windowMagnificationSupported) {
when(mSpyResources.getBoolean(
@@ -1103,24 +1097,6 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
windowMagnificationSupported);
}
private AlertDialog createEditShortcutDialog(Context context) {
context.setTheme(androidx.appcompat.R.style.Theme_AppCompat);
return AccessibilityDialogUtils.showEditShortcutDialog(
context,
DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
}
private Bundle createFragmentSavedInstanceState(int userShortcutType) {
final Bundle savedInstanceState = new Bundle();
savedInstanceState.putInt(KEY_SAVED_USER_SHORTCUT_TYPE, userShortcutType);
final Bundle fragmentState = new Bundle();
fragmentState.putBundle(
/* FragmentStateManager.SAVED_INSTANCE_STATE_KEY */ "savedInstanceState",
savedInstanceState);
return fragmentState;
}
/**
* A test fragment that provides a way to change the context
*/

View File

@@ -18,6 +18,7 @@ package com.android.settings.accessibility.shortcuts;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment.SHORTCUT_SETTINGS;
import static com.google.android.setupcompat.util.WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP;
@@ -36,6 +37,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -240,6 +242,7 @@ public class EditShortcutsPreferenceFragmentTest {
}
@Test
@DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void onSoftwareShortcutSettingChanged_softwareControllersUpdated() {
mFragmentScenario = createFragScenario(/* isInSuw= */ false, TARGET);
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
@@ -256,6 +259,27 @@ public class EditShortcutsPreferenceFragmentTest {
}
@Test
@EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void onSoftwareShortcutSettingsChanged_softwareControllersUpdated() {
mFragmentScenario = createFragScenario(/* isInSuw= */ false, TARGET);
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
ShortcutUtils.optInValueToSettings(
mContext, ShortcutConstants.UserShortcutType.SOFTWARE, TARGET);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
mFragmentScenario.onFragment(fragment -> {
TwoStatePreference preference = fragment.findPreference(
mContext.getString(R.string.accessibility_shortcut_fab_pref));
assertThat(preference.isChecked()).isTrue();
preference = fragment.findPreference(
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
assertThat(preference.isChecked()).isFalse();
});
}
@Test
@DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void onSoftwareShortcutModeChanged_softwareControllersUpdated() {
mFragmentScenario = createFragScenario(/* isInSuw= */ false, TARGET);
mFragmentScenario.moveToState(Lifecycle.State.CREATED);
@@ -309,6 +333,7 @@ public class EditShortcutsPreferenceFragmentTest {
}
@Test
@DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void fragmentResumed_enableTouchExploration_gestureShortcutOptionSummaryUpdated() {
String expectedSummary = StringUtil.getIcuPluralsString(mContext, 3,
R.string.accessibility_shortcut_edit_dialog_summary_gesture)
@@ -330,6 +355,26 @@ public class EditShortcutsPreferenceFragmentTest {
}
@Test
@EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void fragmentResumed_enableTouchExploration_gestureFlag_gestureSummaryUpdated() {
String expectedSummary = StringUtil.getIcuPluralsString(mContext, 3,
R.string.accessibility_shortcut_edit_dialog_summary_gesture);
mFragmentScenario = createFragScenario(/* isInSuw= */ false, TARGET);
mFragmentScenario.moveToState(Lifecycle.State.RESUMED);
ShadowAccessibilityManager am = shadowOf(
mContext.getSystemService(AccessibilityManager.class));
am.setTouchExplorationEnabled(true);
mFragmentScenario.onFragment(fragment -> {
Preference preference = fragment.findPreference(
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
assertThat(preference.getSummary().toString()).isEqualTo(expectedSummary);
});
}
@Test
@DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void fragmentPaused_enableTouchExploration_gestureShortcutOptionSummaryNotUpdated() {
String expectedSummary = StringUtil.getIcuPluralsString(mContext, 2,
R.string.accessibility_shortcut_edit_dialog_summary_gesture)
@@ -350,6 +395,25 @@ public class EditShortcutsPreferenceFragmentTest {
});
}
@Test
@EnableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void fragmentPaused_enableTouchExploration_gestureFlag_gestureSummaryNotUpdated() {
String expectedSummary = StringUtil.getIcuPluralsString(mContext, 2,
R.string.accessibility_shortcut_edit_dialog_summary_gesture);
mFragmentScenario = createFragScenario(/* isInSuw= */ false, TARGET);
mFragmentScenario.moveToState(Lifecycle.State.RESUMED).moveToState(Lifecycle.State.STARTED);
ShadowAccessibilityManager am = shadowOf(
mContext.getSystemService(AccessibilityManager.class));
am.setTouchExplorationEnabled(true);
mFragmentScenario.onFragment(fragment -> {
Preference preference = fragment.findPreference(
mContext.getString(R.string.accessibility_shortcut_gesture_pref));
assertThat(preference.getSummary().toString()).isEqualTo(expectedSummary);
});
}
@Test
@EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT)
public void fragmentResumed_enableTouchExploration_qsShortcutOptionSummaryUpdated() {
@@ -441,7 +505,7 @@ public class EditShortcutsPreferenceFragmentTest {
assertThat(
PreferredShortcuts.retrieveUserShortcutType(
mContext, TARGET)
).isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
).isEqualTo(SOFTWARE);
// Update the chosen shortcut type to Volume keys while the fragment is in the background
ShortcutUtils.optInValueToSettings(
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);
@@ -461,7 +525,7 @@ public class EditShortcutsPreferenceFragmentTest {
assertThat(
PreferredShortcuts.retrieveUserShortcutType(
mContext, TARGET)
).isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE);
).isEqualTo(SOFTWARE);
ShortcutUtils.optInValueToSettings(
mContext, ShortcutConstants.UserShortcutType.HARDWARE, TARGET);

View File

@@ -16,37 +16,46 @@
package com.android.settings.accessibility.shortcuts;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.ComponentName;
import android.content.Context;
import android.provider.Settings;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Flags;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.AccessibilityTestUtils;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.Set;
/**
* Tests for {@link FloatingButtonShortcutOptionController}
*/
@Config(shadows = SettingsShadowResources.class)
@RunWith(RobolectricTestRunner.class)
public class FloatingButtonShortcutOptionControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PREF_KEY = "prefKey";
private static final String TARGET =
new ComponentName("FakePackage", "FakeClass").flattenToString();
private final Context mContext = ApplicationProvider.getApplicationContext();
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
private FloatingButtonShortcutOptionController mController;
private ShortcutOptionPreference mShortcutOptionPreference;
@@ -61,7 +70,6 @@ public class FloatingButtonShortcutOptionControllerTest {
mShortcutOptionPreference.setKey(PREF_KEY);
mPreferenceScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
mPreferenceScreen.addPreference(mShortcutOptionPreference);
setFloatingButtonEnabled(true);
}
@Test
@@ -95,23 +103,26 @@ public class FloatingButtonShortcutOptionControllerTest {
@Test
public void isShortcutAvailable_floatingMenuEnabled_returnTrue() {
setFloatingButtonEnabled(true);
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ true);
assertThat(mController.isShortcutAvailable()).isTrue();
}
@Test
public void isShortcutAvailable_floatingMenuDisabled_returnFalse() {
setFloatingButtonEnabled(false);
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ false, /* floatingButtonEnabled= */ false);
assertThat(mController.isShortcutAvailable()).isFalse();
}
private void setFloatingButtonEnabled(boolean enable) {
int mode = enable
? ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU : ACCESSIBILITY_BUTTON_MODE_GESTURE;
@Test
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void isShortcutAvailable_gestureNavigationMode_returnsTrue() {
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ false);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mode);
assertThat(mController.isShortcutAvailable()).isTrue();
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.accessibility.shortcuts;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.settings.testutils.AccessibilityTestUtils.setupMockAccessibilityManager;
import static com.google.common.truth.Truth.assertThat;
@@ -25,6 +27,10 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Flags;
import android.view.accessibility.AccessibilityManager;
import androidx.preference.PreferenceManager;
@@ -37,6 +43,7 @@ import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settingslib.utils.StringUtil;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -50,6 +57,8 @@ import java.util.Set;
@Config(shadows = SettingsShadowResources.class)
@RunWith(RobolectricTestRunner.class)
public class GestureShortcutOptionControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PREF_KEY = "prefKey";
private static final String TARGET =
new ComponentName("FakePackage", "FakeClass").flattenToString();
@@ -110,6 +119,7 @@ public class GestureShortcutOptionControllerTest {
}
@Test
@DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void getSummary_touchExplorationEnabled_notInSuw_verifySummary() {
enableTouchExploration(true);
mController.setInSetupWizard(false);
@@ -124,6 +134,19 @@ public class GestureShortcutOptionControllerTest {
assertThat(mController.getSummary().toString()).isEqualTo(expected);
}
@Test
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void getSummary_touchExplorationEnabled_notInSuw_gestureFlag_verifySummary() {
enableTouchExploration(true);
mController.setInSetupWizard(false);
String expected = StringUtil.getIcuPluralsString(
mContext,
/* count= */ 3,
R.string.accessibility_shortcut_edit_dialog_summary_gesture);
assertThat(mController.getSummary().toString()).isEqualTo(expected);
}
@Test
public void getSummary_touchExplorationEnabled_inSuw_verifySummary() {
enableTouchExploration(true);
@@ -136,6 +159,18 @@ public class GestureShortcutOptionControllerTest {
assertThat(mController.getSummary().toString()).isEqualTo(expected);
}
@Test
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void getSummary_standaloneGestureFlagOn_verifyNoCustomizeA11yButtonTest() {
enableTouchExploration(true);
String expected = StringUtil.getIcuPluralsString(
mContext,
/* count= */ 3,
R.string.accessibility_shortcut_edit_dialog_summary_gesture);
assertThat(mController.getSummary().toString()).isEqualTo(expected);
}
@Test
public void isShortcutAvailable_inSuw_returnFalse() {
mController.setInSetupWizard(true);
@@ -144,6 +179,7 @@ public class GestureShortcutOptionControllerTest {
}
@Test
@DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void isShortcutAvailable_notInSuwUseGestureNavSystemUseFab_returnFalse() {
mController.setInSetupWizard(false);
AccessibilityTestUtils.setSoftwareShortcutMode(
@@ -179,6 +215,28 @@ public class GestureShortcutOptionControllerTest {
assertThat(mController.isShortcutAvailable()).isFalse();
}
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
@Test
public void isShortcutAvailable_floatingMenuEnabled_gestureNavEnabled_returnsTrue() {
mController.setInSetupWizard(false);
AccessibilityTestUtils.setSoftwareShortcutMode(
mContext, /* gestureNavEnabled= */ true, /* floatingButtonEnabled= */ true);
assertThat(mController.isShortcutAvailable()).isTrue();
}
@EnableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
@Test
public void getShortcutType_gesture() {
assertThat(mController.getShortcutType()).isEqualTo(GESTURE);
}
@DisableFlags(Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
@Test
public void getShortcutType_software() {
assertThat(mController.getShortcutType()).isEqualTo(SOFTWARE);
}
private void enableTouchExploration(boolean enable) {
AccessibilityManager am = setupMockAccessibilityManager(mContext);
when(am.isTouchExplorationEnabled()).thenReturn(enable);

View File

@@ -62,7 +62,7 @@ public class ShortcutOptionPreferenceTest {
@Test
public void bindViewHolder_imageResIdSet_shouldShowImageView() {
mShortcutOptionPreference.setIntroImageResId(
R.drawable.accessibility_shortcut_type_hardware);
R.drawable.accessibility_shortcut_type_volume_keys);
mShortcutOptionPreference.onBindViewHolder(mViewHolder);

View File

@@ -171,6 +171,32 @@ public class AppInfoWithHeaderTest {
assertThat(mAppInfoWithHeader.mAppEntry).isNotNull();
}
@Test
public void noCrossUserPermission_retrieveAppEntry_fail()
throws PackageManager.NameNotFoundException {
TestFragmentWithoutPermission testFragmentWithoutPermission =
new TestFragmentWithoutPermission();
final int userId = 1002;
final String packageName = "com.android.settings";
testFragmentWithoutPermission.mIntent.putExtra(Intent.EXTRA_USER_HANDLE,
new UserHandle(userId));
testFragmentWithoutPermission.mIntent.setData(Uri.fromParts("package",
packageName, null));
final ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class);
entry.info = new ApplicationInfo();
entry.info.packageName = packageName;
when(testFragmentWithoutPermission.mState.getEntry(packageName, userId)).thenReturn(entry);
when(testFragmentWithoutPermission.mPm.getPackageInfoAsUser(eq(entry.info.packageName),
any(), eq(userId))).thenReturn(
testFragmentWithoutPermission.mPackageInfo);
testFragmentWithoutPermission.retrieveAppEntry();
assertThat(testFragmentWithoutPermission.mAppEntry).isNull();
}
public static class TestFragment extends AppInfoWithHeader {
PreferenceManager mManager;
@@ -223,6 +249,11 @@ public class AppInfoWithHeaderTest {
return mShadowContext;
}
@Override
protected boolean hasInteractAcrossUsersFullPermission() {
return true;
}
@Override
protected void onPackageRemoved() {
mPackageRemovedCalled = true;
@@ -233,4 +264,11 @@ public class AppInfoWithHeaderTest {
return mIntent;
}
}
private static final class TestFragmentWithoutPermission extends TestFragment {
@Override
protected boolean hasInteractAcrossUsersFullPermission() {
return false;
}
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.applications;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.view.View;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class ProcessStatsPreferenceTest {
private Context mContext;
private View mRootView;
private ProcessStatsPreference mPref;
private PreferenceViewHolder mHolder;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mRootView = View.inflate(mContext, R.layout.preference_process_stats, null /* parent */);
mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mPref = new ProcessStatsPreference(mContext);
}
@Test
public void setProgress_showProgress() {
mPref.setProgress(1);
mPref.onBindViewHolder(mHolder);
assertThat(mHolder.findViewById(android.R.id.progress).getVisibility())
.isEqualTo(View.VISIBLE);
}
@Test
public void foobar_testName() {
float iconSize = mContext.getResources().getDimension(
com.android.settingslib.widget.theme.R.dimen.secondary_app_icon_size);
assertThat(Float.floatToIntBits(iconSize)).isEqualTo(Float.floatToIntBits(32));
}
}

View File

@@ -39,23 +39,26 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.Application;
import android.app.IActivityManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import androidx.fragment.app.testing.EmptyFragmentActivity;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowActivityManager;
import com.android.settings.testutils.shadow.ShadowFragment;
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.InOrder;
@@ -71,14 +74,14 @@ import org.robolectric.annotation.Config;
@Config(shadows = {ShadowActivityManager.class, ShadowFragment.class})
public class UserAspectRatioDetailsTest {
@Rule
public ActivityScenarioRule<EmptyFragmentActivity> rule =
new ActivityScenarioRule<>(EmptyFragmentActivity.class);
@Mock
private UserAspectRatioManager mUserAspectRatioManager;
@Mock
private IActivityManager mAm;
@Mock
private PackageManager mPackageManager;
@Mock
private SettingsActivity mSettingsActivity;
private RadioWithImagePreference mRadioButtonPref;
private Context mContext;
@@ -93,6 +96,12 @@ public class UserAspectRatioDetailsTest {
mFragment = spy(new UserAspectRatioDetails());
when(mFragment.getContext()).thenReturn(mContext);
when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager);
when(mFragment.getActivity()).thenReturn(mSettingsActivity);
when(mSettingsActivity.getApplication()).thenReturn((Application) mContext);
when(mSettingsActivity.getInitialCallingPackage()).thenReturn("test.package");
when(mSettingsActivity.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.checkPermission(eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL),
any())).thenReturn(PackageManager.PERMISSION_GRANTED);
when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt()))
.thenReturn(false);
ShadowActivityManager.setService(mAm);
@@ -111,8 +120,10 @@ public class UserAspectRatioDetailsTest {
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_FULLSCREEN);
doReturn(2).when(mUserAspectRatioManager)
.getUserMinAspectRatioOrder(USER_MIN_ASPECT_RATIO_UNSET);
rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity());
final Bundle args = new Bundle();
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(0));
args.putParcelable("intent", intent);
args.putString(ARG_PACKAGE_NAME, anyString());
mFragment.setArguments(args);
mFragment.onCreate(Bundle.EMPTY);
@@ -196,8 +207,10 @@ public class UserAspectRatioDetailsTest {
doReturn(true).when(mUserAspectRatioManager)
.hasAspectRatioOption(anyInt(), anyString());
rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity());
final Bundle args = new Bundle();
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_USER_HANDLE, new UserHandle(0));
args.putParcelable("intent", intent);
args.putString(ARG_PACKAGE_NAME, anyString());
mFragment.setArguments(args);
mFragment.onCreate(Bundle.EMPTY);

View File

@@ -24,19 +24,26 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
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.content.Context;
import android.content.Intent;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AndroidRuntimeException;
import android.view.LayoutInflater;
import android.view.View;
@@ -55,6 +62,7 @@ import com.android.settings.R;
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmDeviceCredentialActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
@@ -90,6 +98,8 @@ public class CombinedBiometricProfileSettingsTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Captor
private ArgumentCaptor<Preference> mPreferenceCaptor;
@Mock
@@ -102,6 +112,8 @@ public class CombinedBiometricProfileSettingsTest {
private FaceStatusPreferenceController mFaceStatusPreferenceController;
@Mock
private FaceManager mFaceManager;
@Mock
private BiometricManager mBiometricManager;
@Before
public void setUp() {
@@ -114,6 +126,10 @@ public class CombinedBiometricProfileSettingsTest {
mContext = spy(ApplicationProvider.getApplicationContext());
mFragment = spy(new TestCombinedBiometricProfileSettings(mContext));
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mBiometricManager).when(mActivity).getSystemService(BiometricManager.class);
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_MANDATORY_NOT_ACTIVE);
ReflectionHelpers.setField(mFragment, "mDashboardFeatureProvider",
FakeFeatureFactory.setupForTest().dashboardFeatureProvider);
@@ -160,6 +176,28 @@ public class CombinedBiometricProfileSettingsTest {
ShadowUtils.reset();
}
@Test
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testLaunchBiometricPrompt_onCreateFragment() {
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
doNothing().when(mFragment).startActivityForResult(any(), anyInt());
when(mBiometricManager.canAuthenticate(anyInt(),
eq(BiometricManager.Authenticators.MANDATORY_BIOMETRICS)))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
mFragment.onAttach(mContext);
mFragment.onCreate(null);
mFragment.onActivityResult(CONFIRM_REQUEST, RESULT_FINISHED,
new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(),
eq(BiometricsSettingsBase.BIOMETRIC_AUTH_REQUEST));
Intent intent = intentArgumentCaptor.getValue();
assertThat(intent.getComponent().getClassName()).isEqualTo(
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
}
@Test
public void testClickFingerprintUnlockWithValidGkPwHandle() {
doAnswer(invocation -> {

View File

@@ -0,0 +1,94 @@
/*
* 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.biometrics.fingerprint
import android.app.Activity
import android.content.Intent
import com.android.settings.overlay.FeatureFactory
import com.android.settings.testutils.FakeFeatureFactory
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when`
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows
@RunWith(RobolectricTestRunner::class)
class FingerprintEnrollTest {
private lateinit var featureFactory: FeatureFactory
private companion object {
const val INTENT_KEY = "testKey"
const val INTENT_VALUE = "testValue"
val INTENT = Intent().apply {
putExtra(INTENT_KEY, INTENT_VALUE)
}
}
private val activityProvider = FingerprintEnrollActivityClassProvider()
@Before
fun setUp() {
featureFactory = FakeFeatureFactory.setupForTest()
`when`(featureFactory.fingerprintFeatureProvider.enrollActivityClassProvider)
.thenReturn(activityProvider)
}
private fun setupActivity(activityClass: Class<out FingerprintEnroll>): FingerprintEnroll {
return Robolectric.buildActivity(activityClass, INTENT).create().get()
}
@Test
fun testFinishAndLaunchDefaultActivity() {
// Run
val activity = setupActivity(FingerprintEnroll::class.java)
// Verify
verifyLaunchNextActivity(activity, activityProvider.default)
}
@Test
fun testFinishAndLaunchSetupActivity() {
// Run
val activity = setupActivity(FingerprintEnroll.SetupActivity::class.java)
// Verify
verifyLaunchNextActivity(activity, activityProvider.setup)
}
@Test
fun testFinishAndLaunchInternalActivity() {
// Run
val activity = setupActivity(FingerprintEnroll.InternalActivity::class.java)
// Verify
verifyLaunchNextActivity(activity, activityProvider.internal)
}
private fun verifyLaunchNextActivity(
currentActivityInstance : FingerprintEnroll,
nextActivityClass: Class<out Activity>
) {
assertThat(currentActivityInstance.isFinishing).isTrue()
val nextActivityIntent = Shadows.shadowOf(currentActivityInstance).nextStartedActivity
assertThat(nextActivityIntent.component!!.className).isEqualTo(nextActivityClass.name)
assertThat(nextActivityIntent.extras!!.size()).isEqualTo(1)
assertThat(nextActivityIntent.getStringExtra(INTENT_KEY)).isEqualTo(INTENT_VALUE)
}
}

View File

@@ -19,6 +19,9 @@ package com.android.settings.biometrics.fingerprint;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST;
import static com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_FINISHED;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.CHOOSE_LOCK_GENERIC_REQUEST;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_REQUIRE_SCREEN_ON_TO_AUTH;
@@ -34,18 +37,28 @@ import static org.mockito.Mockito.doReturn;
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 static org.robolectric.Shadows.shadowOf;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.Flags;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Looper;
import android.os.UserHandle;
import android.os.Vibrator;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.ViewGroup;
@@ -53,9 +66,12 @@ import android.view.ViewGroup;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.password.ConfirmDeviceCredentialActivity;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
@@ -80,11 +96,17 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowSettingsPreferenceFragment.class, ShadowUtils.class, ShadowFragment.class,
ShadowUserManager.class, ShadowLockPatternUtils.class})
public class FingerprintSettingsFragmentTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final int PRIMARY_USER_ID = 0;
private static final int GUEST_USER_ID = 10;
@@ -92,12 +114,14 @@ public class FingerprintSettingsFragmentTest {
private Context mContext;
private FragmentActivity mActivity;
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock
private FingerprintManager mFingerprintManager;
@Mock
private FragmentTransaction mFragmentTransaction;
@Mock
private PackageManager mPackageManager;
@Mock
private BiometricManager mBiometricManager;
@Captor
private ArgumentCaptor<CancellationSignal> mCancellationSignalArgumentCaptor =
@@ -107,7 +131,11 @@ public class FingerprintSettingsFragmentTest {
mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass(
FingerprintManager.AuthenticationCallback.class);
@Mock
private Vibrator mVibrator;
private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar;
private FingerprintRemoveSidecar mFingerprintRemoveSidecar;
@Before
public void setUp() {
@@ -117,8 +145,12 @@ public class FingerprintSettingsFragmentTest {
mContext = spy(ApplicationProvider.getApplicationContext());
mFragment = spy(new FingerprintSettingsFragment());
doReturn(mContext).when(mFragment).getContext();
doReturn(mBiometricManager).when(mContext).getSystemService(BiometricManager.class);
doReturn(true).when(mFingerprintManager).isHardwareDetected();
doReturn(mVibrator).when(mContext).getSystemService(Vibrator.class);
when(mBiometricManager.canAuthenticate(PRIMARY_USER_ID,
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE);
}
@After
@@ -139,6 +171,28 @@ public class FingerprintSettingsFragmentTest {
false)).isTrue();
}
@Test
@Ignore("b/353706169")
@EnableFlags(Flags.FLAG_MANDATORY_BIOMETRICS)
public void testLaunchBiometricPromptForFingerprint() {
when(mBiometricManager.canAuthenticate(PRIMARY_USER_ID,
BiometricManager.Authenticators.MANDATORY_BIOMETRICS))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
doNothing().when(mFingerprintManager).generateChallenge(anyInt(), any());
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
setUpFragment(false);
ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
mFragment.onActivityResult(CONFIRM_REQUEST, RESULT_FINISHED,
new Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, 1L));
verify(mFragment).startActivityForResult(intentArgumentCaptor.capture(),
eq(BIOMETRIC_AUTH_REQUEST));
final Intent intent = intentArgumentCaptor.getValue();
assertThat(intent.getComponent().getClassName()).isEqualTo(
ConfirmDeviceCredentialActivity.InternalActivity.class.getName());
}
// Test the case when FingerprintAuthenticateSidecar receives an error callback from the
// framework or from another authentication client. The cancellation signal should not be set
// to null because there may exist a running authentication client.
@@ -182,7 +236,7 @@ public class FingerprintSettingsFragmentTest {
1,
UserHandle.of(GUEST_USER_ID).getIdentifier());
setUpFragment(false, GUEST_USER_ID, TYPE_POWER_BUTTON);
setUpFragment(false, GUEST_USER_ID, TYPE_POWER_BUTTON, 1);
final RestrictedSwitchPreference requireScreenOnToAuthPreference = mFragment.findPreference(
KEY_REQUIRE_SCREEN_ON_TO_AUTH);
@@ -190,11 +244,15 @@ public class FingerprintSettingsFragmentTest {
}
private void setUpFragment(boolean showChooseLock) {
setUpFragment(showChooseLock, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL);
setUpFragment(showChooseLock, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL, 1);
}
private void setUpFragment(boolean showChooseLock, int maxFingerprints) {
setUpFragment(showChooseLock, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL, maxFingerprints);
}
private void setUpFragment(boolean showChooseLock, int userId,
@FingerprintSensorProperties.SensorType int sensorType) {
@FingerprintSensorProperties.SensorType int sensorType, int maxFingerprints) {
ShadowUserManager.getShadow().addProfile(new UserInfo(userId, "", 0));
Intent intent = new Intent();
@@ -216,9 +274,13 @@ public class FingerprintSettingsFragmentTest {
doReturn(mFingerprintAuthenticateSidecar).when(fragmentManager).findFragmentByTag(
"authenticate_sidecar");
mFingerprintRemoveSidecar = new FingerprintRemoveSidecar();
doReturn(mFingerprintRemoveSidecar).when(fragmentManager).findFragmentByTag(
"removal_sidecar");
doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt());
setSensor(sensorType);
setSensor(sensorType, maxFingerprints);
// Start fragment
mFragment.onAttach(mContext);
@@ -235,12 +297,70 @@ public class FingerprintSettingsFragmentTest {
assertThat(mFragment.isVisible()).isTrue();
}
private void setSensor(@FingerprintSensorProperties.SensorType int sensorType) {
@Test
@Ignore("b/353726774")
public void fingerprintVibratesOnAuthSuccess() {
setUpFragment(false);
doNothing().when(mFingerprintManager).authenticate(any(),
mCancellationSignalArgumentCaptor.capture(),
mAuthenticationCallbackArgumentCaptor.capture(), any(), anyInt());
mFingerprintAuthenticateSidecar.startAuthentication(1);
assertThat(mAuthenticationCallbackArgumentCaptor.getValue()).isNotNull();
assertThat(mCancellationSignalArgumentCaptor.getValue()).isNotNull();
mAuthenticationCallbackArgumentCaptor.getValue()
.onAuthenticationSucceeded(new FingerprintManager.AuthenticationResult(null,
new Fingerprint("finger 1", 1, 1), 0 /* userId */, false));
shadowOf(Looper.getMainLooper()).idle();
verify(mVibrator).vibrate(FingerprintSettings.SUCCESS_VIBRATION_EFFECT);
}
@Test
public void testNotIndexable_whenDisabled() {
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(false)
.when(mPackageManager).hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
final BaseSearchIndexProvider provider = FingerprintSettingsFragment.SEARCH_INDEX_DATA_PROVIDER;
assertThat(provider.getDynamicRawDataToIndex(mContext, true)).isEmpty();
}
@Ignore("b/353726774")
@Test
public void testAddButtonWorksAfterRemovalError() {
final Fingerprint fingerprint = new Fingerprint("Test", 0, 0);
doReturn(List.of(fingerprint)).when(mFingerprintManager).getEnrolledFingerprints(anyInt());
setUpFragment(false, 5);
shadowOf(Looper.getMainLooper()).idle();
final Preference addPref = mFragment.findPreference("key_fingerprint_add");
final FingerprintSettings.FingerprintPreference fpPref =
mFragment.findPreference("key_fingerprint_item_0");
assertThat(fpPref).isNotNull();
assertThat(addPref).isNotNull();
assertThat(addPref.isEnabled()).isTrue();
mFingerprintRemoveSidecar.setListener(mFragment.mRemovalListener);
mFragment.deleteFingerPrint(fingerprint);
verify(mFingerprintManager).remove(any(), anyInt(), any());
assertThat(addPref.isEnabled()).isFalse();
mFingerprintRemoveSidecar.mRemoveCallback.onRemovalError(fingerprint, 0, "failure");
shadowOf(Looper.getMainLooper()).idle();
assertThat(addPref.isEnabled()).isTrue();
}
private void setSensor(@FingerprintSensorProperties.SensorType int sensorType,
int maxFingerprints) {
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
props.add(new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
1 /* maxEnrollmentsPerUser */,
maxFingerprints /* maxEnrollmentsPerUser */,
new ArrayList<ComponentInfoInternal>(),
sensorType,
true /* resetLockoutRequiresHardwareAuthToken */));

View File

@@ -28,16 +28,22 @@ import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.provider.DeviceConfig;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.preference.PreferenceFragmentCompat;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.flags.Flags;
import com.android.settings.fuelgauge.BatteryMeterView;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
@@ -48,12 +54,13 @@ import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.util.HashSet;
@@ -62,6 +69,8 @@ import java.util.Set;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class})
public class AdvancedBluetoothDetailsHeaderControllerTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final int BATTERY_LEVEL_MAIN = 30;
private static final int BATTERY_LEVEL_LEFT = 25;
private static final int BATTERY_LEVEL_RIGHT = 45;
@@ -86,6 +95,8 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
private CachedBluetoothDevice mCachedDevice;
@Mock
private BluetoothAdapter mBluetoothAdapter;
@Mock
private PreferenceFragmentCompat mFragment;
private AdvancedBluetoothDetailsHeaderController mController;
private LayoutPreference mLayoutPreference;
@@ -93,10 +104,10 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mContext = Robolectric.buildActivity(SettingsActivity.class).get();
mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key");
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
mController.init(mCachedDevice);
mController.init(mCachedDevice, mFragment);
mLayoutPreference = new LayoutPreference(mContext,
LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null));
mController.mLayoutPreference = mLayoutPreference;
@@ -272,6 +283,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
View.GONE);
assertThat(layout.findViewById(R.id.bt_battery_icon).getVisibility()).isEqualTo(View.GONE);
assertThat(layout.findViewById(R.id.header_icon).getVisibility()).isEqualTo(View.VISIBLE);
assertThat(layout.findViewById(R.id.battery_ring).getVisibility()).isEqualTo(View.GONE);
}
@Ignore
@@ -325,9 +337,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
}
@Test
public void getAvailabilityStatus_untetheredHeadsetWithConfigOn_returnAvailable() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
public void getAvailabilityStatus_untetheredHeadset_returnAvailable() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
@@ -336,31 +346,7 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
}
@Test
public void getAvailabilityStatus_untetheredHeadsetWithConfigOff_returnUnavailable() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_notUntetheredHeadsetWithConfigOn_returnUnavailable() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("false".getBytes());
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
}
@Test
public void getAvailabilityStatus_notUntetheredHeadsetWithConfigOff_returnUnavailable() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
public void getAvailabilityStatus_notUntetheredHeadset_returnUnavailable() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("false".getBytes());
@@ -379,8 +365,6 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
@Test
public void onStart_isAvailable_registerCallback() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
@@ -410,8 +394,6 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
@Test
public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
when(mCachedDevice.getDevice()).thenReturn(null);
@@ -438,8 +420,6 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
@Test
public void onStop_noBluetoothDevice_noNeedToUnregisterCallback() {
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
when(mCachedDevice.getDevice()).thenReturn(null);
@@ -532,6 +512,20 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
rightBatteryPrediction);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void enablePolishFlag_renameButtonShown() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
mController.onStart();
ImageButton button = mLayoutPreference.findViewById(R.id.rename_button);
assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
}
private void assertBatteryPredictionVisible(LinearLayout linearLayout, int visible) {
final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
assertThat(textView.getVisibility()).isEqualTo(visible);
@@ -546,6 +540,10 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary);
assertThat(textView.getText().toString()).isEqualTo(
com.android.settings.Utils.formatPercentage(batteryLevel));
if (Flags.enableBluetoothDeviceDetailsPolish()) {
final ProgressBar bar = linearLayout.findViewById(R.id.battery_ring);
assertThat(bar.getProgress()).isEqualTo(batteryLevel);
}
}
private void assertBatteryIcon(LinearLayout linearLayout, int resId) {

View File

@@ -26,7 +26,6 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.graphics.drawable.Drawable;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
@@ -127,24 +126,10 @@ public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsContro
}
@Test
public void isAvailable_untetheredHeadsetWithConfigOn_returnFalse() {
android.provider.DeviceConfig.setProperty(
android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
public void isAvailable_untetheredHeadset_returnFalse() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("true".getBytes());
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_untetheredHeadsetWithConfigOff_returnTrue() {
android.provider.DeviceConfig.setProperty(
android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("true".getBytes());
assertThat(mController.isAvailable()).isTrue();
}
}

View File

@@ -38,6 +38,7 @@ import android.bluetooth.BluetoothHapPresetInfo;
import androidx.preference.ListPreference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -215,11 +216,13 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
assertThat(mController.getPreference()).isNotNull();
assertThat(mController.getPreference().isEnabled()).isFalse();
assertThat(String.valueOf(mController.getPreference().getSummary())).isEqualTo(
mContext.getString(R.string.bluetooth_hearing_aids_presets_empty_list_message));
}
@Test
public void refresh_validPresetInfo_preferenceEnabled() {
BluetoothHapPresetInfo info = getTestPresetInfo();
BluetoothHapPresetInfo info = getTestPresetInfo(true);
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
mController.refresh();
@@ -230,7 +233,7 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
@Test
public void refresh_invalidActivePresetIndex_summaryIsNull() {
BluetoothHapPresetInfo info = getTestPresetInfo();
BluetoothHapPresetInfo info = getTestPresetInfo(true);
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(PRESET_INDEX_UNAVAILABLE);
@@ -242,7 +245,7 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
@Test
public void refresh_validActivePresetIndex_summaryIsNotNull() {
BluetoothHapPresetInfo info = getTestPresetInfo();
BluetoothHapPresetInfo info = getTestPresetInfo(true);
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
when(mHapClientProfile.getActivePresetIndex(mDevice)).thenReturn(TEST_PRESET_INDEX);
@@ -262,10 +265,30 @@ public class BluetoothDetailsHearingAidsPresetsControllerTest extends
verify(mHapClientProfile).selectPreset(mDevice, TEST_PRESET_INDEX);
}
private BluetoothHapPresetInfo getTestPresetInfo() {
@Test
public void loadAllPresetInfo_unavailablePreset_notAddedToEntries() {
BluetoothHapPresetInfo info = getTestPresetInfo(false);
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
mController.refresh();
assertThat(mController.getPreference().getEntries().length).isEqualTo(0);
}
@Test
public void loadAllPresetInfo_availablePreset_addedToEntries() {
BluetoothHapPresetInfo info = getTestPresetInfo(true);
when(mHapClientProfile.getAllPresetInfo(mDevice)).thenReturn(List.of(info));
mController.refresh();
assertThat(mController.getPreference().getEntries().length).isEqualTo(1);
}
private BluetoothHapPresetInfo getTestPresetInfo(boolean available) {
BluetoothHapPresetInfo info = mock(BluetoothHapPresetInfo.class);
when(info.getName()).thenReturn(TEST_PRESET_NAME);
when(info.getIndex()).thenReturn(TEST_PRESET_INDEX);
when(info.isAvailable()).thenReturn(available);
return info;
}

View File

@@ -44,6 +44,7 @@ import com.android.settings.testutils.shadow.ShadowBluetoothDevice;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
@@ -90,6 +91,13 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Mock
private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
@Mock
private A2dpProfile mA2dpProfile;
@Mock
private LeAudioProfile mLeAudioProfile;
@Mock
private HearingAidProfile mHearingAidProfile;
@Override
public void setUp() {
super.setUp();
@@ -103,18 +111,16 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
mConnectableProfiles = new ArrayList<>();
when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedBluetoothDeviceManager);
setUpMockProfiles();
when(mCachedBluetoothDeviceManager.getCachedDevicesCopy())
.thenReturn(ImmutableList.of(mCachedDevice));
when(mCachedDevice.getConnectableProfiles()).thenAnswer(invocation ->
new ArrayList<>(mConnectableProfiles)
);
when(mCachedDevice.getUiAccessibleProfiles())
.thenAnswer(invocation -> new ArrayList<>(mConnectableProfiles));
when(mCachedDevice.getProfiles())
.thenAnswer(invocation -> ImmutableList.of(mConnectableProfiles));
setupDevice(mDeviceConfig);
mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager,
mCachedDevice, mLifecycle);
mProfiles.setKey(mController.getPreferenceKey());
mController.mProfilesContainer = mProfiles;
mScreen.addPreference(mProfiles);
initController(List.of());
BluetoothProperties.le_audio_allow_list(Lists.newArrayList(LE_DEVICE_MODEL));
}
@@ -389,21 +395,46 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
assertThat(mDevice.getMessageAccessPermission()).isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
}
private A2dpProfile addMockA2dpProfile(boolean preferred, boolean supportsHighQualityAudio,
private void setUpMockProfiles() {
when(mA2dpProfile.toString()).thenReturn("A2DP");
when(mProfileManager.getProfileByName(eq(mA2dpProfile.toString())))
.thenReturn(mA2dpProfile);
when(mA2dpProfile.getNameResource(any()))
.thenReturn(R.string.bluetooth_profile_a2dp);
when(mA2dpProfile.getHighQualityAudioOptionLabel(any())).thenReturn(
mContext.getString(R.string.bluetooth_profile_a2dp_high_quality_unknown_codec));
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLeAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mLeAudioProfile.getNameResource(any()))
.thenReturn(R.string.bluetooth_profile_le_audio);
when(mLeAudioProfile.isProfileReady()).thenReturn(true);
when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mHearingAidProfile.toString()).thenReturn("HearingAid");
when(mHearingAidProfile.getNameResource(any()))
.thenReturn(R.string.bluetooth_profile_hearing_aid);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
}
private void addA2dpProfileToDevice(boolean preferred, boolean supportsHighQualityAudio,
boolean highQualityAudioEnabled) {
A2dpProfile profile = mock(A2dpProfile.class);
when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
when(profile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_a2dp);
when(profile.getHighQualityAudioOptionLabel(mDevice)).thenReturn(
mContext.getString(com.android.settingslib.R
.string.bluetooth_profile_a2dp_high_quality_unknown_codec));
when(profile.supportsHighQualityAudio(mDevice)).thenReturn(supportsHighQualityAudio);
when(profile.isHighQualityAudioEnabled(mDevice)).thenReturn(highQualityAudioEnabled);
when(profile.isEnabled(mDevice)).thenReturn(preferred);
when(profile.isProfileReady()).thenReturn(true);
mConnectableProfiles.add(profile);
return profile;
when(mA2dpProfile.supportsHighQualityAudio(any())).thenReturn(supportsHighQualityAudio);
when(mA2dpProfile.isHighQualityAudioEnabled(any())).thenReturn(highQualityAudioEnabled);
when(mA2dpProfile.isEnabled(any())).thenReturn(preferred);
mConnectableProfiles.add(mA2dpProfile);
}
private void addLeAudioProfileToDevice(boolean enabled) {
when(mLeAudioProfile.isEnabled(any())).thenReturn(enabled);
mConnectableProfiles.add(mLeAudioProfile);
}
private void addHearingAidProfileToDevice(boolean enabled) {
when(mHearingAidProfile.isEnabled(any())).thenReturn(enabled);
mConnectableProfiles.add(mHearingAidProfile);
}
private SwitchPreferenceCompat getHighQualityAudioPref() {
@@ -414,7 +445,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void highQualityAudio_prefIsPresentWhenSupported() {
setupDevice(makeDefaultDeviceConfig());
addMockA2dpProfile(true, true, true);
addA2dpProfileToDevice(true, true, true);
showScreen(mController);
SwitchPreferenceCompat pref = getHighQualityAudioPref();
assertThat(pref.getKey()).isEqualTo(
@@ -431,7 +462,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void highQualityAudio_prefIsAbsentWhenNotSupported() {
setupDevice(makeDefaultDeviceConfig());
addMockA2dpProfile(true, false, false);
addA2dpProfileToDevice(true, false, false);
showScreen(mController);
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
SwitchPreferenceCompat pref = (SwitchPreferenceCompat) mProfiles.getPreference(0);
@@ -444,7 +475,7 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void highQualityAudio_busyDeviceDisablesSwitch() {
setupDevice(makeDefaultDeviceConfig());
addMockA2dpProfile(true, true, true);
addA2dpProfileToDevice(true, true, true);
when(mCachedDevice.isBusy()).thenReturn(true);
showScreen(mController);
SwitchPreferenceCompat pref = getHighQualityAudioPref();
@@ -454,17 +485,17 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void highQualityAudio_mediaAudioDisabledAndReEnabled() {
setupDevice(makeDefaultDeviceConfig());
A2dpProfile audioProfile = addMockA2dpProfile(true, true, true);
addA2dpProfileToDevice(true, true, true);
showScreen(mController);
assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
// Disabling media audio should cause the high quality audio switch to disappear, but not
// the regular audio one.
SwitchPreferenceCompat audioPref =
(SwitchPreferenceCompat) mScreen.findPreference(audioProfile.toString());
(SwitchPreferenceCompat) mScreen.findPreference(mA2dpProfile.toString());
audioPref.performClick();
verify(audioProfile).setEnabled(mDevice, false);
when(audioProfile.isEnabled(mDevice)).thenReturn(false);
verify(mA2dpProfile).setEnabled(mDevice, false);
when(mA2dpProfile.isEnabled(mDevice)).thenReturn(false);
mController.onDeviceAttributesChanged();
assertThat(audioPref.isVisible()).isTrue();
SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
@@ -472,8 +503,8 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
// And re-enabling media audio should make high quality switch to reappear.
audioPref.performClick();
verify(audioProfile).setEnabled(mDevice, true);
when(audioProfile.isEnabled(mDevice)).thenReturn(true);
verify(mA2dpProfile).setEnabled(mDevice, true);
when(mA2dpProfile.isEnabled(mDevice)).thenReturn(true);
mController.onDeviceAttributesChanged();
highQualityAudioPref = getHighQualityAudioPref();
assertThat(highQualityAudioPref.isVisible()).isTrue();
@@ -482,9 +513,9 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void highQualityAudio_mediaAudioStartsDisabled() {
setupDevice(makeDefaultDeviceConfig());
A2dpProfile audioProfile = addMockA2dpProfile(false, true, true);
addA2dpProfileToDevice(false, true, true);
showScreen(mController);
SwitchPreferenceCompat audioPref = mScreen.findPreference(audioProfile.toString());
SwitchPreferenceCompat audioPref = mScreen.findPreference(mA2dpProfile.toString());
SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
assertThat(audioPref).isNotNull();
assertThat(audioPref.isChecked()).isFalse();
@@ -519,18 +550,12 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void prefKeyInBlockingList_hideToggle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
initController(List.of("A2DP"));
setupDevice(makeDefaultDeviceConfig());
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
when(leAudioProfile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
when(leAudioProfile.isProfileReady()).thenReturn(true);
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
addA2dpProfileToDevice(true, true, true);
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
.thenReturn(ImmutableSet.of("LE_AUDIO"));
mConnectableProfiles.add(leAudioProfile);
.thenReturn(ImmutableSet.of());
showScreen(mController);
@@ -540,18 +565,40 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
@Test
public void prefKeyNotInBlockingList_showToggle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
initController(List.of());
setupDevice(makeDefaultDeviceConfig());
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
when(leAudioProfile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
when(leAudioProfile.isProfileReady()).thenReturn(true);
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
addA2dpProfileToDevice(true, true, true);
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
.thenReturn(ImmutableSet.of());
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isTrue();
}
@Test
public void prefKeyInFeatureProviderBlockingList_hideToggle() {
setupDevice(makeDefaultDeviceConfig());
addA2dpProfileToDevice(true, true, true);
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
.thenReturn(ImmutableSet.of("A2DP"));
mConnectableProfiles.add(leAudioProfile);
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isFalse();
}
@Test
public void prefKeyNotInFeatureProviderBlockingList_showToggle() {
setupDevice(makeDefaultDeviceConfig());
addA2dpProfileToDevice(true, true, true);
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
.thenReturn(ImmutableSet.of("LE_AUDIO"));
showScreen(mController);
@@ -563,19 +610,8 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
public void classicAudioDeviceWithLeAudio_showLeAudioToggle() {
mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
setupDevice(makeDefaultDeviceConfig());
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
when(leAudioProfile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
when(leAudioProfile.isProfileReady()).thenReturn(true);
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
mConnectableProfiles.add(leAudioProfile);
when(mCachedDevice.getProfiles())
.thenAnswer(
invocation ->
ImmutableList.of(
leAudioProfile, addMockA2dpProfile(false, false, false)));
addLeAudioProfileToDevice(false);
addA2dpProfileToDevice(false, false, false);
showScreen(mController);
@@ -587,20 +623,43 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
public void leAudioOnlyDevice_hideLeAudioToggle() {
mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
setupDevice(makeDefaultDeviceConfig());
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
when(leAudioProfile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
when(leAudioProfile.isProfileReady()).thenReturn(true);
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
mConnectableProfiles.add(leAudioProfile);
when(mCachedDevice.getProfiles())
.thenAnswer(invocation -> ImmutableList.of(leAudioProfile));
addLeAudioProfileToDevice(false);
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isFalse();
}
@Test
public void ashaHearingAid_hideAshaToggle() {
setupDevice(makeDefaultDeviceConfig());
addHearingAidProfileToDevice(true);
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.isEmpty()).isTrue();
}
@Test
public void ashaHearingAidWithLeAudio_showLeAudioToggle() {
setupDevice(makeDefaultDeviceConfig());
addHearingAidProfileToDevice(false);
addLeAudioProfileToDevice(true);
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.getFirst().getTitle()).isEqualTo(
mContext.getString(mLeAudioProfile.getNameResource(mDevice)));
}
private void initController(List<String> invisibleProfiles) {
mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager,
mCachedDevice, mLifecycle, invisibleProfiles);
mProfiles.setKey(mController.getPreferenceKey());
mController.mProfilesContainer = mProfiles;
mScreen.removeAll();
mScreen.addPreference(mProfiles);
}
}

View File

@@ -38,6 +38,8 @@ import android.content.Intent;
import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.UserManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.FeatureFlagUtils;
import android.view.InputDevice;
import android.view.MenuInflater;
@@ -50,6 +52,9 @@ import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.bluetooth.ui.model.FragmentTypeModel;
import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
@@ -59,6 +64,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -79,6 +85,7 @@ import java.util.List;
com.android.settings.testutils.shadow.ShadowFragment.class,
})
public class BluetoothDeviceDetailsFragmentTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
@@ -101,6 +108,8 @@ public class BluetoothDeviceDetailsFragmentTest {
private InputManager mInputManager;
@Mock
private CompanionDeviceManager mCompanionDeviceManager;
@Mock
private DeviceDetailsFragmentFormatter mFormatter;
@Before
public void setUp() {
@@ -111,7 +120,12 @@ public class BluetoothDeviceDetailsFragmentTest {
.getSystemService(CompanionDeviceManager.class);
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(ImmutableList.of());
removeInputDeviceWithMatchingBluetoothAddress();
FakeFeatureFactory.setupForTest();
FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(),
any(), any(), eq(mCachedDevice))).thenReturn(mFormatter);
when(mFormatter.getVisiblePreferenceKeys(
FragmentTypeModel.DeviceDetailsMainFragment.INSTANCE))
.thenReturn(null);
mFragment = setupFragment();
mFragment.onAttach(mContext);
@@ -165,6 +179,7 @@ public class BluetoothDeviceDetailsFragmentTest {
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void getTitle_displayEditTitle() {
mFragment.onCreateOptionsMenu(mMenu, mInflater);
@@ -201,6 +216,7 @@ public class BluetoothDeviceDetailsFragmentTest {
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void editMenu_clicked_showDialog() {
mFragment.onCreateOptionsMenu(mMenu, mInflater);
final MenuItem item = mMenu.getItem(0);

View File

@@ -16,52 +16,80 @@
package com.android.settings.bluetooth;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_PAIR_AND_JOIN_SHARING;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
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 static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
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.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.concurrent.Executor;
/** Tests for {@link BluetoothDevicePairingDetailBase}. */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowBluetoothAdapter.class,
ShadowAlertDialogCompat.class,
com.android.settings.testutils.shadow.ShadowFragment.class,
})
public class BluetoothDevicePairingDetailBaseTest {
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
public static final String KEY_DEVICE_LIST_GROUP = "test_key";
@@ -86,8 +114,12 @@ public class BluetoothDevicePairingDetailBaseTest {
@Before
public void setUp() {
mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
@@ -155,8 +187,88 @@ public class BluetoothDevicePairingDetailBaseTest {
verify(mFragment).showBluetoothTurnedOnToast();
}
@Test
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingDisabled_finish() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
verify(mFragment).finish();
}
@Test
public void onDeviceBondStateChanged_bonded_pairAndJoinSharingEnabled_handle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
TextView message = dialog.findViewById(R.id.message);
assertThat(message).isNotNull();
// TODO: use stringr res once finalized
assertThat(message.getText().toString()).isEqualTo(
"Connecting to " + TEST_DEVICE_ADDRESS + "...");
verify(mFragment, never()).finish();
}
@Test
public void onDeviceBondStateChanged_bonding_pairAndJoinSharingDisabled_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(false);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(any(BluetoothDevice.class),
any(Executor.class), any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onDeviceBondStateChanged_bonding_pairAndJoinSharingEnabled_addListener() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
verify(mBluetoothAdapter).addOnMetadataChangedListener(eq(mBluetoothDevice),
any(Executor.class),
any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onDeviceBondStateChanged_unbonded_pairAndJoinSharingDisabled_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(
any(BluetoothDevice.class), any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onDeviceBondStateChanged_unbonded_pairAndJoinSharingEnabled_removeListener() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDING);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_NONE);
verify(mBluetoothAdapter).removeOnMetadataChangedListener(eq(mBluetoothDevice),
any(BluetoothAdapter.OnMetadataChangedListener.class));
}
@Test
public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.mSelectedList.add(device);
@@ -165,13 +277,43 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
verify(mFragment).finish();
}
@Test
public void
onProfileConnectionStateChanged_deviceInSelectedListAndConnected_pairAndJoinSharing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.mSelectedList.add(mBluetoothDevice);
setUpFragmentWithPairAndJoinSharingIntent(true);
mFragment.onDeviceBondStateChanged(mCachedBluetoothDevice, BluetoothDevice.BOND_BONDED);
shadowOf(Looper.getMainLooper()).idle();
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
shadowOf(Looper.getMainLooper()).idle();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mFragment.getActivity()).setResult(eq(Activity.RESULT_OK), captor.capture());
Intent intent = captor.getValue();
BluetoothDevice btDevice =
intent != null
? intent.getParcelableExtra(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE,
BluetoothDevice.class)
: null;
assertThat(btDevice).isNotNull();
assertThat(btDevice).isEqualTo(mBluetoothDevice);
verify(mFragment).finish();
}
@Test
public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(device);
@@ -179,13 +321,14 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
// not crash
}
@Test
public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
mFragment.mSelectedList.add(mBluetoothDevice);
mFragment.mSelectedList.add(device);
@@ -194,13 +337,14 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.A2DP);
// not crash
}
@Test
public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
true, BluetoothDevicePreference.SortType.TYPE_FIFO);
@@ -211,13 +355,14 @@ public class BluetoothDevicePairingDetailBaseTest {
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0);
}
@Test
public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
final BluetoothDevicePreference preference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
@@ -233,12 +378,26 @@ public class BluetoothDevicePairingDetailBaseTest {
when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
BluetoothAdapter.STATE_CONNECTED);
mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.A2DP);
// not crash
}
private void setUpFragmentWithPairAndJoinSharingIntent(boolean enablePairAndJoinSharing) {
Bundle args = new Bundle();
args.putBoolean(EXTRA_PAIR_AND_JOIN_SHARING, enablePairAndJoinSharing);
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class));
doReturn(intent).when(activity).getIntent();
doReturn(activity).when(mFragment).getActivity();
FragmentManager fragmentManager = mock(FragmentManager.class);
doReturn(fragmentManager).when(mFragment).getFragmentManager();
mFragment.mShouldTriggerAudioSharingShareThenPairFlow =
mFragment.shouldTriggerAudioSharingShareThenPairFlow();
}
private static class TestBluetoothDevicePairingDetailBase extends
BluetoothDevicePairingDetailBase {

View File

@@ -18,10 +18,10 @@ package com.android.settings.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
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;
@@ -32,22 +32,31 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
import android.util.Pair;
import android.view.ContextThemeWrapper;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
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.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@@ -57,18 +66,21 @@ import java.util.Comparator;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowAlertDialogCompat.class})
@Config(shadows = {ShadowAlertDialogCompat.class,
com.android.settings.testutils.shadow.ShadowBluetoothUtils.class})
public class BluetoothDevicePreferenceTest {
private static final boolean SHOW_DEVICES_WITHOUT_NAMES = true;
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String MAC_ADDRESS_2 = "05:52:C7:0B:D8:3C";
private static final String MAC_ADDRESS_3 = "06:52:C7:0B:D8:3C";
private static final String MAC_ADDRESS_4 = "07:52:C7:0B:D8:3C";
private static final String TEST_MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String TEST_MAC_ADDRESS_1 = "05:52:C7:0B:D8:3C";
private static final String TEST_MAC_ADDRESS_2 = "06:52:C7:0B:D8:3C";
private static final String TEST_MAC_ADDRESS_3 = "07:52:C7:0B:D8:3C";
private static final Comparator<BluetoothDevicePreference> COMPARATOR =
Comparator.naturalOrder();
private static final String FAKE_DESCRIPTION = "fake_description";
private static final int TEST_DEVICE_GROUP_ID = 1;
private Context mContext;
@Rule
public final MockitoRule mockito = MockitoJUnit.rule();
@Mock
private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock
@@ -89,35 +101,37 @@ public class BluetoothDevicePreferenceTest {
private Drawable mDrawable;
@Mock
private BluetoothAdapter mBluetoothAdapter;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
private Context mContext = ApplicationProvider.getApplicationContext();
private FakeFeatureFactory mFakeFeatureFactory;
private MetricsFeatureProvider mMetricsFeatureProvider;
private BluetoothDevicePreference mPreference;
private List<BluetoothDevicePreference> mPreferenceList = new ArrayList<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Context context = spy(RuntimeEnvironment.application.getApplicationContext());
mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
mContext.setTheme(R.style.Theme_Settings);
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
when(mCachedBluetoothDevice.getDrawableWithDescription())
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mCachedDevice1.getAddress()).thenReturn(MAC_ADDRESS_2);
when(mCachedDevice1.getDrawableWithDescription())
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1);
when(mCachedDevice2.getAddress()).thenReturn(MAC_ADDRESS_3);
when(mCachedDevice2.getDrawableWithDescription())
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2);
when(mCachedDevice3.getAddress()).thenReturn(MAC_ADDRESS_4);
when(mCachedDevice3.getDrawableWithDescription())
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
prepareCachedBluetoothDevice(mCachedBluetoothDevice, TEST_MAC_ADDRESS,
new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice);
prepareCachedBluetoothDevice(mCachedDevice1, TEST_MAC_ADDRESS_1,
new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice1);
prepareCachedBluetoothDevice(mCachedDevice2, TEST_MAC_ADDRESS_2,
new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice2);
prepareCachedBluetoothDevice(mCachedDevice3, TEST_MAC_ADDRESS_3,
new Pair<>(mDrawable, FAKE_DESCRIPTION), TEST_DEVICE_GROUP_ID, mBluetoothDevice3);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedBluetoothDevice));
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
mPreference.mBluetoothAdapter = mBluetoothAdapter;
@@ -301,7 +315,8 @@ public class BluetoothDevicePreferenceTest {
// callback is not removed.
mPreference.onAttached();
verify(mCachedBluetoothDevice, times(1)).registerCallback(any());
verify(mCachedBluetoothDevice, times(1)).registerCallback(eq(mContext.getMainExecutor()),
any());
verify(mBluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any());
}
@@ -313,7 +328,99 @@ public class BluetoothDevicePreferenceTest {
mPreference.onAttached();
verify(mCachedBluetoothDevice, times(1)).unregisterCallback(any());
verify(mCachedBluetoothDevice, times(2)).registerCallback(any());
verify(mCachedBluetoothDevice, times(2)).registerCallback(eq(mContext.getMainExecutor()),
any());
verify(mBluetoothAdapter, times(2)).addOnMetadataChangedListener(any(), any(), any());
}
@Test
public void onDeviceAttributesChanged_updatePreference() {
when(mCachedBluetoothDevice.getName()).thenReturn("Name");
mPreference.onAttached();
final String updatedName = "updatedName";
when(mCachedBluetoothDevice.getName()).thenReturn(updatedName);
getCachedBluetoothDeviceCallback().onDeviceAttributesChanged();
assertThat(mPreference.getTitle().toString()).isEqualTo(updatedName);
}
@Test
public void onAttached_memberDevicesAdded_registerAllCallback() {
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3));
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2,
mCachedDevice3));
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
mPreference.onAttached();
verify(mCachedBluetoothDevice).registerCallback(eq(mContext.getMainExecutor()), any());
verify(mCachedDevice1).registerCallback(eq(mContext.getMainExecutor()), any());
verify(mCachedDevice2).registerCallback(eq(mContext.getMainExecutor()), any());
verify(mCachedDevice3).registerCallback(eq(mContext.getMainExecutor()), any());
}
@Test
public void onDetached_memberDevicesAdded_unregisterAllCallback() {
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3));
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2,
mCachedDevice3));
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
mPreference.onAttached();
mPreference.onDetached();
verify(mCachedBluetoothDevice).unregisterCallback(any());
verify(mCachedDevice1).unregisterCallback(any());
verify(mCachedDevice2).unregisterCallback(any());
verify(mCachedDevice3).unregisterCallback(any());
}
@Test
public void onDeviceAttributesChanged_memberDevicesChanged_registerOnlyExistDeviceCallback() {
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
ImmutableSet.of(mCachedDevice1, mCachedDevice2, mCachedDevice3));
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2,
mCachedDevice3));
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
mPreference.onAttached();
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(
ImmutableSet.of(mCachedDevice1, mCachedDevice2));
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedBluetoothDevice, mCachedDevice1, mCachedDevice2));
getCachedBluetoothDeviceCallback().onDeviceAttributesChanged();
verify(mCachedBluetoothDevice, times(2)).registerCallback(eq(mContext.getMainExecutor()),
any());
verify(mCachedDevice1, times(2)).registerCallback(eq(mContext.getMainExecutor()), any());
verify(mCachedDevice2, times(2)).registerCallback(eq(mContext.getMainExecutor()), any());
verify(mCachedDevice3, times(1)).registerCallback(eq(mContext.getMainExecutor()), any());
}
private void prepareCachedBluetoothDevice(CachedBluetoothDevice cachedDevice, String address,
Pair<Drawable, String> drawableWithDescription, int groupId,
BluetoothDevice bluetoothDevice) {
when(cachedDevice.getAddress()).thenReturn(address);
when(cachedDevice.getDrawableWithDescription()).thenReturn(drawableWithDescription);
when(cachedDevice.getGroupId()).thenReturn(groupId);
when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
}
private CachedBluetoothDevice.Callback getCachedBluetoothDeviceCallback() {
ArgumentCaptor<CachedBluetoothDevice.Callback> callbackCaptor = ArgumentCaptor.forClass(
CachedBluetoothDevice.Callback.class);
verify(mCachedBluetoothDevice).registerCallback(eq(mContext.getMainExecutor()),
callbackCaptor.capture());
return callbackCaptor.getValue();
}
}

View File

@@ -275,7 +275,7 @@ public class BluetoothDeviceUpdaterTest {
}
@Override
protected String getPreferenceKey() {
protected String getPreferenceKeyPrefix() {
return "test_bt";
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothDevice;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowAlertDialogCompat.class)
public class BluetoothKeyMissingDialogTest {
@Mock private BluetoothDevice mBluetoothDevice;
private BluetoothKeyMissingDialogFragment mFragment = null;
private FragmentActivity mActivity = null;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Robolectric.setupActivity(FragmentActivity.class);
mFragment = new BluetoothKeyMissingDialogFragment(mBluetoothDevice);
mActivity
.getSupportFragmentManager()
.beginTransaction()
.add(mFragment, null)
.commit();
shadowMainLooper().idle();
}
@Test
public void clickForgetDevice_removeBond() {
mFragment.onClick(mFragment.getDialog(), AlertDialog.BUTTON_POSITIVE);
verify(mBluetoothDevice).removeBond();
assertThat(mActivity.isFinishing()).isTrue();
}
@Test
public void clickCancel_notRemoveBond() {
mFragment.onClick(mFragment.getDialog(), AlertDialog.BUTTON_NEGATIVE);
verify(mBluetoothDevice, never()).removeBond();
assertThat(mActivity.isFinishing()).isTrue();
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
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.verifyNoInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
import android.app.NotificationManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import org.junit.After;
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.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowApplication;
import java.util.List;
import java.util.stream.Collectors;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
public class BluetoothKeyMissingReceiverTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
private ShadowApplication mShadowApplication;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private NotificationManager mNm;
@Mock private BluetoothDevice mBluetoothDevice;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.getApplication());
mShadowApplication = Shadow.extract(mContext);
mShadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
}
@After
public void tearDown() {
ShadowBluetoothUtils.reset();
}
@Test
public void broadcastReceiver_isRegistered() {
List<ShadowApplication.Wrapper> registeredReceivers =
mShadowApplication.getRegisteredReceivers();
int matchedCount =
registeredReceivers.stream()
.filter(
receiver ->
BluetoothKeyMissingReceiver.class
.getSimpleName()
.equals(
receiver.broadcastReceiver
.getClass()
.getSimpleName()))
.collect(Collectors.toList())
.size();
assertThat(matchedCount).isEqualTo(1);
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG)
public void broadcastReceiver_receiveKeyMissingIntentFlagOff_doNothing() {
Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING));
when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice);
BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent);
bluetoothKeyMissingReceiver.onReceive(mContext, intent);
verifyNoInteractions(mNm);
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG)
public void broadcastReceiver_background_showNotification() {
Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING));
when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice);
BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent);
bluetoothKeyMissingReceiver.onReceive(mContext, intent);
verify(mNm).notify(eq(android.R.drawable.stat_sys_data_bluetooth), any(Notification.class));
verify(mContext, never()).startActivityAsUser(any(), any());
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_KEY_MISSING_DIALOG)
public void broadcastReceiver_foreground_receiveKeyMissingIntent_showDialog() {
when(mLocalBtManager.isForegroundActivity()).thenReturn(true);
Intent intent = spy(new Intent(BluetoothDevice.ACTION_KEY_MISSING));
when(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(mBluetoothDevice);
BluetoothKeyMissingReceiver bluetoothKeyMissingReceiver = getReceiver(intent);
bluetoothKeyMissingReceiver.onReceive(mContext, intent);
verifyNoInteractions(mNm);
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivityAsUser(captor.capture(), eq(UserHandle.CURRENT));
assertThat(captor.getValue().getComponent().getClassName())
.isEqualTo(BluetoothKeyMissingDialog.class.getName());
}
private BluetoothKeyMissingReceiver getReceiver(Intent intent) {
assertThat(mShadowApplication.hasReceiverForIntent(intent)).isTrue();
List<BroadcastReceiver> receiversForIntent =
mShadowApplication.getReceiversForIntent(intent);
assertThat(receiversForIntent).hasSize(1);
BroadcastReceiver broadcastReceiver = receiversForIntent.get(0);
assertThat(broadcastReceiver).isInstanceOf(BluetoothKeyMissingReceiver.class);
return (BluetoothKeyMissingReceiver) broadcastReceiver;
}
}

View File

@@ -30,7 +30,6 @@ import android.content.Context;
import android.content.Intent;
import android.os.Parcel;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
@@ -177,30 +176,11 @@ public class BluetoothPairingControllerTest {
assertThat(mBluetoothPairingController.isLeAudio()).isTrue();
}
@Test
public void isLeContactSharingEnabled_configIsFalse_returnsFalse() {
mockIsLeContactSharingEnabled(false);
mBluetoothPairingController = createBluetoothPairingController();
assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isFalse();
}
@Test
public void isLeContactSharingEnabled_configIsTrue_returnsTrue() {
mockIsLeContactSharingEnabled(true);
mBluetoothPairingController = createBluetoothPairingController();
assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isTrue();
}
@Test
public void isContactSharingVisible_profileIsNotReady_returnsTrue() {
// isProfileReady=false, isLeAudio=false, isLeContactSharingEnabled=true
// isProfileReady=false, isLeAudio=false
mockIsProfileReady(false);
mockIsLeAudio(false);
mockIsLeContactSharingEnabled(true);
mBluetoothPairingController = createBluetoothPairingController();
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
@@ -210,10 +190,9 @@ public class BluetoothPairingControllerTest {
@Test
public void isContactSharingVisible_profileIsReady_returnsFalse() {
// isProfileReady=true, isLeAudio=false, isLeContactSharingEnabled=true
// isProfileReady=true, isLeAudio=false
mockIsProfileReady(true);
mockIsLeAudio(false);
mockIsLeContactSharingEnabled(true);
mBluetoothPairingController = createBluetoothPairingController();
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
@@ -223,10 +202,9 @@ public class BluetoothPairingControllerTest {
@Test
public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsReady_returnsFalse() {
// isProfileReady=true, isLeAudio=true, isLeContactSharingEnabled=true
// isProfileReady=true, isLeAudio=true
mockIsProfileReady(true);
mockIsLeAudio(true);
mockIsLeContactSharingEnabled(true);
mBluetoothPairingController = createBluetoothPairingController();
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
@@ -236,10 +214,9 @@ public class BluetoothPairingControllerTest {
@Test
public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsNotReady_returnsTrue() {
// isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=true
// isProfileReady=false, isLeAudio=true
mockIsProfileReady(false);
mockIsLeAudio(true);
mockIsLeContactSharingEnabled(true);
mBluetoothPairingController = createBluetoothPairingController();
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
@@ -247,19 +224,6 @@ public class BluetoothPairingControllerTest {
assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue();
}
@Test
public void isContactSharingVisible_DeviceIsLeAndContactSharingIsNotEnabled_returnsFalse() {
// isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=false
mockIsProfileReady(false);
mockIsLeAudio(true);
mockIsLeContactSharingEnabled(false);
mBluetoothPairingController = createBluetoothPairingController();
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
}
private void mockIsProfileReady(boolean mockValue) {
when(mPbapLocalBluetoothProfile.isProfileReady()).thenReturn(mockValue);
}
@@ -271,11 +235,4 @@ public class BluetoothPairingControllerTest {
}
when(mLocalBluetoothProfile.getProfileId()).thenReturn(profileId);
}
private void mockIsLeContactSharingEnabled(boolean mockValue) {
android.provider.DeviceConfig.setProperty(
android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED,
/* value= */ mockValue ? "true" : "false", true);
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.widget.TextView;
import com.android.settings.R;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowDeviceConfig.class})
public class GeneralBluetoothDetailsHeaderControllerTest
extends BluetoothDetailsControllerTestBase {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private GeneralBluetoothDetailsHeaderController mController;
private LayoutPreference mPreference;
@Mock private BluetoothDevice mBluetoothDevice;
@Mock private LeAudioProfile mLeAudioProfile;
@Override
public void setUp() {
super.setUp();
FakeFeatureFactory.setupForTest();
mController =
new GeneralBluetoothDetailsHeaderController(
mContext, mFragment, mCachedDevice, mLifecycle);
mPreference = new LayoutPreference(mContext, R.layout.general_bt_entity_header);
mPreference.setKey(mController.getPreferenceKey());
mScreen.addPreference(mPreference);
setupDevice(mDeviceConfig);
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
}
@After
public void tearDown() {
ShadowEntityHeaderController.reset();
}
/**
* Test to verify the current test context object works so that we are not checking null against
* null
*/
@Test
public void testContextMock() {
assertThat(mContext.getString(com.android.settingslib.R.string.bluetooth_connected))
.isNotNull();
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void header() {
when(mCachedDevice.getName()).thenReturn("device name");
when(mCachedDevice.getConnectionSummary()).thenReturn("Active");
showScreen(mController);
TextView deviceName = mPreference.findViewById(R.id.bt_header_device_name);
TextView summary = mPreference.findViewById(R.id.bt_header_connection_summary);
assertThat(deviceName.getText().toString()).isEqualTo("device name");
assertThat(summary.getText().toString()).isEqualTo("Active");
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void connectionStatusChangesWhileScreenOpen() {
TextView summary = mPreference.findViewById(R.id.bt_header_connection_summary);
when(mCachedDevice.getConnectionSummary())
.thenReturn(
mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
showScreen(mController);
String summaryText1 = summary.getText().toString();
when(mCachedDevice.getConnectionSummary()).thenReturn(null);
mController.onDeviceAttributesChanged();
String summaryText2 = summary.getText().toString();
when(mCachedDevice.getConnectionSummary())
.thenReturn(
mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
mController.onDeviceAttributesChanged();
String summaryText3 = summary.getText().toString();
assertThat(summaryText1)
.isEqualTo(
mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
assertThat(summaryText2).isEqualTo("");
assertThat(summaryText3)
.isEqualTo(
mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void isAvailable_untetheredHeadset_returnFalse() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("true".getBytes());
assertThat(mController.isAvailable()).isFalse();
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void isAvailable_notUntetheredHeadset_returnTrue() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("false".getBytes());
assertThat(mController.isAvailable()).isTrue();
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void isAvailable_leAudioDevice_returnFalse() {
when(mCachedDevice.getUiAccessibleProfiles())
.thenReturn(List.of(mLeAudioProfile));
assertThat(mController.isAvailable()).isFalse();
}
@Test
@EnableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void isAvailable_flagEnabled_returnTrue() {
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
.thenReturn("false".getBytes());
assertThat(mController.isAvailable()).isTrue();
}
@Test
@DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH)
public void iaAvailable_flagDisabled_returnFalse() {
assertThat(mController.isAvailable()).isFalse();
}
}

View File

@@ -0,0 +1,254 @@
/*
* 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.bluetooth.domain.interactor
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.core.app.ApplicationProvider
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LeAudioProfile
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.media.data.repository.SpatializerRepository
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoInteractions
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.robolectric.RobolectricTestRunner
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(RobolectricTestRunner::class)
class SpatialAudioInteractorTest {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var cachedDevice: CachedBluetoothDevice
@Mock private lateinit var bluetoothDevice: BluetoothDevice
@Mock private lateinit var spatializerRepository: SpatializerRepository
@Mock private lateinit var leAudioProfile: LeAudioProfile
private lateinit var underTest: SpatialAudioInteractor
private val testScope = TestScope()
@Before
fun setUp() {
val context = spy(ApplicationProvider.getApplicationContext<Context>())
`when`(cachedDevice.device).thenReturn(bluetoothDevice)
`when`(cachedDevice.address).thenReturn(BLUETOOTH_ADDRESS)
`when`(leAudioProfile.profileId).thenReturn(BluetoothProfile.LE_AUDIO)
underTest =
SpatialAudioInteractorImpl(
context,
audioManager,
SpatializerInteractor(spatializerRepository),
testScope.backgroundScope,
testScope.testScheduler)
}
@Test
fun getDeviceSetting_noAudioProfile_returnNull() {
testScope.runTest {
val setting = getLatestValue(underTest.getDeviceSetting(cachedDevice))
assertThat(setting).isNull()
verifyNoInteractions(spatializerRepository)
}
}
@Test
fun getDeviceSetting_audioProfileNotEnabled_returnNull() {
testScope.runTest {
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(false)
val setting = getLatestValue(underTest.getDeviceSetting(cachedDevice))
assertThat(setting).isNull()
verifyNoInteractions(spatializerRepository)
}
}
@Test
fun getDeviceSetting_spatialAudioNotSupported_returnNull() {
testScope.runTest {
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
spatializerRepository.isSpatialAudioAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(false)
val setting = getLatestValue(underTest.getDeviceSetting(cachedDevice))
assertThat(setting).isNull()
}
}
@Test
fun getDeviceSetting_spatialAudioSupported_returnTwoToggles() {
testScope.runTest {
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
spatializerRepository.isSpatialAudioAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(
spatializerRepository.isHeadTrackingAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(false)
`when`(spatializerRepository.getSpatialAudioCompatibleDevices())
.thenReturn(listOf(BLE_AUDIO_DEVICE_ATTRIBUTES))
`when`(spatializerRepository.isHeadTrackingEnabled(BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(false)
val setting =
getLatestValue(underTest.getDeviceSetting(cachedDevice))
as DeviceSettingModel.MultiTogglePreference
assertThat(setting).isNotNull()
assertThat(setting.toggles.size).isEqualTo(2)
assertThat(setting.state.selectedIndex).isEqualTo(1)
}
}
@Test
fun getDeviceSetting_headTrackingSupported_returnThreeToggles() {
testScope.runTest {
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
spatializerRepository.isSpatialAudioAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(
spatializerRepository.isHeadTrackingAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(spatializerRepository.getSpatialAudioCompatibleDevices())
.thenReturn(listOf(BLE_AUDIO_DEVICE_ATTRIBUTES))
`when`(spatializerRepository.isHeadTrackingEnabled(BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
val setting =
getLatestValue(underTest.getDeviceSetting(cachedDevice))
as DeviceSettingModel.MultiTogglePreference
assertThat(setting).isNotNull()
assertThat(setting.toggles.size).isEqualTo(3)
assertThat(setting.state.selectedIndex).isEqualTo(2)
}
}
@Test
fun getDeviceSetting_updateState_enableSpatialAudio() {
testScope.runTest {
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
spatializerRepository.isSpatialAudioAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(
spatializerRepository.isHeadTrackingAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(spatializerRepository.getSpatialAudioCompatibleDevices()).thenReturn(listOf())
`when`(spatializerRepository.isHeadTrackingEnabled(BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(false)
val setting =
getLatestValue(underTest.getDeviceSetting(cachedDevice))
as DeviceSettingModel.MultiTogglePreference
setting.updateState(DeviceSettingStateModel.MultiTogglePreferenceState(2))
runCurrent()
assertThat(setting).isNotNull()
verify(spatializerRepository, times(1))
.addSpatialAudioCompatibleDevice(BLE_AUDIO_DEVICE_ATTRIBUTES)
}
}
@Test
fun getDeviceSetting_updateState_enableHeadTracking() {
testScope.runTest {
`when`(cachedDevice.profiles).thenReturn(listOf(leAudioProfile))
`when`(leAudioProfile.isEnabled(bluetoothDevice)).thenReturn(true)
`when`(
spatializerRepository.isSpatialAudioAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(
spatializerRepository.isHeadTrackingAvailableForDevice(
BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(true)
`when`(spatializerRepository.getSpatialAudioCompatibleDevices()).thenReturn(listOf())
`when`(spatializerRepository.isHeadTrackingEnabled(BLE_AUDIO_DEVICE_ATTRIBUTES))
.thenReturn(false)
val setting =
getLatestValue(underTest.getDeviceSetting(cachedDevice))
as DeviceSettingModel.MultiTogglePreference
setting.updateState(DeviceSettingStateModel.MultiTogglePreferenceState(2))
runCurrent()
assertThat(setting).isNotNull()
verify(spatializerRepository, times(1))
.addSpatialAudioCompatibleDevice(BLE_AUDIO_DEVICE_ATTRIBUTES)
verify(spatializerRepository, times(1))
.setHeadTrackingEnabled(BLE_AUDIO_DEVICE_ATTRIBUTES, true)
}
}
private fun getLatestValue(deviceSettingFlow: Flow<DeviceSettingModel?>): DeviceSettingModel? {
var latestValue: DeviceSettingModel? = null
deviceSettingFlow.onEach { latestValue = it }.launchIn(testScope.backgroundScope)
testScope.runCurrent()
return latestValue
}
private companion object {
const val BLUETOOTH_ADDRESS = "12:34:56:78:12:34"
val BLE_AUDIO_DEVICE_ATTRIBUTES =
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_HEADSET,
BLUETOOTH_ADDRESS,
)
}
}

View File

@@ -0,0 +1,304 @@
/*
* 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.bluetooth.ui.view
import android.bluetooth.BluetoothAdapter
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.media.AudioManager
import android.net.Uri
import androidx.fragment.app.FragmentActivity
import androidx.preference.Preference
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
import androidx.test.core.app.ApplicationProvider
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
import com.android.settings.dashboard.DashboardFragment
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.Shadows
import org.robolectric.shadows.ShadowLooper
import org.robolectric.shadows.ShadowLooper.shadowMainLooper
@RunWith(RobolectricTestRunner::class)
class DeviceDetailsFragmentFormatterTest {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var cachedDevice: CachedBluetoothDevice
@Mock private lateinit var bluetoothAdapter: BluetoothAdapter
@Mock private lateinit var repository: DeviceSettingRepository
@Mock private lateinit var spatialAudioInteractor: SpatialAudioInteractor
private lateinit var fragment: TestFragment
private lateinit var underTest: DeviceDetailsFragmentFormatter
private lateinit var featureFactory: FakeFeatureFactory
private lateinit var fragmentActivity: FragmentActivity
private val testScope = TestScope()
@Before
fun setUp() {
val context = ApplicationProvider.getApplicationContext<Context>()
featureFactory = FakeFeatureFactory.setupForTest()
`when`(
featureFactory.bluetoothFeatureProvider.getDeviceSettingRepository(
eq(context), eq(bluetoothAdapter), any()))
.thenReturn(repository)
`when`(
featureFactory.bluetoothFeatureProvider.getSpatialAudioInteractor(
eq(context), any(AudioManager::class.java), any()))
.thenReturn(spatialAudioInteractor)
fragmentActivity = Robolectric.setupActivity(FragmentActivity::class.java)
assertThat(fragmentActivity.applicationContext).isNotNull()
fragment = TestFragment(context)
fragmentActivity.supportFragmentManager.beginTransaction().add(fragment, null).commit()
shadowMainLooper().idle()
fragment.preferenceScreen.run {
addPreference(Preference(context).apply { key = "bluetooth_device_header" })
addPreference(Preference(context).apply { key = "action_buttons" })
addPreference(Preference(context).apply { key = "keyboard_settings" })
}
underTest =
DeviceDetailsFragmentFormatterImpl(
context,
fragment,
bluetoothAdapter,
cachedDevice,
testScope.testScheduler)
}
@Test
fun getVisiblePreferenceKeysForMainPage_hasConfig_returnList() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
highlighted = false,
preferenceKey = "bluetooth_device_header"
),
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, highlighted = false, preferenceKey = "action_buttons"),
),
listOf(),
null))
val keys =
underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(keys).containsExactly("bluetooth_device_header", "action_buttons")
}
}
@Test
fun getVisiblePreferenceKeysForMainPage_noConfig_returnNull() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null)
val keys =
underTest.getVisiblePreferenceKeys(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(keys).isNull()
}
}
@Test
fun getMenuItem_returnItem() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(), listOf(), DeviceSettingConfigItemModel.AppProvidedItem(12345, false)))
val intent = Intent().apply {
setAction(Intent.ACTION_VIEW)
setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
`when`(repository.getDeviceSetting(cachedDevice, 12345))
.thenReturn(
flowOf(
DeviceSettingModel.HelpPreference(
cachedDevice = cachedDevice,
id = 12345,
intent = intent,
)))
var helpPreference: DeviceSettingPreferenceModel.HelpPreference? = null
underTest.getMenuItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment).onEach {
helpPreference = it
}.launchIn(testScope.backgroundScope)
delay(100)
runCurrent()
helpPreference!!.onClick()
ShadowLooper.idleMainLooper()
val shadowActivity = Shadows.shadowOf(fragmentActivity)
assertThat(shadowActivity.nextStartedActivity).isSameInstanceAs(intent)
}
}
@Test
fun updateLayout_configIsNull_notChange() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice)).thenReturn(null)
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(getDisplayedPreferences().mapNotNull { it.key })
.containsExactly("bluetooth_device_header", "action_buttons", "keyboard_settings")
}
}
@Test
fun updateLayout_itemsNotInConfig_hide() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
highlighted = false, preferenceKey = "bluetooth_device_header"),
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
highlighted = false, preferenceKey = "keyboard_settings"),
),
listOf(),
null))
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(getDisplayedPreferences().mapNotNull { it.key })
.containsExactly("bluetooth_device_header", "keyboard_settings")
}
}
@Test
fun updateLayout_newItems_displayNewItems() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_HEADER,
highlighted = false,
preferenceKey = "bluetooth_device_header"),
DeviceSettingConfigItemModel.AppProvidedItem(
DeviceSettingId.DEVICE_SETTING_ID_ANC, highlighted = false),
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_KEYBOARD_SETTINGS,
highlighted = false,
preferenceKey = "keyboard_settings"),
),
listOf(),
null))
`when`(repository.getDeviceSetting(cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_ANC))
.thenReturn(
flowOf(
DeviceSettingModel.MultiTogglePreference(
cachedDevice,
DeviceSettingId.DEVICE_SETTING_ID_ANC,
"title",
toggles =
listOf(
ToggleModel(
"",
DeviceSettingIcon.BitmapIcon(
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)))),
isActive = true,
state = DeviceSettingStateModel.MultiTogglePreferenceState(0),
isAllowedChangingState = true,
updateState = {})))
underTest.updateLayout(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(getDisplayedPreferences().mapNotNull { it.key })
.containsExactly(
"bluetooth_device_header",
"DEVICE_SETTING_${DeviceSettingId.DEVICE_SETTING_ID_ANC}",
"keyboard_settings")
}
}
private fun getDisplayedPreferences(): List<Preference> {
val prefs = mutableListOf<Preference>()
for (i in 0..<fragment.preferenceScreen.preferenceCount) {
prefs.add(fragment.preferenceScreen.getPreference(i))
}
return prefs
}
class TestFragment(context: Context) : DashboardFragment() {
private val mPreferenceManager: PreferenceManager = PreferenceManager(context)
init {
mPreferenceManager.setPreferences(mPreferenceManager.createPreferenceScreen(context))
}
public override fun getPreferenceScreenResId(): Int = 0
override fun getLogTag(): String = "TestLogTag"
override fun getPreferenceScreen(): PreferenceScreen {
return mPreferenceManager.preferenceScreen
}
override fun getMetricsCategory(): Int = 0
override fun getPreferenceManager(): PreferenceManager {
return mPreferenceManager
}
}
private companion object {}
}

View File

@@ -0,0 +1,292 @@
/*
* 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.bluetooth.ui.viewmodel
import android.app.Application
import android.bluetooth.BluetoothAdapter
import android.graphics.Bitmap
import androidx.test.core.app.ApplicationProvider
import com.android.settings.bluetooth.domain.interactor.SpatialAudioInteractor
import com.android.settings.bluetooth.ui.layout.DeviceSettingLayout
import com.android.settings.bluetooth.ui.model.DeviceSettingPreferenceModel
import com.android.settings.bluetooth.ui.model.FragmentTypeModel
import com.android.settings.testutils.FakeFeatureFactory
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.devicesettings.DeviceSettingId
import com.android.settingslib.bluetooth.devicesettings.data.repository.DeviceSettingRepository
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigItemModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingConfigModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingIcon
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.DeviceSettingStateModel
import com.android.settingslib.bluetooth.devicesettings.shared.model.ToggleModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.robolectric.RobolectricTestRunner
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(RobolectricTestRunner::class)
class BluetoothDeviceDetailsViewModelTest {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var cachedDevice: CachedBluetoothDevice
@Mock private lateinit var bluetoothAdapter: BluetoothAdapter
@Mock private lateinit var repository: DeviceSettingRepository
@Mock private lateinit var spatialAudioInteractor: SpatialAudioInteractor
private lateinit var underTest: BluetoothDeviceDetailsViewModel
private lateinit var featureFactory: FakeFeatureFactory
private val testScope = TestScope()
@Before
fun setUp() {
val application = ApplicationProvider.getApplicationContext<Application>()
featureFactory = FakeFeatureFactory.setupForTest()
underTest =
BluetoothDeviceDetailsViewModel(
application,
repository,
spatialAudioInteractor,
cachedDevice,
testScope.testScheduler)
}
@Test
fun getItems_returnConfigMainMainItems() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), null))
val keys = underTest.getItems(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(keys).containsExactly(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2)
}
}
@Test
fun getHelpItems_mainPage_returnNull() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2),
listOf(),
SETTING_ITEM_HELP))
val item = underTest.getHelpItem(FragmentTypeModel.DeviceDetailsMainFragment)
assertThat(item).isNull()
}
}
@Test
fun getHelpItems_moreSettings_returnConfigHelpItem() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2),
listOf(),
SETTING_ITEM_HELP))
val item = underTest.getHelpItem(FragmentTypeModel.DeviceDetailsMoreSettingsFragment)
assertThat(item).isSameInstanceAs(SETTING_ITEM_HELP)
}
}
@Test
fun getDeviceSetting_returnRepositoryResponse() {
testScope.runTest {
val remoteSettingId1 = 10001
val pref = buildMultiTogglePreference(remoteSettingId1)
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(
BUILTIN_SETTING_ITEM_1,
buildRemoteSettingItem(remoteSettingId1),
),
listOf(),
null))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
.thenReturn(flowOf(pref))
var deviceSettingPreference: DeviceSettingPreferenceModel? = null
underTest
.getDeviceSetting(cachedDevice, remoteSettingId1)
.onEach { deviceSettingPreference = it }
.launchIn(testScope.backgroundScope)
runCurrent()
assertThat(deviceSettingPreference?.id).isEqualTo(pref.id)
verify(repository, times(1)).getDeviceSetting(cachedDevice, remoteSettingId1)
}
}
@Test
fun getDeviceSetting_spatialAudio_returnSpatialAudioInteractorResponse() {
testScope.runTest {
val pref =
buildMultiTogglePreference(
DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE)
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(
BUILTIN_SETTING_ITEM_1,
buildRemoteSettingItem(
DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE),
),
listOf(),
null))
`when`(spatialAudioInteractor.getDeviceSetting(cachedDevice)).thenReturn(flowOf(pref))
var deviceSettingPreference: DeviceSettingPreferenceModel? = null
underTest
.getDeviceSetting(
cachedDevice, DeviceSettingId.DEVICE_SETTING_ID_SPATIAL_AUDIO_MULTI_TOGGLE)
.onEach { deviceSettingPreference = it }
.launchIn(testScope.backgroundScope)
runCurrent()
assertThat(deviceSettingPreference?.id).isEqualTo(pref.id)
verify(spatialAudioInteractor, times(1)).getDeviceSetting(cachedDevice)
}
}
@Test
fun getLayout_builtinDeviceSettings() {
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(BUILTIN_SETTING_ITEM_1, BUILDIN_SETTING_ITEM_2), listOf(), null))
val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
assertThat(getLatestLayout(layout))
.isEqualTo(
listOf(
listOf(DeviceSettingId.DEVICE_SETTING_ID_HEADER),
listOf(DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS)))
}
}
@Test
fun getLayout_remoteDeviceSettings() {
val remoteSettingId1 = 10001
val remoteSettingId2 = 10002
val remoteSettingId3 = 10003
testScope.runTest {
`when`(repository.getDeviceSettingsConfig(cachedDevice))
.thenReturn(
DeviceSettingConfigModel(
listOf(
BUILTIN_SETTING_ITEM_1,
buildRemoteSettingItem(remoteSettingId1),
buildRemoteSettingItem(remoteSettingId2),
buildRemoteSettingItem(remoteSettingId3),
),
listOf(),
null))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId1))
.thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId1)))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId2))
.thenReturn(flowOf(buildMultiTogglePreference(remoteSettingId2)))
`when`(repository.getDeviceSetting(cachedDevice, remoteSettingId3))
.thenReturn(flowOf(buildActionSwitchPreference(remoteSettingId3)))
val layout = underTest.getLayout(FragmentTypeModel.DeviceDetailsMainFragment)!!
assertThat(getLatestLayout(layout))
.isEqualTo(
listOf(
listOf(DeviceSettingId.DEVICE_SETTING_ID_HEADER),
listOf(remoteSettingId1, remoteSettingId2),
listOf(remoteSettingId3),
))
}
}
private fun getLatestLayout(layout: DeviceSettingLayout): List<List<Int>> {
val latestLayout = MutableList(layout.rows.size) { emptyList<Int>() }
for (i in layout.rows.indices) {
layout.rows[i]
.columns
.onEach { latestLayout[i] = it.map { c -> c.settingId } }
.launchIn(testScope.backgroundScope)
}
testScope.runCurrent()
return latestLayout.filter { !it.isEmpty() }.toList()
}
private fun buildMultiTogglePreference(settingId: Int) =
DeviceSettingModel.MultiTogglePreference(
cachedDevice,
settingId,
"title",
toggles =
listOf(
ToggleModel(
"toggle1",
DeviceSettingIcon.BitmapIcon(
Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)))),
isActive = true,
state = DeviceSettingStateModel.MultiTogglePreferenceState(0),
isAllowedChangingState = true,
updateState = {})
private fun buildActionSwitchPreference(settingId: Int) =
DeviceSettingModel.ActionSwitchPreference(cachedDevice, settingId, "title")
private fun buildRemoteSettingItem(settingId: Int) =
DeviceSettingConfigItemModel.AppProvidedItem(settingId, false)
private companion object {
val BUILTIN_SETTING_ITEM_1 =
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_HEADER, false, "bluetooth_device_header")
val BUILDIN_SETTING_ITEM_2 =
DeviceSettingConfigItemModel.BuiltinItem.CommonBuiltinItem(
DeviceSettingId.DEVICE_SETTING_ID_ACTION_BUTTONS, false, "action_buttons")
val SETTING_ITEM_HELP = DeviceSettingConfigItemModel.AppProvidedItem(12345, false)
}
}

View File

@@ -36,7 +36,6 @@ import com.android.settings.R;
import com.android.settings.connecteddevice.fastpair.FastPairDeviceUpdater;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerListHelper;
import com.android.settings.flags.Flags;
import com.android.settings.slices.SlicePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
@@ -94,7 +93,6 @@ public class ConnectedDeviceDashboardFragmentTest {
mContext = spy(RuntimeEnvironment.application);
mFragment = new ConnectedDeviceDashboardFragment();
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_SUBSEQUENT_PAIR_SETTINGS_INTEGRATION);
mSetFlagsRule.enableFlags(com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mFeatureFactory

View File

@@ -17,6 +17,8 @@ package com.android.settings.connecteddevice;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.android.settings.flags.Flags.FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING;
import static com.android.settings.flags.Flags.FLAG_ROTATION_CONNECTED_DISPLAY_SETTING;
import static com.google.common.truth.Truth.assertThat;
@@ -30,6 +32,7 @@ import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.input.InputManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -40,13 +43,16 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.display.ExternalDisplayUpdater;
import com.android.settings.connecteddevice.dock.DockUpdater;
import com.android.settings.connecteddevice.stylus.StylusDeviceUpdater;
import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.FakeFeatureFlagsImpl;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
@@ -65,7 +71,6 @@ import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplicationPackageManager;
@@ -84,6 +89,8 @@ public class ConnectedDeviceGroupControllerTest {
@Mock
private DashboardFragment mDashboardFragment;
@Mock
private ExternalDisplayUpdater mExternalDisplayUpdater;
@Mock
private ConnectedBluetoothDeviceUpdater mConnectedBluetoothDeviceUpdater;
@Mock
private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater;
@@ -105,6 +112,9 @@ public class ConnectedDeviceGroupControllerTest {
private CachedBluetoothDevice mCachedDevice;
@Mock
private BluetoothDevice mDevice;
@Mock
private Resources mResources;
private final FakeFeatureFlagsImpl mFakeFeatureFlags = new FakeFeatureFlagsImpl();
private ShadowApplicationPackageManager mPackageManager;
private PreferenceGroup mPreferenceGroup;
@@ -118,8 +128,10 @@ public class ConnectedDeviceGroupControllerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFakeFeatureFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, true);
mFakeFeatureFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, true);
mContext = spy(RuntimeEnvironment.application);
mContext = spy(ApplicationProvider.getApplicationContext());
mPreference = new Preference(mContext);
mPreference.setKey(PREFERENCE_KEY_1);
mPackageManager = (ShadowApplicationPackageManager) Shadows.shadowOf(
@@ -129,15 +141,19 @@ public class ConnectedDeviceGroupControllerTest {
doReturn(mContext).when(mDashboardFragment).getContext();
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager);
when(mContext.getResources()).thenReturn(mResources);
when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{});
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mContext);
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, mConnectedDockUpdater, mStylusDeviceUpdater);
mConnectedDeviceGroupController = spy(new ConnectedDeviceGroupController(mContext));
when(mConnectedDeviceGroupController.getFeatureFlags()).thenReturn(mFakeFeatureFlags);
mConnectedDeviceGroupController.init(mExternalDisplayUpdater,
mConnectedBluetoothDeviceUpdater, mConnectedUsbDeviceUpdater, mConnectedDockUpdater,
mStylusDeviceUpdater);
mConnectedDeviceGroupController.mPreferenceGroup = mPreferenceGroup;
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
@@ -147,6 +163,7 @@ public class ConnectedDeviceGroupControllerTest {
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
true);
when(mPreferenceScreen.getContext()).thenReturn(mContext);
}
@Test
@@ -193,6 +210,7 @@ public class ConnectedDeviceGroupControllerTest {
// register the callback in onStart()
mConnectedDeviceGroupController.onStart();
verify(mExternalDisplayUpdater).registerCallback();
verify(mConnectedBluetoothDeviceUpdater).registerCallback();
verify(mConnectedUsbDeviceUpdater).registerCallback();
verify(mConnectedDockUpdater).registerCallback();
@@ -204,6 +222,7 @@ public class ConnectedDeviceGroupControllerTest {
public void onStop_shouldUnregisterUpdaters() {
// unregister the callback in onStop()
mConnectedDeviceGroupController.onStop();
verify(mExternalDisplayUpdater).unregisterCallback();
verify(mConnectedBluetoothDeviceUpdater).unregisterCallback();
verify(mConnectedUsbDeviceUpdater).unregisterCallback();
verify(mConnectedDockUpdater).unregisterCallback();
@@ -212,22 +231,36 @@ public class ConnectedDeviceGroupControllerTest {
@Test
public void getAvailabilityStatus_noBluetoothUsbDockFeature_returnUnSupported() {
mFakeFeatureFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, false);
mFakeFeatureFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, null, null);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_connectedDisplay_returnSupported() {
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, null, null);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
AVAILABLE_UNSEARCHABLE);
}
@Test
public void getAvailabilityStatus_BluetoothFeature_returnSupported() {
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, null, null);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
@@ -239,7 +272,7 @@ public class ConnectedDeviceGroupControllerTest {
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, true);
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, null, null);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
@@ -251,7 +284,7 @@ public class ConnectedDeviceGroupControllerTest {
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, mConnectedDockUpdater, null);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
@@ -261,6 +294,8 @@ public class ConnectedDeviceGroupControllerTest {
@Test
public void getAvailabilityStatus_noUsiStylusFeature_returnUnSupported() {
mFakeFeatureFlags.setFlag(FLAG_ROTATION_CONNECTED_DISPLAY_SETTING, false);
mFakeFeatureFlags.setFlag(FLAG_RESOLUTION_AND_ENABLE_CONNECTED_DISPLAY_SETTING, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false);
mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false);
@@ -268,7 +303,7 @@ public class ConnectedDeviceGroupControllerTest {
when(mInputManager.getInputDevice(0)).thenReturn(new InputDevice.Builder().setSources(
InputDevice.SOURCE_DPAD).setExternal(false).build());
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, null, mStylusDeviceUpdater);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(
@@ -284,7 +319,7 @@ public class ConnectedDeviceGroupControllerTest {
when(mInputManager.getInputDevice(0)).thenReturn(new InputDevice.Builder().setSources(
InputDevice.SOURCE_STYLUS).setExternal(false).build());
mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater,
mConnectedDeviceGroupController.init(null, mConnectedBluetoothDeviceUpdater,
mConnectedUsbDeviceUpdater, mConnectedDockUpdater, mStylusDeviceUpdater);
assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo(

View File

@@ -16,6 +16,8 @@
package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingBluetoothDeviceUpdater.PREF_KEY_PREFIX;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -85,7 +87,6 @@ import java.util.List;
public class AudioSharingBluetoothDeviceUpdaterTest {
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
private static final String TEST_DEVICE_NAME = "test";
private static final String PREF_KEY = "audio_sharing_bt";
private static final String TAG = "AudioSharingBluetoothDeviceUpdater";
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -263,7 +264,7 @@ public class AudioSharingBluetoothDeviceUpdaterTest {
@Test
public void getPreferenceKey_returnsCorrectKey() {
assertThat(mDeviceUpdater.getPreferenceKey()).isEqualTo(PREF_KEY);
assertThat(mDeviceUpdater.getPreferenceKeyPrefix()).isEqualTo(PREF_KEY_PREFIX);
}
@Test

View File

@@ -23,6 +23,7 @@ import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.os.Bundle;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.appcompat.app.AlertDialog;
@@ -78,10 +79,6 @@ public class AudioSharingCallAudioDialogFragmentTest {
BluetoothStatusCodes.FEATURE_SUPPORTED);
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mFragment = new AudioSharingCallAudioDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
}
@After
@@ -91,6 +88,7 @@ public class AudioSharingCallAudioDialogFragmentTest {
@Test
public void getMetricsCategory_correctValue() {
mFragment = new AudioSharingCallAudioDialogFragment();
assertThat(mFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_SHARING_CALL_AUDIO);
}
@@ -98,21 +96,52 @@ public class AudioSharingCallAudioDialogFragmentTest {
@Test
public void onCreateDialog_flagOff_dialogNotExist() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.show(mParent, new ArrayList<>(), (item) -> {});
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
AudioSharingCallAudioDialogFragment.show(mParent, new ArrayList<>(), -1, (item) -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mParent = new Fragment();
AudioSharingCallAudioDialogFragment.show(mParent, new ArrayList<>(), -1, (item) -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_nullDeviceItems_showEmptyDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment = new AudioSharingCallAudioDialogFragment();
mFragment.setArguments(Bundle.EMPTY);
FragmentController.setupFragment(
mFragment, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
AlertDialog dialog = (AlertDialog) mFragment.onCreateDialog(Bundle.EMPTY);
dialog.show();
shadowMainLooper().idle();
assertThat(dialog.isShowing()).isTrue();
assertThat(dialog.getListView()).isNull();
}
@Test
public void onCreateDialog_showCorrectItems() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
ArrayList<AudioSharingDeviceItem> deviceItemList = new ArrayList<>();
deviceItemList.add(TEST_DEVICE_ITEM1);
deviceItemList.add(TEST_DEVICE_ITEM2);
mFragment.show(mParent, deviceItemList, (item) -> {});
AudioSharingCallAudioDialogFragment.show(mParent, deviceItemList, 0, (item) -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.getListView().getCount()).isEqualTo(2);
}
}

View File

@@ -16,7 +16,6 @@
package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
@@ -64,6 +63,7 @@ import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -197,7 +197,8 @@ public class AudioSharingCallAudioPreferenceControllerTest {
verify(mBtEventManager, never()).registerCallback(mController);
verify(mContentResolver, never())
.registerContentObserver(
Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
Settings.Secure.getUriFor(
BluetoothUtils.getPrimaryGroupIdUriForBroadcast()),
false,
mContentObserver);
verify(mAssistant, never())
@@ -211,7 +212,8 @@ public class AudioSharingCallAudioPreferenceControllerTest {
verify(mBtEventManager).registerCallback(mController);
verify(mContentResolver)
.registerContentObserver(
Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
Settings.Secure.getUriFor(
BluetoothUtils.getPrimaryGroupIdUriForBroadcast()),
false,
mContentObserver);
verify(mAssistant)
@@ -319,9 +321,7 @@ public class AudioSharingCallAudioPreferenceControllerTest {
public void onProfileConnectionStateChanged_noDeviceInSharing_updateSummary() {
Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of());
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
mController.displayPreference(mScreen);
mPreference.setSummary("test");
mController.onProfileConnectionStateChanged(
@@ -340,9 +340,7 @@ public class AudioSharingCallAudioPreferenceControllerTest {
when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.displayPreference(mScreen);
mContentObserver.onChange(true);
@@ -369,8 +367,7 @@ public class AudioSharingCallAudioPreferenceControllerTest {
when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
when(mCacheManager.findDevice(mDevice3)).thenReturn(mCachedDevice3);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
when(mAssistant.getAllConnectedDevices())
.thenReturn(ImmutableList.of(mDevice1, mDevice2, mDevice3));
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.displayPreference(mScreen);
@@ -389,9 +386,7 @@ public class AudioSharingCallAudioPreferenceControllerTest {
when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.displayPreference(mScreen);
shadowOf(Looper.getMainLooper()).idle();
@@ -403,9 +398,7 @@ public class AudioSharingCallAudioPreferenceControllerTest {
Settings.Secure.putInt(
mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of());
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
mController.displayPreference(mScreen);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.getSummary().toString()).isEmpty();
@@ -429,9 +422,7 @@ public class AudioSharingCallAudioPreferenceControllerTest {
when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(List.of(mDevice1, mDevice2));
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice1, mDevice2));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.init(mParentFragment);
mController.displayPreference(mScreen);
@@ -521,18 +512,14 @@ public class AudioSharingCallAudioPreferenceControllerTest {
Settings.Secure.putInt(
mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of());
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
mController.displayPreference(mScreen);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.getSummary().toString()).isEmpty();
// onReceiveStateChanged will update summary
Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mDevice1, /* sourceId= */ 1, mState);
@@ -552,17 +539,13 @@ public class AudioSharingCallAudioPreferenceControllerTest {
Settings.Secure.putInt(
mContentResolver, TEST_SETTINGS_KEY, BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of());
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
mController.displayPreference(mScreen);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.getSummary().toString()).isEmpty();
Settings.Secure.putInt(mContentResolver, TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID1);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);

View File

@@ -95,6 +95,15 @@ public class AudioSharingConfirmDialogFragmentTest {
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingConfirmDialogFragment.show(new Fragment());
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_showDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);

View File

@@ -16,51 +16,81 @@
package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingDashboardFragment.SHARE_THEN_PAIR_REQUEST_CODE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
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 static org.robolectric.Shadows.shadowOf;
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.View;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsCategoryController;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.widget.SettingsMainSwitchBar;
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.Mockito;
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;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowFragment.class})
@Config(shadows = {ShadowFragment.class, ShadowBluetoothAdapter.class})
public class AudioSharingDashboardFragmentTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private SettingsActivity mActivity;
@Mock private SettingsMainSwitchBar mSwitchBar;
@Mock private View mView;
@Mock private AudioSharingDeviceVolumeGroupController mVolumeGroupController;
@Mock private AudioSharingCallAudioPreferenceController mCallAudioController;
@Mock private AudioSharingPlaySoundPreferenceController mPlaySoundController;
@Mock private AudioStreamsCategoryController mStreamsCategoryController;
@Mock private AudioSharingSwitchBarController mSwitchBarController;
private final Context mContext = ApplicationProvider.getApplicationContext();
private AudioSharingDashboardFragment mFragment;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
when(mSwitchBar.getRootView()).thenReturn(mView);
mFragment = new AudioSharingDashboardFragment();
}
@@ -96,13 +126,73 @@ public class AudioSharingDashboardFragmentTest {
verify(mSwitchBar).show();
}
@Test
public void onActivityResult_shareThenPairWithBadCode_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController,
mSwitchBarController);
Intent data = new Intent();
Bundle extras = new Bundle();
BluetoothDevice device = Mockito.mock(BluetoothDevice.class);
extras.putParcelable(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, device);
data.putExtras(extras);
mFragment.onActivityResult(SHARE_THEN_PAIR_REQUEST_CODE, Activity.RESULT_CANCELED, data);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBarController, never()).handleAutoAddSourceAfterPair(device);
}
@Test
public void onActivityResult_shareThenPairWithNoDevice_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController,
mSwitchBarController);
Intent data = new Intent();
Bundle extras = new Bundle();
extras.putParcelable(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, null);
data.putExtras(extras);
mFragment.onActivityResult(SHARE_THEN_PAIR_REQUEST_CODE, Activity.RESULT_CANCELED, data);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBarController, never()).handleAutoAddSourceAfterPair(any());
}
@Test
public void onActivityResult_shareThenPairWithDevice_handleAutoAddSource() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController,
mSwitchBarController);
Intent data = new Intent();
Bundle extras = new Bundle();
BluetoothDevice device = Mockito.mock(BluetoothDevice.class);
extras.putParcelable(EXTRA_BT_DEVICE_TO_AUTO_ADD_SOURCE, device);
data.putExtras(extras);
mFragment.onActivityResult(SHARE_THEN_PAIR_REQUEST_CODE, Activity.RESULT_OK, data);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBarController).handleAutoAddSourceAfterPair(device);
}
@Test
public void onAudioSharingStateChanged_updateVisibilityForControllers() {
mFragment.setControllers(
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController);
mStreamsCategoryController,
mSwitchBarController);
mFragment.onAudioSharingStateChanged();
verify(mVolumeGroupController).updateVisibility();
verify(mCallAudioController).updateVisibility();
@@ -116,7 +206,8 @@ public class AudioSharingDashboardFragmentTest {
mVolumeGroupController,
mCallAudioController,
mPlaySoundController,
mStreamsCategoryController);
mStreamsCategoryController,
mSwitchBarController);
mFragment.onAudioSharingProfilesConnected();
verify(mVolumeGroupController).onAudioSharingProfilesConnected();
}

View File

@@ -30,9 +30,11 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
@@ -57,6 +59,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.SettingsActivity;
import com.android.settings.bluetooth.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowFragment;
@@ -136,6 +139,7 @@ public class AudioSharingDevicePreferenceControllerTest {
private LifecycleOwner mLifecycleOwner;
private PreferenceCategory mPreferenceGroup;
private Preference mAudioSharingPreference;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
@@ -148,6 +152,7 @@ public class AudioSharingDevicePreferenceControllerTest {
BluetoothStatusCodes.FEATURE_SUPPORTED);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mFeatureFactory = FakeFeatureFactory.setupForTest();
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
when(mLocalBtManager.getEventManager()).thenReturn(mEventManager);
@@ -175,6 +180,7 @@ public class AudioSharingDevicePreferenceControllerTest {
.thenReturn(mAudioSharingPreference);
when(mScreen.findPreference(KEY)).thenReturn(mPreferenceGroup);
mController = new AudioSharingDevicePreferenceController(mContext);
mController.init(mFragment);
mController.setBluetoothDeviceUpdater(mBluetoothDeviceUpdater);
mController.setDialogHandler(mDialogHandler);
doReturn(mActivity).when(mFragment).getActivity();
@@ -297,7 +303,7 @@ public class AudioSharingDevicePreferenceControllerTest {
@Test
public void onProfileConnectionStateChanged_notMediaDevice_doNothing() {
doReturn(ImmutableList.of()).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of()).when(mCachedDevice).getUiAccessibleProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice, BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.HID_DEVICE);
verifyNoInteractions(mDialogHandler);
@@ -307,7 +313,7 @@ public class AudioSharingDevicePreferenceControllerTest {
public void onProfileConnectionStateChanged_leaDeviceDisconnected_closeOpeningDialogsForIt() {
// Test when LEA device LE_AUDIO_BROADCAST_ASSISTANT disconnected.
when(mDevice.isConnected()).thenReturn(true);
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getUiAccessibleProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice,
@@ -319,7 +325,7 @@ public class AudioSharingDevicePreferenceControllerTest {
@Test
public void onProfileConnectionStateChanged_assistantProfileConnecting_doNothing() {
// Test when LEA device LE_AUDIO_BROADCAST_ASSISTANT connecting
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getUiAccessibleProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice,
@@ -332,7 +338,7 @@ public class AudioSharingDevicePreferenceControllerTest {
public void onProfileConnectionStateChanged_otherProfileConnected_doNothing() {
// Test when LEA device other profile connected
when(mDevice.isConnected()).thenReturn(true);
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getUiAccessibleProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice, BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
@@ -343,7 +349,7 @@ public class AudioSharingDevicePreferenceControllerTest {
public void onProfileConnectionStateChanged_otherProfileConnecting_doNothing() {
// Test when LEA device other profile connecting
when(mDevice.isConnected()).thenReturn(true);
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getUiAccessibleProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice, BluetoothAdapter.STATE_CONNECTING, BluetoothProfile.A2DP);
@@ -354,7 +360,7 @@ public class AudioSharingDevicePreferenceControllerTest {
public void onProfileConnectionStateChanged_assistantProfileConnected_handle() {
// Test when LEA device LE_AUDIO_BROADCAST_ASSISTANT connected
when(mDevice.isConnected()).thenReturn(true);
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getUiAccessibleProfiles();
doReturn(ImmutableList.of(mLeAudioProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice,
@@ -368,7 +374,7 @@ public class AudioSharingDevicePreferenceControllerTest {
onProfileConnectionStateChanged_nonLeaDeviceDisconnected_closeOpeningDialogsForIt() {
// Test when non-LEA device totally disconnected
when(mLeAudioProfile.isEnabled(mDevice)).thenReturn(false);
doReturn(ImmutableList.of(mA2dpProfile)).when(mCachedDevice).getConnectableProfiles();
doReturn(ImmutableList.of(mA2dpProfile)).when(mCachedDevice).getUiAccessibleProfiles();
doReturn(ImmutableList.of(mLeAudioProfile, mA2dpProfile)).when(mCachedDevice).getProfiles();
when(mCachedDevice.isConnected()).thenReturn(false);
mController.onProfileConnectionStateChanged(
@@ -384,7 +390,7 @@ public class AudioSharingDevicePreferenceControllerTest {
.thenReturn(BluetoothAdapter.STATE_CONNECTED);
doReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile))
.when(mCachedDevice)
.getConnectableProfiles();
.getUiAccessibleProfiles();
doReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice, BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
@@ -399,7 +405,7 @@ public class AudioSharingDevicePreferenceControllerTest {
.thenReturn(BluetoothAdapter.STATE_DISCONNECTED);
doReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile))
.when(mCachedDevice)
.getConnectableProfiles();
.getUiAccessibleProfiles();
doReturn(ImmutableList.of(mA2dpProfile, mHeadsetProfile)).when(mCachedDevice).getProfiles();
mController.onProfileConnectionStateChanged(
mCachedDevice, BluetoothAdapter.STATE_CONNECTED, BluetoothProfile.A2DP);
@@ -526,6 +532,25 @@ public class AudioSharingDevicePreferenceControllerTest {
verify(mBluetoothDeviceUpdater, times(2)).forceUpdate();
}
@Test
public void testBluetoothLeBroadcastAssistantCallbacks_logAction() {
mController.mBroadcastAssistantCallback.onSourceAddFailed(
mDevice, mSource, /* reason= */ 1);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED,
SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY);
mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
mDevice, /* sourceId= */ 1, /* reason= */ 1);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_LEAVE_FAILED,
SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY);
}
@Test
public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
@@ -534,10 +559,6 @@ public class AudioSharingDevicePreferenceControllerTest {
mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceAdded(
mDevice, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceAddFailed(
mDevice, mSource, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
mDevice, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceModified(
mDevice, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceModifyFailed(
@@ -546,7 +567,8 @@ public class AudioSharingDevicePreferenceControllerTest {
mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
shadowOf(Looper.getMainLooper()).idle();
// Above callbacks won't update group preference
// Above callbacks won't update group preference and log actions
verify(mBluetoothDeviceUpdater, never()).forceUpdate();
verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
}
}

View File

@@ -16,6 +16,8 @@
package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingDeviceVolumeControlUpdater.PREF_KEY_PREFIX;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -29,15 +31,11 @@ import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.media.AudioManager;
import android.os.Looper;
import android.provider.Settings;
import android.widget.SeekBar;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
@@ -45,7 +43,6 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.bluetooth.BluetoothDevicePreference;
import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -53,7 +50,6 @@ 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.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -78,14 +74,7 @@ import java.util.List;
@Config(shadows = {ShadowBluetoothUtils.class})
public class AudioSharingDeviceVolumeControlUpdaterTest {
private static final String TEST_DEVICE_NAME = "test";
private static final String TAG = "AudioSharingDeviceVolumeControlUpdater";
private static final String PREF_KEY = "audio_sharing_volume_control";
private static final String TEST_SETTINGS_KEY =
"bluetooth_le_broadcast_fallback_active_group_id";
private static final int TEST_DEVICE_GROUP_ID = 1;
private static final int TEST_VOLUME_VALUE = 255;
private static final int TEST_MAX_STREAM_VALUE = 10;
private static final int TEST_MIN_STREAM_VALUE = 0;
private static final String TAG = "AudioSharingVolUpdater";
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -97,39 +86,32 @@ public class AudioSharingDeviceVolumeControlUpdaterTest {
@Mock private LocalBluetoothProfileManager mLocalBtProfileManager;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private VolumeControlProfile mVolumeControl;
@Mock private BluetoothLeBroadcastReceiveState mState;
@Mock private AudioManager mAudioManager;
private Context mContext;
private AudioSharingDeviceVolumeControlUpdater mDeviceUpdater;
private Collection<CachedBluetoothDevice> mCachedDevices;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
mContext = ApplicationProvider.getApplicationContext();
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
when(mLocalBtProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(mState.getBisSyncState()).thenReturn(bisSyncState);
doReturn(TEST_DEVICE_NAME).when(mCachedBluetoothDevice).getName();
doReturn(mBluetoothDevice).when(mCachedBluetoothDevice).getDevice();
doReturn(ImmutableSet.of()).when(mCachedBluetoothDevice).getMemberDevice();
doReturn(TEST_DEVICE_GROUP_ID).when(mCachedBluetoothDevice).getGroupId();
mCachedDevices = new ArrayList<>();
mCachedDevices.add(mCachedBluetoothDevice);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
doNothing().when(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
doNothing().when(mDevicePreferenceCallback).onDeviceRemoved(any(Preference.class));
when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
mDeviceUpdater =
spy(
new AudioSharingDeviceVolumeControlUpdater(
@@ -249,76 +231,6 @@ public class AudioSharingDeviceVolumeControlUpdaterTest {
.isEqualTo(mCachedBluetoothDevice);
}
@Test
public void addPreference_notFallbackDevice_setDeviceVolume() {
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
setupPreferenceMapWithDevice();
verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture());
assertThat(captor.getValue() instanceof AudioSharingDeviceVolumePreference).isTrue();
AudioSharingDeviceVolumePreference preference =
(AudioSharingDeviceVolumePreference) captor.getValue();
SeekBar seekBar = mock(SeekBar.class);
when(seekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE);
preference.onStopTrackingTouch(seekBar);
verify(mVolumeControl)
.setDeviceVolume(mBluetoothDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME,
/* isPrimary= */ false);
}
@Test
public void addPreference_fallbackDevice_setStreamVolume() {
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
setupPreferenceMapWithDevice();
verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture());
assertThat(captor.getValue() instanceof AudioSharingDeviceVolumePreference).isTrue();
AudioSharingDeviceVolumePreference preference =
(AudioSharingDeviceVolumePreference) captor.getValue();
Settings.Secure.putInt(
mContext.getContentResolver(), TEST_SETTINGS_KEY, TEST_DEVICE_GROUP_ID);
when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.thenReturn(TEST_MAX_STREAM_VALUE);
when(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC))
.thenReturn(TEST_MIN_STREAM_VALUE);
SeekBar seekBar = mock(SeekBar.class);
when(seekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE);
preference.onStopTrackingTouch(seekBar);
verifyNoInteractions(mVolumeControl);
verify(mAudioManager)
.setStreamVolume(AudioManager.STREAM_MUSIC, TEST_MAX_STREAM_VALUE, /* flags= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME,
/* isPrimary= */ true);
}
@Test
public void testOnSeekBarChangeListener_doNothing() {
ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
setupPreferenceMapWithDevice();
verify(mDevicePreferenceCallback).onDeviceAdded(captor.capture());
assertThat(captor.getValue() instanceof AudioSharingDeviceVolumePreference).isTrue();
AudioSharingDeviceVolumePreference preference =
(AudioSharingDeviceVolumePreference) captor.getValue();
SeekBar seekBar = mock(SeekBar.class);
preference.onProgressChanged(seekBar, TEST_VOLUME_VALUE, /* fromUser= */ false);
verifyNoInteractions(mAudioManager);
verifyNoInteractions(mVolumeControl);
}
@Test
public void getLogTag_returnsCorrectTag() {
assertThat(mDeviceUpdater.getLogTag()).isEqualTo(TAG);
@@ -326,7 +238,7 @@ public class AudioSharingDeviceVolumeControlUpdaterTest {
@Test
public void getPreferenceKey_returnsCorrectKey() {
assertThat(mDeviceUpdater.getPreferenceKey()).isEqualTo(PREF_KEY);
assertThat(mDeviceUpdater.getPreferenceKeyPrefix()).isEqualTo(PREF_KEY_PREFIX);
}
@Test

View File

@@ -16,8 +16,6 @@
package com.android.settings.connecteddevice.audiosharing;
import static com.android.settings.connecteddevice.audiosharing.AudioSharingUtils.SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -59,6 +57,7 @@ import com.android.settings.connecteddevice.DevicePreferenceCallback;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -208,7 +207,8 @@ public class AudioSharingDeviceVolumeGroupControllerTest {
.registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class));
verify(mContentResolver, never())
.registerContentObserver(
Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
Settings.Secure.getUriFor(
BluetoothUtils.getPrimaryGroupIdUriForBroadcast()),
false,
mContentObserver);
}
@@ -223,11 +223,9 @@ public class AudioSharingDeviceVolumeGroupControllerTest {
verify(mDeviceUpdater).registerCallback();
verify(mVolumeControl)
.registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class));
verify(mContentResolver)
.registerContentObserver(
Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
false,
mContentObserver);
verify(mContentResolver).registerContentObserver(
Settings.Secure.getUriFor(BluetoothUtils.getPrimaryGroupIdUriForBroadcast()), false,
mContentObserver);
}
@Test
@@ -242,7 +240,8 @@ public class AudioSharingDeviceVolumeGroupControllerTest {
.registerCallback(any(Executor.class), any(BluetoothVolumeControl.Callback.class));
verify(mContentResolver)
.registerContentObserver(
Settings.Secure.getUriFor(SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID),
Settings.Secure.getUriFor(
BluetoothUtils.getPrimaryGroupIdUriForBroadcast()),
false,
mContentObserver);
}
@@ -317,7 +316,8 @@ public class AudioSharingDeviceVolumeGroupControllerTest {
@Test
public void onDeviceAdded_rankFallbackDeviceOnTop() {
Settings.Secure.putInt(
mContentResolver, SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID, TEST_DEVICE_GROUP_ID2);
mContentResolver, BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID2);
when(mPreference1.getProgress()).thenReturn(TEST_VOLUME_VALUE);
when(mPreference2.getProgress()).thenReturn(TEST_VOLUME_VALUE);
mController.setPreferenceGroup(mPreferenceGroup);
@@ -427,7 +427,8 @@ public class AudioSharingDeviceVolumeGroupControllerTest {
@Test
public void settingsObserverOnChange_updatePreferenceOrder() {
Settings.Secure.putInt(
mContentResolver, SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID, TEST_DEVICE_GROUP_ID2);
mContentResolver, BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID2);
when(mPreference1.getProgress()).thenReturn(TEST_VOLUME_VALUE);
when(mPreference2.getProgress()).thenReturn(TEST_VOLUME_VALUE);
mController.setPreferenceGroup(mPreferenceGroup);
@@ -435,8 +436,8 @@ public class AudioSharingDeviceVolumeGroupControllerTest {
mController.onDeviceAdded(mPreference2);
shadowOf(Looper.getMainLooper()).idle();
Settings.Secure.putInt(
mContentResolver, SETTINGS_KEY_FALLBACK_DEVICE_GROUP_ID, TEST_DEVICE_GROUP_ID1);
Settings.Secure.putInt(mContentResolver, BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID1);
mContentObserver.onChange(true);
shadowOf(Looper.getMainLooper()).idle();

View File

@@ -18,11 +18,32 @@ package com.android.settings.connecteddevice.audiosharing;
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.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.media.AudioManager;
import android.provider.Settings;
import android.widget.SeekBar;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.bluetooth.Utils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import org.junit.Before;
import org.junit.Rule;
@@ -32,18 +53,45 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothUtils.class})
public class AudioSharingDeviceVolumePreferenceTest {
private static final int TEST_DEVICE_GROUP_ID = 1;
private static final int TEST_VOLUME_VALUE = 255;
private static final int TEST_MAX_STREAM_VALUE = 10;
private static final int TEST_MIN_STREAM_VALUE = 0;
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private LocalBluetoothProfileManager mLocalBtProfileManager;
@Mock private VolumeControlProfile mVolumeControl;
@Mock private CachedBluetoothDevice mCachedDevice;
@Mock private BluetoothDevice mDevice;
@Mock private AudioManager mAudioManager;
@Mock private SeekBar mSeekBar;
private Context mContext;
private AudioSharingDeviceVolumePreference mPreference;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setup() {
mContext = ApplicationProvider.getApplicationContext();
mContext = spy(ApplicationProvider.getApplicationContext());
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
when(mLocalBtProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
.thenReturn(TEST_MAX_STREAM_VALUE);
when(mAudioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC))
.thenReturn(TEST_MIN_STREAM_VALUE);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID);
when(mSeekBar.getProgress()).thenReturn(TEST_VOLUME_VALUE);
mPreference = new AudioSharingDeviceVolumePreference(mContext, mCachedDevice);
}
@@ -58,4 +106,128 @@ public class AudioSharingDeviceVolumePreferenceTest {
assertThat(mPreference.getMax()).isEqualTo(AudioSharingDeviceVolumePreference.MAX_VOLUME);
assertThat(mPreference.getMin()).isEqualTo(AudioSharingDeviceVolumePreference.MIN_VOLUME);
}
@Test
public void onStopTrackingTouch_notFallbackDevice_setDeviceVolume() {
mPreference.onStopTrackingTouch(mSeekBar);
verify(mVolumeControl).setDeviceVolume(mDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME,
/* isPrimary= */ false);
}
@Test
public void onProgressChanged_notFallbackDevice_fromUserNotInTouch_setDeviceVolume() {
mPreference.onProgressChanged(mSeekBar, TEST_VOLUME_VALUE, /* fromUser= */ true);
verify(mVolumeControl).setDeviceVolume(mDevice, TEST_VOLUME_VALUE, /* isGroupOp= */ true);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME,
/* isPrimary= */ false);
}
@Test
public void onProgressChanged_notFallbackDevice_fromUserInTouch_doNothing() {
mPreference.onStartTrackingTouch(mSeekBar);
mPreference.onProgressChanged(mSeekBar, TEST_VOLUME_VALUE, /* fromUser= */ true);
verifyNoInteractions(mVolumeControl);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider, never())
.action(
any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME),
anyBoolean());
}
@Test
public void onProgressChanged_notFallbackDevice_notFromUserNotInTouch_doNothing() {
mPreference.onProgressChanged(mSeekBar, TEST_VOLUME_VALUE, /* fromUser= */ false);
verifyNoInteractions(mVolumeControl);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider, never())
.action(
any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME),
anyBoolean());
}
@Test
public void onStopTrackingTouch_fallbackDevice_setDeviceVolume() {
Settings.Secure.putInt(
mContext.getContentResolver(),
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID);
mPreference.onStopTrackingTouch(mSeekBar);
verifyNoInteractions(mVolumeControl);
verify(mAudioManager)
.setStreamVolume(AudioManager.STREAM_MUSIC, TEST_MAX_STREAM_VALUE, /* flags= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME,
/* isPrimary= */ true);
}
@Test
public void onProgressChanged_fallbackDevice_fromUserNotInTouch_setDeviceVolume() {
Settings.Secure.putInt(
mContext.getContentResolver(),
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID);
mPreference.onProgressChanged(mSeekBar, TEST_VOLUME_VALUE, /* fromUser= */ true);
verifyNoInteractions(mVolumeControl);
verify(mAudioManager)
.setStreamVolume(AudioManager.STREAM_MUSIC, TEST_MAX_STREAM_VALUE, /* flags= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME,
/* isPrimary= */ true);
}
@Test
public void onProgressChanged_fallbackDevice_fromUserInTouch_doNothing() {
Settings.Secure.putInt(
mContext.getContentResolver(),
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID);
mPreference.onStartTrackingTouch(mSeekBar);
mPreference.onProgressChanged(mSeekBar, TEST_VOLUME_VALUE, /* fromUser= */ true);
verifyNoInteractions(mVolumeControl);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider, never())
.action(
any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME),
anyBoolean());
}
@Test
public void onProgressChanged_fallbackDevice_notFromUserNotInTouch_doNothing() {
Settings.Secure.putInt(
mContext.getContentResolver(),
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
TEST_DEVICE_GROUP_ID);
mPreference.onProgressChanged(mSeekBar, TEST_VOLUME_VALUE, /* fromUser= */ false);
verifyNoInteractions(mVolumeControl);
verifyNoInteractions(mAudioManager);
verify(mFeatureFactory.metricsFeatureProvider, never())
.action(
any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_CHANGE_MEDIA_DEVICE_VOLUME),
anyBoolean());
}
}

View File

@@ -34,6 +34,7 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
@@ -82,18 +83,12 @@ public class AudioSharingDialogFragmentTest {
new AudioSharingDeviceItem(TEST_DEVICE_NAME3, /* groupId= */ 3, /* isActive= */ false);
private static final AudioSharingDialogFragment.DialogEventListener EMPTY_EVENT_LISTENER =
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onItemClick(AudioSharingDeviceItem item) {}
@Override
public void onCancelClick() {}
};
private static final Pair<Integer, Object> TEST_EVENT_DATA = Pair.create(1, 1);
private static final Pair<Integer, Object>[] TEST_EVENT_DATA_LIST =
new Pair[] {TEST_EVENT_DATA};
private Fragment mParent;
private AudioSharingDialogFragment mFragment;
private FakeFeatureFactory mFeatureFactory;
@Before
@@ -107,7 +102,6 @@ public class AudioSharingDialogFragmentTest {
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mFragment = new AudioSharingDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
@@ -120,7 +114,8 @@ public class AudioSharingDialogFragmentTest {
@Test
public void getMetricsCategory_correctValue() {
assertThat(mFragment.getMetricsCategory())
AudioSharingDialogFragment fragment = new AudioSharingDialogFragment();
assertThat(fragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE);
}
@@ -130,13 +125,22 @@ public class AudioSharingDialogFragmentTest {
AudioSharingDialogFragment.show(
mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_noConnectedDevice() {
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingDialogFragment.show(
new Fragment(), new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_noExtraConnectedDevice() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingDialogFragment.show(
mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
@@ -148,42 +152,87 @@ public class AudioSharingDialogFragmentTest {
assertThat(description).isNotNull();
ImageView image = dialog.findViewById(R.id.description_image);
assertThat(image).isNotNull();
Button shareBtn = dialog.findViewById(R.id.positive_btn);
assertThat(shareBtn).isNotNull();
Button cancelBtn = dialog.findViewById(R.id.negative_btn);
assertThat(cancelBtn).isNotNull();
Button positiveBtn = dialog.findViewById(R.id.positive_btn);
assertThat(positiveBtn).isNotNull();
Button negativeBtn = dialog.findViewById(R.id.negative_btn);
assertThat(negativeBtn).isNotNull();
assertThat(dialog.isShowing()).isTrue();
assertThat(description.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(description.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_dialog_connect_device_content));
assertThat(image.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE);
assertThat(cancelBtn.getVisibility()).isEqualTo(View.GONE);
assertThat(positiveBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(positiveBtn.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_pair_button_label));
assertThat(negativeBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(negativeBtn.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_qrcode_button_label));
}
@Test
public void onCreateDialog_noConnectedDevice_dialogDismiss() {
public void onCreateDialog_noExtraConnectedDevice_pairNewDevice() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AtomicBoolean isPairBtnClicked = new AtomicBoolean(false);
AudioSharingDialogFragment.show(
mParent, new ArrayList<>(), EMPTY_EVENT_LISTENER, TEST_EVENT_DATA_LIST);
mParent,
new ArrayList<>(),
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onPositiveClick() {
isPairBtnClicked.set(true);
}
},
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
View btnView = dialog.findViewById(android.R.id.button2);
assertThat(btnView).isNotNull();
btnView.performClick();
Button pairBtn = dialog.findViewById(R.id.positive_btn);
assertThat(pairBtn).isNotNull();
pairBtn.performClick();
shadowMainLooper().idle();
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_POSITIVE_BTN_CLICKED),
eq(TEST_EVENT_DATA));
assertThat(isPairBtnClicked.get()).isTrue();
assertThat(dialog.isShowing()).isFalse();
}
@Test
public void onCreateDialog_noExtraConnectedDevice_showQRCode() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AtomicBoolean isQrCodeBtnClicked = new AtomicBoolean(false);
AudioSharingDialogFragment.show(
mParent,
new ArrayList<>(),
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onCancelClick() {
isQrCodeBtnClicked.set(true);
}
},
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
Button qrCodeBtn = dialog.findViewById(R.id.negative_btn);
assertThat(qrCodeBtn).isNotNull();
qrCodeBtn.performClick();
shadowMainLooper().idle();
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(Context.class),
eq(SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_NEGATIVE_BTN_CLICKED),
eq(TEST_EVENT_DATA));
assertThat(isQrCodeBtnClicked.get()).isTrue();
assertThat(dialog.isShowing()).isFalse();
}
@Test
public void onCreateDialog_flagOn_singleConnectedDevice() {
public void onCreateDialog_flagOn_singleExtraConnectedDevice() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
@@ -198,10 +247,10 @@ public class AudioSharingDialogFragmentTest {
assertThat(description).isNotNull();
ImageView image = dialog.findViewById(R.id.description_image);
assertThat(image).isNotNull();
Button shareBtn = dialog.findViewById(R.id.positive_btn);
assertThat(shareBtn).isNotNull();
Button cancelBtn = dialog.findViewById(R.id.negative_btn);
assertThat(cancelBtn).isNotNull();
Button positiveBtn = dialog.findViewById(R.id.positive_btn);
assertThat(positiveBtn).isNotNull();
Button negativeBtn = dialog.findViewById(R.id.negative_btn);
assertThat(negativeBtn).isNotNull();
assertThat(dialog.isShowing()).isTrue();
assertThat(title.getText().toString())
.isEqualTo(
@@ -211,12 +260,16 @@ public class AudioSharingDialogFragmentTest {
assertThat(description.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_dialog_share_content));
assertThat(image.getVisibility()).isEqualTo(View.GONE);
assertThat(shareBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(cancelBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(positiveBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(positiveBtn.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_share_button_label));
assertThat(negativeBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(negativeBtn.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_no_thanks_button_label));
}
@Test
public void onCreateDialog_singleConnectedDevice_dialogDismiss() {
public void onCreateDialog_singleExtraConnectedDevice_dialogDismiss() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
@@ -239,7 +292,7 @@ public class AudioSharingDialogFragmentTest {
}
@Test
public void onCreateDialog_singleConnectedDevice_shareClicked() {
public void onCreateDialog_singleExtraConnectedDevice_shareClicked() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
@@ -249,12 +302,9 @@ public class AudioSharingDialogFragmentTest {
list,
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onItemClick(AudioSharingDeviceItem item) {
public void onItemClick(@NonNull AudioSharingDeviceItem item) {
isShareBtnClicked.set(true);
}
@Override
public void onCancelClick() {}
},
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
@@ -276,7 +326,7 @@ public class AudioSharingDialogFragmentTest {
}
@Test
public void onCreateDialog_flagOn_multipleConnectedDevice() {
public void onCreateDialog_flagOn_multipleExtraConnectedDevice() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
@@ -304,12 +354,14 @@ public class AudioSharingDialogFragmentTest {
assertThat(image.getVisibility()).isEqualTo(View.GONE);
assertThat(shareBtn.getVisibility()).isEqualTo(View.GONE);
assertThat(cancelBtn.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(cancelBtn.getText().toString())
.isEqualTo(mParent.getString(com.android.settings.R.string.cancel));
assertThat(recyclerView.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(recyclerView.getAdapter().getItemCount()).isEqualTo(3);
}
@Test
public void onCreateDialog_multipleConnectedDevice_dialogDismiss() {
public void onCreateDialog_multipleExtraConnectedDevice_dialogDismiss() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
@@ -320,9 +372,6 @@ public class AudioSharingDialogFragmentTest {
mParent,
list,
new AudioSharingDialogFragment.DialogEventListener() {
@Override
public void onItemClick(AudioSharingDeviceItem item) {}
@Override
public void onCancelClick() {
isCancelBtnClicked.set(true);

View File

@@ -24,7 +24,9 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@@ -34,9 +36,11 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
@@ -45,6 +49,7 @@ import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.SettingsActivity;
import com.android.settings.bluetooth.Utils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
@@ -67,6 +72,7 @@ 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.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -108,6 +114,7 @@ public class AudioSharingDialogHandlerTest {
@Mock private CachedBluetoothDeviceManager mCacheManager;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private AudioManager mAudioManager;
@Mock private CachedBluetoothDevice mCachedDevice1;
@Mock private CachedBluetoothDevice mCachedDevice2;
@Mock private CachedBluetoothDevice mCachedDevice3;
@@ -132,7 +139,7 @@ public class AudioSharingDialogHandlerTest {
FragmentActivity.class,
0 /* containerViewId */,
null /* bundle */);
mContext = mParentFragment.getContext();
mContext = spy(mParentFragment.getContext());
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
ShadowBluetoothAdapter shadowBluetoothAdapter =
@@ -147,6 +154,8 @@ public class AudioSharingDialogHandlerTest {
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_NORMAL);
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(mState.getBisSyncState()).thenReturn(bisSyncState);
@@ -182,13 +191,23 @@ public class AudioSharingDialogHandlerTest {
ShadowBluetoothUtils.reset();
}
@Test
public void handleUserTriggeredDeviceConnected_inCall_setActive() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mCachedDevice1).setActive();
}
@Test
public void handleUserTriggeredNonLeaDeviceConnected_noSharing_setActive() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
@@ -199,9 +218,7 @@ public class AudioSharingDialogHandlerTest {
public void handleUserTriggeredNonLeaDeviceConnected_sharing_showStopDialog() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
@@ -238,22 +255,33 @@ public class AudioSharingDialogHandlerTest {
public void handleUserTriggeredLeaDeviceConnected_noSharingNoTwoLeaDevices_setActive() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mCachedDevice1).setActive();
}
@Test
public void handleUserTriggeredLeaDeviceConnected_noSharingLeaDeviceInErrorState_setActive() {
setUpBroadcast(false);
when(mCachedDevice1.getGroupId()).thenReturn(-1);
when(mLeAudioProfile.getGroupId(mDevice1)).thenReturn(-1);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).isEmpty();
verify(mCachedDevice1).setActive();
}
@Test
public void handleUserTriggeredLeaDeviceConnected_noSharingTwoLeaDevices_showJoinDialog() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
@@ -287,16 +315,26 @@ public class AudioSharingDialogHandlerTest {
AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
assertThat(listener).isNotNull();
listener.onShareClick();
verify(mBroadcast).startPrivateBroadcast();
ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(argumentCaptor.capture());
Intent intent = argumentCaptor.getValue();
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(AudioSharingDashboardFragment.class.getName());
assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS))
.isNotNull();
Bundle args = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
assertThat(args).isNotNull();
assertThat(args.getBoolean(LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING))
.isTrue();
listener.onCancelClick();
verify(mCachedDevice1).setActive();
}
@Test
public void handleUserTriggeredLeaDeviceConnected_sharing_showJoinDialog() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
@@ -330,6 +368,8 @@ public class AudioSharingDialogHandlerTest {
1));
AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
assertThat(listener).isNotNull();
listener.onCancelClick();
verify(mAssistant, never()).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
listener.onShareClick();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
}
@@ -339,9 +379,7 @@ public class AudioSharingDialogHandlerTest {
handleUserTriggeredLeaDeviceConnected_sharingWithTwoLeaDevices_showDisconnectDialog() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3, mDevice4);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
when(mAssistant.getAllSources(mDevice4)).thenReturn(ImmutableList.of(mState));
@@ -381,13 +419,23 @@ public class AudioSharingDialogHandlerTest {
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
}
@Test
public void handleDeviceConnected_inCall_doNothing() {
when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);
setUpBroadcast(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
verify(mCachedDevice2, never()).setActive();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).isEmpty();
}
@Test
public void handleNonLeaDeviceConnected_noSharing_doNothing() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
@@ -397,10 +445,8 @@ public class AudioSharingDialogHandlerTest {
@Test
public void handleNonLeaDeviceConnected_sharing_showStopDialog() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
@@ -437,22 +483,33 @@ public class AudioSharingDialogHandlerTest {
public void handleLeaDeviceConnected_noSharingNoTwoLeaDevices_doNothing() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
verify(mCachedDevice1, never()).setActive();
}
@Test
public void handleLeaDeviceConnected_noSharingLeaDeviceInErrorState_doNothing() {
setUpBroadcast(false);
when(mCachedDevice1.getGroupId()).thenReturn(-1);
when(mLeAudioProfile.getGroupId(mDevice1)).thenReturn(-1);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).isEmpty();
verify(mCachedDevice1, never()).setActive();
}
@Test
public void handleLeaDeviceConnected_noSharingTwoLeaDevices_showJoinDialog() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
@@ -486,16 +543,26 @@ public class AudioSharingDialogHandlerTest {
AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
assertThat(listener).isNotNull();
listener.onShareClick();
verify(mBroadcast).startPrivateBroadcast();
ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(argumentCaptor.capture());
Intent intent = argumentCaptor.getValue();
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(AudioSharingDashboardFragment.class.getName());
assertThat(intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS))
.isNotNull();
Bundle args = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
assertThat(args).isNotNull();
assertThat(args.getBoolean(LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING))
.isTrue();
listener.onCancelClick();
verify(mCachedDevice1, never()).setActive();
}
@Test
public void handleLeaDeviceConnected_sharing_showJoinDialog() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
@@ -529,6 +596,8 @@ public class AudioSharingDialogHandlerTest {
1));
AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
assertThat(listener).isNotNull();
listener.onCancelClick();
verify(mAssistant, never()).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
listener.onShareClick();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
}
@@ -537,9 +606,7 @@ public class AudioSharingDialogHandlerTest {
public void handleLeaDeviceConnected_sharingWithTwoLeaDevices_showDisconnectDialog() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3, mDevice4);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
when(mAssistant.getAllSources(mDevice4)).thenReturn(ImmutableList.of(mState));
@@ -584,9 +651,7 @@ public class AudioSharingDialogHandlerTest {
// Show join dialog
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
@@ -604,14 +669,55 @@ public class AudioSharingDialogHandlerTest {
SettingsEnums.DIALOG_START_AUDIO_SHARING);
}
@Test
public void closeOpeningDialogsForLeaDevice_unattachedFragment_doNothing() {
mParentFragment = new Fragment();
mHandler = new AudioSharingDialogHandler(mContext, mParentFragment);
mHandler.closeOpeningDialogsForLeaDevice(mCachedDevice1);
shadowOf(Looper.getMainLooper()).idle();
verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
}
@Test
public void closeOpeningDialogsForLeaDevice_closeDisconnectDialog() {
// Show disconnect dialog
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3, mDevice4);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
when(mAssistant.getAllSources(mDevice4)).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mParentFragment.getChildFragmentManager().getFragments())
.comparingElementsUsing(TAG_EQUALS)
.containsExactly(AudioSharingDisconnectDialogFragment.tag());
// Close opening dialogs
mHandler.closeOpeningDialogsForLeaDevice(mCachedDevice1);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mParentFragment.getChildFragmentManager().getFragments()).isEmpty();
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_DIALOG_AUTO_DISMISS,
SettingsEnums.DIALOG_AUDIO_SHARING_SWITCH_DEVICE);
}
@Test
public void closeOpeningDialogsForNonLeaDevice_unattachedFragment_doNothing() {
mParentFragment = new Fragment();
mHandler = new AudioSharingDialogHandler(mContext, mParentFragment);
mHandler.closeOpeningDialogsForNonLeaDevice(mCachedDevice2);
shadowOf(Looper.getMainLooper()).idle();
verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
}
@Test
public void closeOpeningDialogsForNonLeaDevice_closeStopDialog() {
// Show stop dialog
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice2);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
@@ -633,9 +739,7 @@ public class AudioSharingDialogHandlerTest {
public void closeOpeningDialogsOtherThan() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice3)).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ true);
shadowOf(Looper.getMainLooper()).idle();
@@ -645,9 +749,7 @@ public class AudioSharingDialogHandlerTest {
.containsExactly(AudioSharingStopDialogFragment.tag());
deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
@@ -678,43 +780,40 @@ public class AudioSharingDialogHandlerTest {
}
@Test
public void onPlaybackStarted_addSource() {
setUpBroadcast(false);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice3);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of());
mHandler.handleDeviceConnected(mCachedDevice1, /* userTriggered= */ true);
public void onBroadcastStopFailed_logAction() {
setUpBroadcast(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1);
when(mAssistant.getAllConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mHandler.handleDeviceConnected(mCachedDevice2, /* userTriggered= */ false);
shadowOf(Looper.getMainLooper()).idle();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments)
.comparingElementsUsing(TAG_EQUALS)
.containsExactly(AudioSharingJoinDialogFragment.tag());
AudioSharingJoinDialogFragment fragment =
(AudioSharingJoinDialogFragment) Iterables.getOnlyElement(childFragments);
AudioSharingJoinDialogFragment.DialogEventListener listener = fragment.getListener();
.containsExactly(AudioSharingStopDialogFragment.tag());
AudioSharingStopDialogFragment fragment =
(AudioSharingStopDialogFragment) Iterables.getOnlyElement(childFragments);
AudioSharingStopDialogFragment.DialogEventListener listener = fragment.getListener();
assertThat(listener).isNotNull();
listener.onShareClick();
listener.onStopSharingClick();
setUpBroadcast(true);
mHandler.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
verify(mAssistant).addSource(mDevice3, mMetadata, /* isGroupOp= */ false);
mHandler.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 1);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_STOP_FAILED,
SettingsEnums.SETTINGS_CONNECTED_DEVICE_CATEGORY);
}
@Test
public void testBluetoothLeBroadcastCallbacks_doNothing() {
mHandler.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1);
mHandler.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1);
mHandler.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
mHandler.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata);
mHandler.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1);
mHandler.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
mHandler.mBroadcastCallback.onPlaybackStopped(/* reason= */ 1, /* broadcastId= */ 1);
mHandler.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
mHandler.mBroadcastCallback.onBroadcastStopFailed(/* reason= */ 1);
mHandler.mBroadcastCallback.onBroadcastUpdateFailed(/* reason= */ 1, /* broadcastId= */ 1);
verify(mAssistant, never())
@@ -723,6 +822,7 @@ public class AudioSharingDialogHandlerTest {
any(BluetoothLeBroadcastMetadata.class),
anyBoolean());
verify(mAssistant, never()).removeSource(any(BluetoothDevice.class), anyInt());
verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
}
private void setUpBroadcast(boolean isBroadcasting) {

View File

@@ -153,6 +153,23 @@ public class AudioSharingDisconnectDialogFragmentTest {
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mDeviceItems = new ArrayList<>();
mDeviceItems.add(TEST_DEVICE_ITEM1);
mDeviceItems.add(TEST_DEVICE_ITEM2);
AudioSharingDisconnectDialogFragment.show(
new Fragment(),
mDeviceItems,
mCachedDevice3,
EMPTY_EVENT_LISTENER,
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_dialogShowBtnForTwoDevices() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);

View File

@@ -0,0 +1,132 @@
/*
* 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.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.View;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.flags.Flags;
import org.junit.After;
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;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.androidx.fragment.FragmentController;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowAlertDialogCompat.class,
ShadowBluetoothAdapter.class,
})
public class AudioSharingErrorDialogFragmentTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Fragment mParent;
private AudioSharingErrorDialogFragment mFragment;
@Before
public void setUp() {
ShadowAlertDialogCompat.reset();
ShadowBluetoothAdapter shadowBluetoothAdapter =
Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mFragment = new AudioSharingErrorDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
}
@After
public void tearDown() {
ShadowAlertDialogCompat.reset();
}
@Test
public void getMetricsCategory_correctValue() {
// TODO: update metrics id
assertThat(mFragment.getMetricsCategory())
.isEqualTo(0);
}
@Test
public void onCreateDialog_flagOff_dialogNotExist() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingErrorDialogFragment.show(mParent);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingErrorDialogFragment.show(new Fragment());
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_showDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingErrorDialogFragment.show(mParent);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
}
@Test
public void onCreateDialog_clickOk_dialogDismiss() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingErrorDialogFragment.show(mParent);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
View btnView = dialog.findViewById(android.R.id.button1);
assertThat(btnView).isNotNull();
btnView.performClick();
shadowMainLooper().idle();
assertThat(dialog.isShowing()).isFalse();
}
}

View File

@@ -0,0 +1,146 @@
/*
* 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.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.flags.Flags;
import org.junit.After;
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;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.androidx.fragment.FragmentController;
import java.util.concurrent.atomic.AtomicBoolean;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowAlertDialogCompat.class, ShadowBluetoothAdapter.class})
public class AudioSharingIncompatibleDialogFragmentTest {
private static final String TEST_DEVICE_NAME = "test";
private static final AudioSharingIncompatibleDialogFragment.DialogEventListener
EMPTY_EVENT_LISTENER = () -> {};
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Fragment mParent;
private AudioSharingIncompatibleDialogFragment mFragment;
@Before
public void setUp() {
ShadowAlertDialogCompat.reset();
ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mFragment = new AudioSharingIncompatibleDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(mParent, FragmentActivity.class, /* containerViewId= */
0, /* bundle= */ null);
}
@After
public void tearDown() {
ShadowAlertDialogCompat.reset();
}
@Test
public void getMetricsCategory_correctValue() {
// TODO: update to real metrics id
assertThat(mFragment.getMetricsCategory()).isEqualTo(0);
}
@Test
public void onCreateDialog_flagOff_dialogNotExist() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingIncompatibleDialogFragment.show(mParent, TEST_DEVICE_NAME,
EMPTY_EVENT_LISTENER);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingIncompatibleDialogFragment.show(new Fragment(), TEST_DEVICE_NAME,
EMPTY_EVENT_LISTENER);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_showDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingIncompatibleDialogFragment.show(mParent, TEST_DEVICE_NAME,
EMPTY_EVENT_LISTENER);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
TextView title = dialog.findViewById(R.id.title_text);
assertThat(title).isNotNull();
// TODO: use string res
assertThat(title.getText().toString()).isEqualTo(
"Can't share audio with " + TEST_DEVICE_NAME);
}
@Test
public void onCreateDialog_clickBtn_callbackTriggered() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AtomicBoolean isBtnClicked = new AtomicBoolean(false);
AudioSharingIncompatibleDialogFragment.show(mParent, TEST_DEVICE_NAME,
() -> isBtnClicked.set(true));
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
View btnView = dialog.findViewById(android.R.id.button1);
assertThat(btnView).isNotNull();
btnView.performClick();
shadowMainLooper().idle();
assertThat(dialog.isShowing()).isFalse();
assertThat(isBtnClicked.get()).isTrue();
}
}

View File

@@ -31,6 +31,7 @@ import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
@@ -163,9 +164,24 @@ public class AudioSharingJoinDialogFragmentTest {
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingJoinDialogFragment.show(
new Fragment(),
new ArrayList<>(),
mCachedDevice2,
EMPTY_EVENT_LISTENER,
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_dialogShowTextForSingleDevice() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.isEnabled(null)).thenReturn(true);
AudioSharingJoinDialogFragment.show(
mParent,
new ArrayList<>(),
@@ -178,6 +194,10 @@ public class AudioSharingJoinDialogFragmentTest {
assertThat(dialog.isShowing()).isTrue();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
assertThat(shadowDialog.getMessage().toString()).isEqualTo(TEST_DEVICE_NAME2);
Button btnView = dialog.findViewById(R.id.negative_btn);
assertThat(btnView).isNotNull();
assertThat(btnView.getText().toString())
.isEqualTo(mParent.getString(R.string.audio_sharing_no_thanks_button_label));
}
@Test
@@ -198,6 +218,13 @@ public class AudioSharingJoinDialogFragmentTest {
R.string.audio_sharing_share_dialog_subtitle,
TEST_DEVICE_NAME1,
TEST_DEVICE_NAME2));
Button btnView = dialog.findViewById(R.id.negative_btn);
assertThat(btnView).isNotNull();
assertThat(btnView.getText().toString())
.isEqualTo(
mParent.getString(
R.string.audio_sharing_switch_active_button_label,
TEST_DEVICE_NAME2));
}
@Test

View File

@@ -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();
}
}

View File

@@ -0,0 +1,142 @@
/*
* 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(shareButton.getContentDescription()).isNotNull();
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);
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.platform.test.flag.junit.SetFlagsRule;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.flags.Flags;
import org.junit.After;
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;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.androidx.fragment.FragmentController;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowAlertDialogCompat.class,
ShadowBluetoothAdapter.class,
})
public class AudioSharingProgressDialogFragmentTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String TEST_MESSAGE1 = "message1";
private static final String TEST_MESSAGE2 = "message2";
private Fragment mParent;
private AudioSharingProgressDialogFragment mFragment;
@Before
public void setUp() {
ShadowAlertDialogCompat.reset();
ShadowBluetoothAdapter shadowBluetoothAdapter =
Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mFragment = new AudioSharingProgressDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
}
@After
public void tearDown() {
ShadowAlertDialogCompat.reset();
}
@Test
public void getMetricsCategory_correctValue() {
// TODO: update real metric
assertThat(mFragment.getMetricsCategory())
.isEqualTo(0);
}
@Test
public void onCreateDialog_flagOff_dialogNotExist() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingProgressDialogFragment.show(new Fragment(), TEST_MESSAGE1);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_flagOn_showDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
TextView view = dialog.findViewById(R.id.message);
assertThat(view).isNotNull();
assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE1);
}
@Test
public void dismissDialog_succeed() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
AudioSharingProgressDialogFragment.dismiss(mParent);
shadowMainLooper().idle();
assertThat(dialog.isShowing()).isFalse();
}
@Test
public void showDialog_sameMessage_keepExistingDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1);
shadowMainLooper().idle();
assertThat(dialog.isShowing()).isTrue();
}
@Test
public void showDialog_newMessage_keepAndUpdateDialog() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE1);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
TextView view = dialog.findViewById(R.id.message);
assertThat(view).isNotNull();
assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE1);
AudioSharingProgressDialogFragment.show(mParent, TEST_MESSAGE2);
shadowMainLooper().idle();
assertThat(dialog.isShowing()).isTrue();
assertThat(view.getText().toString()).isEqualTo(TEST_MESSAGE2);
}
}

View File

@@ -25,7 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@@ -164,8 +164,7 @@ public class AudioSharingReceiverTest {
AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
audioSharingReceiver.onReceive(mContext, intent);
verify(mNm, times(1))
.notify(eq(R.drawable.ic_bt_le_audio_sharing), any(Notification.class));
verify(mNm).notify(eq(R.drawable.ic_bt_le_audio_sharing), any(Notification.class));
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_SHOW_AUDIO_SHARING_NOTIFICATION);
}
@@ -181,7 +180,7 @@ public class AudioSharingReceiverTest {
AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
audioSharingReceiver.onReceive(mContext, intent);
verify(mNm, times(1)).cancel(R.drawable.ic_bt_le_audio_sharing);
verify(mNm).cancel(R.drawable.ic_bt_le_audio_sharing);
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION);
}
@@ -199,8 +198,10 @@ public class AudioSharingReceiverTest {
}
@Test
public void broadcastReceiver_receiveAudioSharingStopIntent_stopBroadcast() {
public void
broadcastReceiver_receiveAudioSharingStopIntent_notInBroadcast_cancelNotification() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.isEnabled(null)).thenReturn(false);
int broadcastId = 1;
when(mBroadcast.getLatestBroadcastId()).thenReturn(broadcastId);
@@ -209,7 +210,25 @@ public class AudioSharingReceiverTest {
AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
audioSharingReceiver.onReceive(mContext, intent);
verify(mBroadcast, times(1)).stopBroadcast(broadcastId);
verify(mBroadcast, never()).stopBroadcast(broadcastId);
verify(mNm).cancel(R.drawable.ic_bt_le_audio_sharing);
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_CANCEL_AUDIO_SHARING_NOTIFICATION);
}
@Test
public void broadcastReceiver_receiveAudioSharingStopIntent_inBroadcast_stopBroadcast() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.isEnabled(null)).thenReturn(true);
int broadcastId = 1;
when(mBroadcast.getLatestBroadcastId()).thenReturn(broadcastId);
Intent intent = new Intent(ACTION_LE_AUDIO_SHARING_STOP);
intent.setPackage(mContext.getPackageName());
AudioSharingReceiver audioSharingReceiver = getAudioSharingReceiver(intent);
audioSharingReceiver.onReceive(mContext, intent);
verify(mBroadcast).stopBroadcast(broadcastId);
verify(mFeatureFactory.metricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_STOP_AUDIO_SHARING_FROM_NOTIFICATION);
}

View File

@@ -148,6 +148,20 @@ public class AudioSharingStopDialogFragmentTest {
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_unattachedFragment_dialogNotExist() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
AudioSharingStopDialogFragment.show(
new Fragment(),
ImmutableList.of(TEST_DEVICE_ITEM2),
mCachedDevice1,
EMPTY_EVENT_LISTENER,
TEST_EVENT_DATA_LIST);
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
public void onCreateDialog_oneDeviceInSharing_showDialogWithCorrectMessage() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);

View File

@@ -18,6 +18,7 @@ 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.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_START_LE_AUDIO_SHARING;
import static com.google.common.truth.Truth.assertThat;
@@ -30,8 +31,10 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
@@ -39,31 +42,43 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcast;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Looper;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.CompoundButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.bluetooth.Utils;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -98,21 +113,25 @@ import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
ShadowThreadUtils.class,
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
ShadowThreadUtils.class,
ShadowAlertDialogCompat.class
})
public class AudioSharingSwitchBarControllerTest {
private static final String TEST_DEVICE_NAME1 = "test1";
private static final String TEST_DEVICE_NAME2 = "test2";
private static final String TEST_DEVICE_ANONYMIZED_ADDR1 = "XX:XX:01";
private static final String TEST_DEVICE_ANONYMIZED_ADDR2 = "XX:XX:02";
private static final int TEST_DEVICE_GROUP_ID1 = 1;
private static final int TEST_DEVICE_GROUP_ID2 = 2;
private static final Correspondence<Fragment, String> TAG_EQUALS =
private static final Correspondence<Fragment, String> CLAZZNAME_EQUALS =
Correspondence.from(
(Fragment fragment, String tag) ->
(Fragment fragment, String clazzName) ->
fragment instanceof DialogFragment
&& ((DialogFragment) fragment).getTag() != null
&& ((DialogFragment) fragment).getTag().equals(tag),
&& ((DialogFragment) fragment).getClass().getName() != null
&& ((DialogFragment) fragment).getClass().getName().equals(
clazzName),
"is equal to");
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -121,10 +140,13 @@ public class AudioSharingSwitchBarControllerTest {
@Spy Context mContext = ApplicationProvider.getApplicationContext();
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private CachedBluetoothDeviceManager mDeviceManager;
@Mock private BluetoothEventManager mEventManager;
@Mock private LocalBluetoothProfileManager mBtProfileManager;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private VolumeControlProfile mVolumeControl;
@Mock private BluetoothLeBroadcastMetadata mMetadata;
@Mock private BluetoothLeBroadcastReceiveState mState;
@Mock private CompoundButton mBtnView;
@Mock private CachedBluetoothDevice mCachedDevice1;
@Mock private CachedBluetoothDevice mCachedDevice2;
@@ -155,11 +177,14 @@ public class AudioSharingSwitchBarControllerTest {
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(localBluetoothManager.getProfileManager()).thenReturn(mBtProfileManager);
when(localBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(localBluetoothManager.getEventManager()).thenReturn(mEventManager);
when(mDevice1.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ANONYMIZED_ADDR1);
when(mDeviceManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
when(mCachedDevice1.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID1);
when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
when(mDevice2.getAnonymizedAddress()).thenReturn(TEST_DEVICE_ANONYMIZED_ADDR2);
when(mDeviceManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
when(mCachedDevice2.getGroupId()).thenReturn(TEST_DEVICE_GROUP_ID2);
@@ -213,6 +238,7 @@ public class AudioSharingSwitchBarControllerTest {
@After
public void tearDown() {
ShadowAlertDialogCompat.reset();
ShadowBluetoothUtils.reset();
ShadowThreadUtils.reset();
}
@@ -295,6 +321,7 @@ public class AudioSharingSwitchBarControllerTest {
verify(mAssistant, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class));
verify(mBtProfileManager).addServiceListener(mController);
assertThat(mSwitchBar.isChecked()).isFalse();
assertThat(mSwitchBar.isEnabled()).isFalse();
@@ -315,11 +342,24 @@ public class AudioSharingSwitchBarControllerTest {
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
verify(mEventManager).registerCallback(any(BluetoothCallback.class));
verify(mBtProfileManager, never()).addServiceListener(mController);
assertThat(mSwitchBar.isChecked()).isTrue();
assertThat(mSwitchBar.isEnabled()).isTrue();
}
@Test
public void onStart_flagOn_updateSwitch() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBroadcast.isEnabled(null)).thenReturn(false);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
mController.onStart(mLifecycleOwner);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isFalse();
assertThat(mSwitchBar.isEnabled()).isTrue();
}
@Test
public void onStop_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
@@ -329,6 +369,7 @@ public class AudioSharingSwitchBarControllerTest {
.unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
verify(mBtProfileManager, never()).removeServiceListener(mController);
}
@@ -345,6 +386,7 @@ public class AudioSharingSwitchBarControllerTest {
.unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
}
@Test
@@ -361,6 +403,7 @@ public class AudioSharingSwitchBarControllerTest {
verify(mBroadcast).unregisterServiceCallBack(any(BluetoothLeBroadcast.Callback.class));
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
verify(mEventManager).unregisterCallback(any(BluetoothCallback.class));
}
@Test
@@ -372,17 +415,23 @@ public class AudioSharingSwitchBarControllerTest {
}
@Test
public void onCheckedChangedToChecked_noConnectedLeaDevices_flagOn_notStartAudioSharing() {
public void onCheckedChangedToChecked_noConnectedLeaDevices_flagOn_showDialog() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of());
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isFalse();
verify(mBroadcast, never()).startPrivateBroadcast();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments)
.comparingElementsUsing(CLAZZNAME_EQUALS)
.containsExactly(AudioSharingConfirmDialogFragment.class.getName());
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
@@ -390,9 +439,7 @@ public class AudioSharingSwitchBarControllerTest {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, false);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of());
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
verify(mBroadcast).startPrivateBroadcast();
@@ -403,9 +450,7 @@ public class AudioSharingSwitchBarControllerTest {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice1));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
verify(mBroadcast).startPrivateBroadcast();
@@ -431,30 +476,187 @@ public class AudioSharingSwitchBarControllerTest {
}
@Test
public void onPlaybackStarted_showJoinAudioSharingDialog() {
public void onPlaybackStarted_notInit_noDialog() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getDevicesMatchingConnectionStates(
new int[] {BluetoothProfile.STATE_CONNECTED}))
.thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
doNothing().when(mBroadcast).startPrivateBroadcast();
mController =
new AudioSharingSwitchBarController(
mContext,
mSwitchBar,
new AudioSharingSwitchBarController.OnAudioSharingStateChangedListener() {
@Override
public void onAudioSharingStateChanged() {}
@Override
public void onAudioSharingProfilesConnected() {}
});
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
// No progress dialog.
assertThat(childFragments).isEmpty();
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments)
.comparingElementsUsing(TAG_EQUALS)
.containsExactly(AudioSharingDialogFragment.tag());
childFragments = mParentFragment.getChildFragmentManager().getFragments();
// No audio sharing dialog.
assertThat(childFragments).isEmpty();
}
AudioSharingDialogFragment fragment =
(AudioSharingDialogFragment) Iterables.getOnlyElement(childFragments);
Pair<Integer, Object>[] eventData = fragment.getEventData();
@Test
public void onPlaybackStarted_hasLocalSource_noDialog() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mState.getBroadcastId()).thenReturn(1);
when(mBroadcast.getLatestBroadcastId()).thenReturn(1);
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(mState));
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant, never()).addSource(any(), any(), anyBoolean());
verify(mFeatureFactory.metricsFeatureProvider, never())
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
childFragments = mParentFragment.getChildFragmentManager().getFragments();
// No audio sharing dialog.
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).doesNotContain(
AudioSharingDialogFragment.class.getName());
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void onPlaybackStarted_singleActiveDevice_showJoinAudioSharingDialog() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2));
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L));
mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice2, /* sourceId= */ 1,
mState);
shadowOf(Looper.getMainLooper()).idle();
childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments)
.comparingElementsUsing(CLAZZNAME_EQUALS)
.containsExactly(AudioSharingDialogFragment.class.getName());
Pair<Integer, Object>[] eventData = new Pair[0];
for (Fragment fragment : childFragments) {
if (fragment instanceof AudioSharingDialogFragment) {
eventData = ((AudioSharingDialogFragment) fragment).getEventData();
break;
}
}
assertThat(eventData)
.asList()
.containsExactly(
Pair.create(
AudioSharingUtils.MetricKey.METRIC_KEY_SOURCE_PAGE_ID.ordinal(),
SettingsEnums.AUDIO_SHARING_SETTINGS),
Pair.create(
AudioSharingUtils.MetricKey.METRIC_KEY_PAGE_ID.ordinal(),
SettingsEnums.DIALOG_AUDIO_SHARING_ADD_DEVICE),
Pair.create(
AudioSharingUtils.MetricKey.METRIC_KEY_USER_TRIGGERED.ordinal(), 0),
Pair.create(
AudioSharingUtils.MetricKey.METRIC_KEY_DEVICE_COUNT_IN_SHARING
.ordinal(),
1),
Pair.create(
AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
.ordinal(),
0));
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void onPlaybackStarted_oneActiveOnConnected_showJoinAudioSharingDialog() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
AudioSharingProgressDialogFragment progressFragment =
(AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments);
// TODO: use string res once finalized
String expectedMessage = "Starting audio stream...";
checkProgressDialogMessage(progressFragment, expectedMessage);
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
// TODO: use string res once finalized
expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "...";
checkProgressDialogMessage(progressFragment, expectedMessage);
childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments)
.comparingElementsUsing(CLAZZNAME_EQUALS)
.containsExactly(AudioSharingDialogFragment.class.getName(),
AudioSharingProgressDialogFragment.class.getName());
Pair<Integer, Object>[] eventData = new Pair[0];
for (Fragment fragment : childFragments) {
if (fragment instanceof AudioSharingDialogFragment) {
eventData = ((AudioSharingDialogFragment) fragment).getEventData();
break;
}
}
assertThat(eventData)
.asList()
.containsExactly(
@@ -474,22 +676,116 @@ public class AudioSharingSwitchBarControllerTest {
AudioSharingUtils.MetricKey.METRIC_KEY_CANDIDATE_DEVICE_COUNT
.ordinal(),
1));
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void testBluetoothLeBroadcastCallbacks_updateSwitch() {
public void onPlaybackStarted_oneActiveOnConnected_clickShareBtnOnDialog_addSource() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
View btnView = dialog.findViewById(R.id.positive_btn);
assertThat(btnView).isNotNull();
btnView.performClick();
shadowMainLooper().idle();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
assertThat(dialog.isShowing()).isFalse();
// Progress dialog shows sharing progress for the user chosen sink.
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
AudioSharingProgressDialogFragment progressFragment =
(AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments);
// TODO: use string res once finalized
String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "...";
checkProgressDialogMessage(progressFragment, expectedMessage);
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void onPlaybackStarted_oneActiveOnConnected_clickCancelBtnOnDialog_doNothing() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
View btnView = dialog.findViewById(R.id.negative_btn);
assertThat(btnView).isNotNull();
btnView.performClick();
shadowMainLooper().idle();
verify(mAssistant, never()).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
assertThat(dialog.isShowing()).isFalse();
// Progress dialog shows sharing progress for the auto add active sink.
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
AudioSharingProgressDialogFragment progressFragment =
(AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments);
// TODO: use string res once finalized
String expectedMessage = "Sharing with " + TEST_DEVICE_NAME2 + "...";
checkProgressDialogMessage(progressFragment, expectedMessage);
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void testBroadcastCallbacks_updateSwitch() {
mOnAudioSharingStateChanged = false;
mSwitchBar.setChecked(false);
when(mBroadcast.isEnabled(any())).thenReturn(false);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedDevice1, mCachedDevice2));
mController.mBroadcastCallback.onBroadcastStartFailed(/* reason= */ 1);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isFalse();
assertThat(mSwitchBar.isEnabled()).isTrue();
assertThat(mOnAudioSharingStateChanged).isFalse();
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_START_FAILED,
SettingsEnums.AUDIO_SHARING_SETTINGS);
when(mBroadcast.isEnabled(any())).thenReturn(true);
mController.mBroadcastCallback.onBroadcastStarted(/* reason= */ 1, /* broadcastId= */ 1);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isTrue();
assertThat(mSwitchBar.isEnabled()).isTrue();
assertThat(mOnAudioSharingStateChanged).isTrue();
mOnAudioSharingStateChanged = false;
@@ -497,18 +793,24 @@ public class AudioSharingSwitchBarControllerTest {
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isTrue();
assertThat(mOnAudioSharingStateChanged).isFalse();
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_STOP_FAILED,
SettingsEnums.AUDIO_SHARING_SETTINGS);
when(mBroadcast.isEnabled(any())).thenReturn(false);
when(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
mController.mBroadcastCallback.onBroadcastStopped(/* reason= */ 1, /* broadcastId= */ 1);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isFalse();
assertThat(mSwitchBar.isEnabled()).isFalse();
assertThat(mOnAudioSharingStateChanged).isTrue();
}
@Test
public void testBluetoothLeBroadcastCallbacks_doNothing() {
BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class);
mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, metadata);
public void testBroadcastCallbacks_doNothing() {
mController.mBroadcastCallback.onBroadcastMetadataChanged(/* reason= */ 1, mMetadata);
mController.mBroadcastCallback.onBroadcastUpdated(/* reason= */ 1, /* broadcastId= */ 1);
mController.mBroadcastCallback.onPlaybackStarted(/* reason= */ 1, /* broadcastId= */ 1);
mController.mBroadcastCallback.onPlaybackStopped(/* reason= */ 1, /* broadcastId= */ 1);
@@ -517,4 +819,257 @@ public class AudioSharingSwitchBarControllerTest {
verify(mSwitchBar, never()).setChecked(anyBoolean());
assertThat(mOnAudioSharingStateChanged).isFalse();
}
@Test
public void testAssistantCallbacks_onSourceAddFailed_twoDevices_showErrorAndLogAction() {
FeatureFlagUtils.setEnabled(
mContext, FeatureFlagUtils.SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, true);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mAssistant.getAllSources(any(BluetoothDevice.class))).thenReturn(ImmutableList.of());
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
when(mBroadcast.isEnabled(null)).thenReturn(true);
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
View btnView = dialog.findViewById(R.id.positive_btn);
assertThat(btnView).isNotNull();
btnView.performClick();
shadowMainLooper().idle();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
assertThat(dialog.isShowing()).isFalse();
mController.mBroadcastAssistantCallback.onSourceAddFailed(
mDevice1, mMetadata, /* reason= */ 1);
shadowMainLooper().idle();
// Progress dialog shows sharing progress for the user chosen sink.
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingErrorDialogFragment.class.getName());
verify(mFeatureFactory.metricsFeatureProvider)
.action(
mContext,
SettingsEnums.ACTION_AUDIO_SHARING_JOIN_FAILED,
SettingsEnums.AUDIO_SHARING_SETTINGS);
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void testAssistantCallbacks_onReceiveStateChanged_dismissProgressDialog() {
AudioSharingProgressDialogFragment.show(mParentFragment, TEST_DEVICE_NAME1);
shadowOf(Looper.getMainLooper()).idle();
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
when(mState.getBisSyncState()).thenReturn(ImmutableList.of(1L));
mController.mBroadcastAssistantCallback.onReceiveStateChanged(mDevice1, /* sourceId= */ 1,
mState);
shadowOf(Looper.getMainLooper()).idle();
childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).isEmpty();
}
@Test
public void testAssistantCallbacks_doNothing() {
// Do nothing
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mDevice1, /* sourceId= */ 1, mState);
mController.mBroadcastAssistantCallback.onSearchStarted(/* reason= */ 1);
mController.mBroadcastAssistantCallback.onSearchStartFailed(/* reason= */ 1);
mController.mBroadcastAssistantCallback.onSearchStopped(/* reason= */ 1);
mController.mBroadcastAssistantCallback.onSearchStopFailed(/* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceAdded(
mDevice1, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceRemoved(
mDevice1, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
mDevice1, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceModified(
mDevice1, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceModifyFailed(
mDevice1, /* sourceId= */ 1, /* reason= */ 1);
mController.mBroadcastAssistantCallback.onSourceFound(mMetadata);
mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
verifyNoMoreInteractions(mFeatureFactory.metricsFeatureProvider);
}
@Test
public void onActiveDeviceChanged_leaProfile_updateSwitch() {
mSwitchBar.setChecked(true);
mSwitchBar.setEnabled(false);
when(mBroadcast.isEnabled(null)).thenReturn(false);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedDevice2, mCachedDevice1));
mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.LE_AUDIO);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isFalse();
verify(mSwitchBar).setEnabled(true);
}
@Test
public void onActiveDeviceChanged_a2dpProfile_updateSwitch() {
mSwitchBar.setChecked(true);
mSwitchBar.setEnabled(false);
when(mBroadcast.isEnabled(null)).thenReturn(false);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1, mDevice2));
when(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(false);
when(mCachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(
ImmutableList.of(mCachedDevice1, mCachedDevice2));
mController.onActiveDeviceChanged(mCachedDevice2, BluetoothProfile.A2DP);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mSwitchBar.isChecked()).isFalse();
verify(mSwitchBar).setEnabled(true);
}
@Test
public void onActiveDeviceChanged_nullActiveDevice_doNothing() {
mController.onActiveDeviceChanged(/* activeDevice= */ null, BluetoothProfile.LE_AUDIO);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBar, never()).setEnabled(anyBoolean());
verify(mSwitchBar, never()).setChecked(anyBoolean());
}
@Test
public void testAccessibilityDelegate() {
View view = new View(mContext);
AccessibilityEvent event =
new AccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
assertThat(
mSwitchBar
.getRootView()
.getAccessibilityDelegate()
.onRequestSendAccessibilityEvent(mSwitchBar, view, event))
.isTrue();
event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_ENABLED);
assertThat(
mSwitchBar
.getRootView()
.getAccessibilityDelegate()
.onRequestSendAccessibilityEvent(mSwitchBar, view, event))
.isFalse();
}
@Test
public void handleStartAudioSharingFromIntent_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
var unused = setUpFragmentWithStartSharingIntent();
mController.onStart(mLifecycleOwner);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBar, never()).setChecked(true);
}
@Test
public void handleStartAudioSharingFromIntent_profileNotReady_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mAssistant.isProfileReady()).thenReturn(false);
var unused = setUpFragmentWithStartSharingIntent();
mController.onServiceConnected();
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBar, never()).setChecked(true);
}
@Test
public void handleStartAudioSharingFromIntent_argFalse_doNothing() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mController.onStart(mLifecycleOwner);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBar, never()).setChecked(true);
}
@Test
public void handleStartAudioSharingFromIntent_handle() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mBtnView.isEnabled()).thenReturn(true);
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice2, mDevice1));
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
Fragment parentFragment = setUpFragmentWithStartSharingIntent();
mController.onServiceConnected();
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitchBar).setChecked(true);
doNothing().when(mBroadcast).startPrivateBroadcast();
mController.onCheckedChanged(mBtnView, /* isChecked= */ true);
shadowOf(Looper.getMainLooper()).idle();
verify(mBroadcast).startPrivateBroadcast();
mController.mBroadcastCallback.onPlaybackStarted(0, 0);
shadowOf(Looper.getMainLooper()).idle();
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(Context.class), eq(SettingsEnums.ACTION_AUTO_JOIN_AUDIO_SHARING));
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
verify(mAssistant).addSource(mDevice2, mMetadata, /* isGroupOp= */ false);
List<Fragment> childFragments = parentFragment.getChildFragmentManager().getFragments();
// Skip audio sharing dialog.
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
// Progress dialog shows sharing progress for the auto add second sink.
AudioSharingProgressDialogFragment progressFragment =
(AudioSharingProgressDialogFragment) Iterables.getOnlyElement(childFragments);
// TODO: use string res once finalized
String expectedMessage = "Sharing with " + TEST_DEVICE_NAME1 + "...";
checkProgressDialogMessage(progressFragment, expectedMessage);
childFragments.forEach(fragment -> ((DialogFragment) fragment).dismiss());
}
@Test
public void handleAutoAddSourceAfterPair() {
when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mDevice1));
when(mBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(mMetadata);
mController.handleAutoAddSourceAfterPair(mDevice1);
shadowOf(Looper.getMainLooper()).idle();
verify(mAssistant).addSource(mDevice1, mMetadata, /* isGroupOp= */ false);
List<Fragment> childFragments = mParentFragment.getChildFragmentManager().getFragments();
assertThat(childFragments).comparingElementsUsing(CLAZZNAME_EQUALS).containsExactly(
AudioSharingProgressDialogFragment.class.getName());
}
private Fragment setUpFragmentWithStartSharingIntent() {
Bundle args = new Bundle();
args.putBoolean(EXTRA_START_LE_AUDIO_SHARING, true);
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, args);
Fragment fragment = new Fragment();
FragmentController.of(fragment, intent)
.create(/* containerViewId= */ 0, /* bundle= */ null)
.start()
.resume()
.visible()
.get();
shadowOf(Looper.getMainLooper()).idle();
mController.init(fragment);
return fragment;
}
private void checkProgressDialogMessage(
@NonNull AudioSharingProgressDialogFragment fragment,
@NonNull String expectedMessage) {
TextView progressMessage = fragment.getDialog() == null ? null
: fragment.getDialog().findViewById(R.id.message);
assertThat(progressMessage).isNotNull();
assertThat(progressMessage.getText().toString()).isEqualTo(expectedMessage);
}
}

View File

@@ -20,22 +20,45 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Add
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AddSourceBadCodeStateTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock private AudioStreamPreference mPreference;
@Mock private AudioStreamsProgressCategoryController mController;
@Mock private AudioStreamsHelper mHelper;
private FakeFeatureFactory mFeatureFactory;
private AddSourceBadCodeState mInstance;
@Before
public void setUp() {
mInstance = AddSourceBadCodeState.getInstance();
mFeatureFactory = FakeFeatureFactory.setupForTest();
mInstance = new AddSourceBadCodeState();
}
@Test
public void testGetInstance() {
mInstance = AddSourceBadCodeState.getInstance();
assertThat(mInstance).isNotNull();
assertThat(mInstance).isInstanceOf(SyncedState.class);
}
@@ -55,4 +78,19 @@ public class AddSourceBadCodeStateTest {
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
}
@Test
public void testPerformAction() {
when(mPreference.getContext()).thenReturn(mContext);
when(mPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS);
mInstance.performAction(mPreference, mController, mHelper);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
eq(mContext),
eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_BAD_CODE),
eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal()));
}
}

View File

@@ -20,22 +20,45 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Add
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AddSourceFailedStateTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock private AudioStreamPreference mPreference;
@Mock private AudioStreamsProgressCategoryController mController;
@Mock private AudioStreamsHelper mHelper;
private FakeFeatureFactory mFeatureFactory;
private AddSourceFailedState mInstance;
@Before
public void setUp() {
mInstance = AddSourceFailedState.getInstance();
mFeatureFactory = FakeFeatureFactory.setupForTest();
mInstance = new AddSourceFailedState();
}
@Test
public void testGetInstance() {
mInstance = AddSourceFailedState.getInstance();
assertThat(mInstance).isNotNull();
assertThat(mInstance).isInstanceOf(SyncedState.class);
}
@@ -54,4 +77,19 @@ public class AddSourceFailedStateTest {
.isEqualTo(
AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED);
}
@Test
public void testPerformAction() {
when(mPreference.getContext()).thenReturn(mContext);
when(mPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS);
mInstance.performAction(mPreference, mController, mHelper);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
eq(mContext),
eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER),
eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal()));
}
}

View File

@@ -22,11 +22,21 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
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.BluetoothLeBroadcastMetadata;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.Rule;
@@ -36,27 +46,41 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowAlertDialog;
import org.robolectric.shadows.ShadowLooper;
import java.util.concurrent.TimeUnit;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowAlertDialog.class,
})
public class AddSourceWaitForResponseStateTest {
private static final int BROADCAST_ID = 1;
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private static final int BROADCAST_ID = 1;
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
@Mock private AudioStreamPreference mMockPreference;
@Mock private AudioStreamsProgressCategoryController mMockController;
@Mock private AudioStreamsHelper mMockHelper;
@Mock private BluetoothLeBroadcastMetadata mMockMetadata;
@Mock private AudioStreamsRepository mMockRepository;
private FakeFeatureFactory mFeatureFactory;
private AddSourceWaitForResponseState mInstance;
@Before
public void setUp() {
mInstance = AddSourceWaitForResponseState.getInstance();
mFeatureFactory = FakeFeatureFactory.setupForTest();
mInstance = new AddSourceWaitForResponseState();
when(mMockPreference.getContext()).thenReturn(mContext);
when(mMockPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS);
}
@Test
public void testGetInstance() {
mInstance = AddSourceWaitForResponseState.getInstance();
assertThat(mInstance).isNotNull();
assertThat(mInstance).isInstanceOf(AudioStreamStateHandler.class);
}
@@ -93,11 +117,18 @@ public class AddSourceWaitForResponseStateTest {
public void testPerformAction_metadataIsNotNull_addSource() {
when(mMockPreference.getAudioStreamMetadata()).thenReturn(mMockMetadata);
when(mMockPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.UNKNOWN);
.thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS);
mInstance.setAudioStreamsRepositoryForTesting(mMockRepository);
mInstance.performAction(mMockPreference, mMockController, mMockHelper);
verify(mMockHelper).addSource(mMockMetadata);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
eq(mContext),
eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN),
eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal()));
verify(mMockRepository).cacheMetadata(mMockMetadata);
verify(mMockController, never()).handleSourceFailedToConnect(anyInt());
}
@@ -108,12 +139,28 @@ public class AddSourceWaitForResponseStateTest {
when(mMockPreference.getAudioStreamState()).thenReturn(mInstance.getStateEnum());
when(mMockPreference.getAudioStreamBroadcastId()).thenReturn(BROADCAST_ID);
when(mMockPreference.getSourceOriginForLogging())
.thenReturn(SourceOriginForLogging.UNKNOWN);
.thenReturn(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS);
when(mMockController.getFragment()).thenReturn(mock(AudioStreamsDashboardFragment.class));
mInstance.setAudioStreamsRepositoryForTesting(mMockRepository);
mInstance.performAction(mMockPreference, mMockController, mMockHelper);
ShadowLooper.idleMainLooper(ADD_SOURCE_WAIT_FOR_RESPONSE_TIMEOUT_MILLIS, TimeUnit.SECONDS);
verify(mMockHelper).addSource(mMockMetadata);
verify(mMockController).handleSourceFailedToConnect(BROADCAST_ID);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
eq(mContext),
eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN),
eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal()));
verify(mMockRepository).cacheMetadata(mMockMetadata);
verify(mFeatureFactory.metricsFeatureProvider)
.action(
eq(mContext),
eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_TIMEOUT),
eq(SourceOriginForLogging.QR_CODE_SCAN_SETTINGS.ordinal()));
verify(mContext).getString(R.string.audio_streams_dialog_stream_is_not_available);
verify(mContext).getString(R.string.audio_streams_is_not_playing);
verify(mContext).getString(R.string.audio_streams_dialog_close);
}
}

View File

@@ -16,22 +16,39 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
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.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
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.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.View;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.ActionButtonsPreference;
import org.junit.After;
@@ -39,14 +56,17 @@ 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.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -55,22 +75,33 @@ import java.util.List;
ShadowAudioStreamsHelper.class,
})
public class AudioStreamButtonControllerTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String KEY = "audio_stream_button";
private static final int BROADCAST_ID = 1;
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock private AudioStreamsHelper mAudioStreamsHelper;
@Mock private PreferenceScreen mScreen;
@Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private AudioStreamsRepository mRepository;
@Mock private ActionButtonsPreference mPreference;
@Mock private BluetoothDevice mSourceDevice;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private FakeFeatureFactory mFeatureFactory;
private AudioStreamButtonController mController;
@Before
public void setUp() {
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mController = new AudioStreamButtonController(mContext, KEY);
mController.init(BROADCAST_ID);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
when(mPreference.getContext()).thenReturn(mContext);
when(mPreference.setButton1Text(anyInt())).thenReturn(mPreference);
@@ -85,6 +116,40 @@ public class AudioStreamButtonControllerTest {
ShadowAudioStreamsHelper.reset();
}
@Test
public void onStart_registerCallbacks() {
mController.onStart(mLifecycleOwner);
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStart_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamButtonController(mContext, KEY);
mController.onStart(mLifecycleOwner);
verify(mAssistant, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_unregisterCallbacks() {
mController.onStop(mLifecycleOwner);
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamButtonController(mContext, KEY);
mController.onStop(mLifecycleOwner);
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void testDisplayPreference_sourceConnected_setDisconnectButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
@@ -96,18 +161,160 @@ public class AudioStreamButtonControllerTest {
verify(mPreference).setButton1Enabled(true);
verify(mPreference).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference).setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
verify(mPreference).setButton1OnClickListener(any(View.OnClickListener.class));
ArgumentCaptor<View.OnClickListener> listenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
verify(mPreference).setButton1OnClickListener(listenerCaptor.capture());
var listener = listenerCaptor.getValue();
assertThat(listener).isNotNull();
listener.onClick(mock(View.class));
verify(mAudioStreamsHelper).removeSource(BROADCAST_ID);
verify(mPreference).setButton1Enabled(false);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_LEAVE_BUTTON_CLICK));
}
@Test
public void testDisplayPreference_sourceNotConnected_setConnectButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.setAudioStreamsRepositoryForTesting(mRepository);
var metadataToRejoin = mock(BluetoothLeBroadcastMetadata.class);
when(mRepository.getSavedMetadata(any(), anyInt())).thenReturn(metadataToRejoin);
mController.displayPreference(mScreen);
verify(mPreference).setButton1Enabled(true);
verify(mPreference).setButton1Text(R.string.audio_streams_connect);
verify(mPreference).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
verify(mPreference).setButton1OnClickListener(any(View.OnClickListener.class));
ArgumentCaptor<View.OnClickListener> listenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
verify(mPreference).setButton1OnClickListener(listenerCaptor.capture());
var listener = listenerCaptor.getValue();
assertThat(listener).isNotNull();
listener.onClick(mock(View.class));
verify(mAudioStreamsHelper).addSource(metadataToRejoin);
verify(mPreference).setButton1Enabled(false);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN), anyInt());
}
@Test
public void testCallback_onSourceRemoved_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceRemoved(
mock(BluetoothDevice.class), /* sourceId= */ 0, /* reason= */ 0);
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_connect);
verify(mPreference, times(2)).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
}
@Test
public void testCallback_onSourceRemovedFailed_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceRemoveFailed(
mock(BluetoothDevice.class), /* sourceId= */ 0, /* reason= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_LEAVE_FAILED));
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference, times(2))
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
}
@Test
public void testCallback_onReceiveStateChanged_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(state.getBisSyncState()).thenReturn(bisSyncState);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mock(BluetoothDevice.class), /* sourceId= */ 0, state);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED), anyInt());
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference, times(2))
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
}
@Test
public void testCallback_onReceiveStateChangedWithSourcePresent_updateButton() {
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
String address = "11:22:33:44:55:66";
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
when(state.getBroadcastId()).thenReturn(BROADCAST_ID);
when(state.getSourceDevice()).thenReturn(mSourceDevice);
when(mSourceDevice.getAddress()).thenReturn(address);
List<Long> bisSyncState = new ArrayList<>();
when(state.getBisSyncState()).thenReturn(bisSyncState);
when(mAudioStreamsHelper.getAllPresentSources()).thenReturn(List.of(state));
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mock(BluetoothDevice.class), /* sourceId= */ 0, state);
verify(mFeatureFactory.metricsFeatureProvider, never())
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED), anyInt());
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_disconnect);
verify(mPreference, times(2))
.setButton1Icon(com.android.settings.R.drawable.ic_settings_close);
}
@Test
public void testCallback_onSourceAddFailed_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceAddFailed(
mock(BluetoothDevice.class),
mock(BluetoothLeBroadcastMetadata.class),
/* reason= */ 0);
verify(mFeatureFactory.metricsFeatureProvider)
.action(any(), eq(SettingsEnums.ACTION_AUDIO_STREAM_JOIN_FAILED_OTHER), anyInt());
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_connect);
verify(mPreference, times(2)).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
}
@Test
public void testCallback_onSourceLost_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 0);
// Called twice, once in displayPreference, the other one in callback
verify(mPreference, times(2)).setButton1Enabled(true);
verify(mPreference, times(2)).setButton1Text(R.string.audio_streams_connect);
verify(mPreference, times(2)).setButton1Icon(com.android.settings.R.drawable.ic_add_24dp);
}
}

View File

@@ -18,28 +18,132 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.platform.test.flag.junit.SetFlagsRule;
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.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.settingslib.flags.Flags;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
})
public class AudioStreamConfirmDialogActivityTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private LocalBluetoothManager mLocalBluetoothManager;
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private VolumeControlProfile mVolumeControl;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private AudioStreamConfirmDialogActivity mActivity;
@Before
public void setUp() {
mActivity = Robolectric.buildActivity(AudioStreamConfirmDialogActivity.class).get();
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile())
.thenReturn(mAssistant);
when(mLocalBluetoothProfileManager.getVolumeControlProfile()).thenReturn(mVolumeControl);
when(mBroadcast.isProfileReady()).thenReturn(true);
when(mAssistant.isProfileReady()).thenReturn(true);
when(mVolumeControl.isProfileReady()).thenReturn(true);
}
@After
public void tearDown() {
ShadowBluetoothUtils.reset();
}
@Test
public void isValidFragment_returnsTrue() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
assertThat(mActivity.isValidFragment(AudioStreamConfirmDialog.class.getName())).isTrue();
}
@Test
public void isValidFragment_returnsFalse() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
assertThat(mActivity.isValidFragment("")).isFalse();
}
@Test
public void isToolbarEnabled_returnsFalse() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
assertThat(mActivity.isToolbarEnabled()).isFalse();
}
@Test
public void setupActivity_serviceNotReady_registerCallback() {
when(mBroadcast.isProfileReady()).thenReturn(false);
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
verify(mLocalBluetoothProfileManager).addServiceListener(any());
}
@Test
public void setupActivity_serviceNotReady_registerCallback_onServiceCallback() {
when(mBroadcast.isProfileReady()).thenReturn(false);
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
verify(mLocalBluetoothProfileManager).addServiceListener(any());
when(mBroadcast.isProfileReady()).thenReturn(true);
mActivity.onServiceConnected();
verify(mLocalBluetoothProfileManager).removeServiceListener(any());
mActivity.onServiceDisconnected();
// Do nothing.
}
@Test
public void setupActivity_serviceReady_doNothing() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
verify(mLocalBluetoothProfileManager, never()).addServiceListener(any());
}
@Test
public void onStop_unregisterCallback() {
mActivity = Robolectric.setupActivity(AudioStreamConfirmDialogActivity.class);
mActivity.onStop();
verify(mLocalBluetoothProfileManager).removeServiceListener(any());
}
}

View File

@@ -16,14 +16,20 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static android.app.settings.SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamConfirmDialog.DEFAULT_DEVICE_NAME;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsDashboardFragment.KEY_BROADCAST_METADATA;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
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 static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -127,6 +133,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_FEATURE_UNSUPPORTED);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -152,6 +160,10 @@ public class AudioStreamConfirmDialogTest {
assertThat(rightButton).isNotNull();
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity).finish();
}
@Test
@@ -165,6 +177,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_NO_LE_DEVICE);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -184,18 +198,27 @@ public class AudioStreamConfirmDialogTest {
View leftButton = dialog.findViewById(R.id.left_button);
assertThat(leftButton).isNotNull();
assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(leftButton.hasOnClickListeners()).isTrue();
leftButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
Button rightButton = dialog.findViewById(R.id.right_button);
assertThat(rightButton).isNotNull();
assertThat(rightButton.getText())
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_button));
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity, times(2)).finish();
}
@Test
public void showDialog_noMetadata() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mBluetoothDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
when(mAssistant.getAllConnectedDevices()).thenReturn(devices);
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
FragmentController.setupFragment(
@@ -207,6 +230,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -231,13 +256,17 @@ public class AudioStreamConfirmDialogTest {
assertThat(rightButton).isNotNull();
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity).finish();
}
@Test
public void showDialog_invalidMetadata() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mBluetoothDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
when(mAssistant.getAllConnectedDevices()).thenReturn(devices);
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
Intent intent = new Intent();
@@ -252,6 +281,8 @@ public class AudioStreamConfirmDialogTest {
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_DATA_ERROR);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
@@ -276,14 +307,18 @@ public class AudioStreamConfirmDialogTest {
assertThat(rightButton).isNotNull();
assertThat(rightButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity).finish();
}
@Test
public void showDialog_confirmListen() {
List<BluetoothDevice> devices = new ArrayList<>();
devices.add(mBluetoothDevice);
when(mAssistant.getDevicesMatchingConnectionStates(any())).thenReturn(devices);
when(mBluetoothDevice.getAlias()).thenReturn(DEVICE_NAME);
when(mAssistant.getAllConnectedDevices()).thenReturn(devices);
when(mBluetoothDevice.getAlias()).thenReturn("");
Intent intent = new Intent();
intent.putExtra(KEY_BROADCAST_METADATA, VALID_METADATA);
@@ -296,9 +331,11 @@ public class AudioStreamConfirmDialogTest {
shadowMainLooper().idle();
assertThat(mDialogFragment.getMetricsCategory())
.isEqualTo(SettingsEnums.DIALOG_AUDIO_STREAM_CONFIRM_LISTEN);
.isEqualTo(DIALOG_AUDIO_STREAM_CONFIRM_LISTEN);
assertThat(mDialogFragment.mActivity).isNotNull();
mDialogFragment.mActivity = spy(mDialogFragment.mActivity);
var dialog = mDialogFragment.getDialog();
Dialog dialog = mDialogFragment.getDialog();
assertThat(dialog).isNotNull();
assertThat(dialog.isShowing()).isTrue();
TextView title = dialog.findViewById(R.id.dialog_title);
@@ -311,17 +348,27 @@ public class AudioStreamConfirmDialogTest {
assertThat(subtitle1.getVisibility()).isEqualTo(View.VISIBLE);
TextView subtitle2 = dialog.findViewById(R.id.dialog_subtitle_2);
assertThat(subtitle2).isNotNull();
var defaultName = mContext.getString(DEFAULT_DEVICE_NAME);
assertThat(subtitle2.getText())
.isEqualTo(
mContext.getString(
R.string.audio_streams_dialog_control_volume, DEVICE_NAME));
R.string.audio_streams_dialog_control_volume, defaultName));
View leftButton = dialog.findViewById(R.id.left_button);
assertThat(leftButton).isNotNull();
assertThat(leftButton.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(leftButton.hasOnClickListeners()).isTrue();
leftButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
Button rightButton = dialog.findViewById(R.id.right_button);
assertThat(rightButton).isNotNull();
assertThat(rightButton.getText())
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_listen));
assertThat(rightButton.hasOnClickListeners()).isTrue();
rightButton.callOnClick();
assertThat(dialog.isShowing()).isFalse();
verify(mDialogFragment.mActivity, times(2)).finish();
}
}

View File

@@ -16,22 +16,48 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamDetailsFragment.BROADCAST_ID_ARG;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamDetailsFragment.BROADCAST_NAME_ARG;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioStreamDetailsFragmentTest {
private AudioStreamDetailsFragment mFragment;
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
private static final String BROADCAST_NAME = "name";
private static final int BROADCAST_ID = 1;
private final Context mContext = ApplicationProvider.getApplicationContext();
@Mock private AudioStreamHeaderController mHeaderController;
@Mock private AudioStreamButtonController mButtonController;
private TestFragment mFragment;
@Before
public void setUp() {
mFragment = new AudioStreamDetailsFragment();
mFragment = spy(new TestFragment());
doReturn(mHeaderController).when(mFragment).use(AudioStreamHeaderController.class);
doReturn(mButtonController).when(mFragment).use(AudioStreamButtonController.class);
}
@Test
@@ -44,4 +70,29 @@ public class AudioStreamDetailsFragmentTest {
public void getLogTag_returnsCorrectTag() {
assertThat(mFragment.getLogTag()).isEqualTo(AudioStreamDetailsFragment.TAG);
}
@Test
public void getMetricsCategory_returnsCorrectEnum() {
assertThat(mFragment.getMetricsCategory()).isEqualTo(SettingsEnums.AUDIO_STREAM_DETAIL);
}
@Test
public void onAttach_getArguments() {
Bundle bundle = new Bundle();
bundle.putString(BROADCAST_NAME_ARG, BROADCAST_NAME);
bundle.putInt(BROADCAST_ID_ARG, BROADCAST_ID);
mFragment.setArguments(bundle);
mFragment.onAttach(mContext);
verify(mButtonController).init(BROADCAST_ID);
verify(mHeaderController).init(mFragment, BROADCAST_NAME, BROADCAST_ID);
}
public static class TestFragment extends AudioStreamDetailsFragment {
@Override
protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
return super.use(clazz);
}
}
}

View File

@@ -18,13 +18,24 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamHeaderController.AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY;
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
@@ -32,6 +43,8 @@ import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadow
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowEntityHeaderController;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.After;
@@ -45,8 +58,10 @@ import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -56,8 +71,9 @@ import java.util.List;
ShadowAudioStreamsHelper.class,
})
public class AudioStreamHeaderControllerTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String KEY = "audio_stream_header";
private static final int BROADCAST_ID = 1;
private static final String BROADCAST_NAME = "broadcast name";
@@ -65,15 +81,24 @@ public class AudioStreamHeaderControllerTest {
@Mock private AudioStreamsHelper mAudioStreamsHelper;
@Mock private PreferenceScreen mScreen;
@Mock private BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private AudioStreamDetailsFragment mFragment;
@Mock private LayoutPreference mPreference;
@Mock private EntityHeaderController mHeaderController;
@Mock private BluetoothDevice mBluetoothDevice;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private AudioStreamHeaderController mController;
@Before
public void setUp() {
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
ShadowEntityHeaderController.setUseMock(mHeaderController);
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mAssistant);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = new AudioStreamHeaderController(mContext, KEY);
mController.init(mFragment, BROADCAST_NAME, BROADCAST_ID);
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
@@ -87,6 +112,40 @@ public class AudioStreamHeaderControllerTest {
ShadowAudioStreamsHelper.reset();
}
@Test
public void onStart_registerCallbacks() {
mController.onStart(mLifecycleOwner);
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStart_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamHeaderController(mContext, KEY);
mController.onStart(mLifecycleOwner);
verify(mAssistant, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_unregisterCallbacks() {
mController.onStop(mLifecycleOwner);
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_profileNull_doNothing() {
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(null);
mController = new AudioStreamHeaderController(mContext, KEY);
mController.onStop(mLifecycleOwner);
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void testDisplayPreference_sourceConnected_setSummary() {
when(mAudioStreamsHelper.getAllConnectedSources())
@@ -96,9 +155,11 @@ public class AudioStreamHeaderControllerTest {
mController.displayPreference(mScreen);
verify(mHeaderController).setLabel(BROADCAST_NAME);
verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController)
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY));
verify(mHeaderController).done(true);
verify(mScreen).addPreference(any());
}
@Test
@@ -108,7 +169,113 @@ public class AudioStreamHeaderControllerTest {
mController.displayPreference(mScreen);
verify(mHeaderController).setLabel(BROADCAST_NAME);
verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController).done(true);
verify(mScreen).addPreference(any());
}
@Test
public void testDisplayPreference_sourcePresent_setSummary() {
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
String address = "11:22:33:44:55:66";
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
when(mBroadcastReceiveState.getSourceDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getAddress()).thenReturn(address);
List<Long> bisSyncState = new ArrayList<>();
when(mBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
when(mAudioStreamsHelper.getAllPresentSources())
.thenReturn(List.of(mBroadcastReceiveState));
mController.displayPreference(mScreen);
verify(mHeaderController).setLabel(BROADCAST_NAME);
verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController)
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY));
verify(mHeaderController).done(true);
verify(mScreen).addPreference(any());
}
@Test
public void testDisplayPreference_sourceNotPresent_setSummary() {
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
when(mAudioStreamsHelper.getAllPresentSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
verify(mHeaderController).setLabel(BROADCAST_NAME);
verify(mHeaderController).setIcon(any(Drawable.class));
verify(mHeaderController).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController).done(true);
verify(mScreen).addPreference(any());
}
@Test
public void testCallback_onSourceRemoved_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceRemoved(
mock(BluetoothDevice.class), /* sourceId= */ 0, /* reason= */ 0);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2)).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController, times(2)).done(true);
}
@Test
public void testCallback_onSourceLost_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(Collections.emptyList());
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 1);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2)).setSummary(AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY);
verify(mHeaderController, times(2)).done(true);
}
@Test
public void testCallback_onReceiveStateChanged_updateButton() {
when(mAudioStreamsHelper.getAllConnectedSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
List<Long> bisSyncState = new ArrayList<>();
bisSyncState.add(1L);
when(state.getBisSyncState()).thenReturn(bisSyncState);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mock(BluetoothDevice.class), /* sourceId= */ 0, state);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2))
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY));
verify(mHeaderController, times(2)).done(true);
}
@Test
public void testCallback_onReceiveStateChangedWithSourcePresent_updateButton() {
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
String address = "11:22:33:44:55:66";
when(mAudioStreamsHelper.getAllPresentSources())
.thenReturn(List.of(mBroadcastReceiveState));
when(mBroadcastReceiveState.getBroadcastId()).thenReturn(BROADCAST_ID);
when(mBroadcastReceiveState.getSourceDevice()).thenReturn(mBluetoothDevice);
when(mBluetoothDevice.getAddress()).thenReturn(address);
mController.displayPreference(mScreen);
mController.mBroadcastAssistantCallback.onReceiveStateChanged(
mock(BluetoothDevice.class), /* sourceId= */ 0, mBroadcastReceiveState);
// Called twice, once in displayPreference, the other one in callback
verify(mHeaderController, times(2))
.setSummary(mContext.getString(AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY));
verify(mHeaderController, times(2)).done(true);
}
}

View File

@@ -18,22 +18,29 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.LEAVE_BROADCAST_ACTION;
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.doReturn;
import static org.mockito.Mockito.mock;
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.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.Intent;
@@ -43,14 +50,20 @@ import android.content.res.Resources;
import android.media.session.ISession;
import android.media.session.ISessionController;
import android.media.session.MediaSessionManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowThreadUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -72,10 +85,12 @@ import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Set;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowThreadUtils.class,
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
ShadowAudioStreamsHelper.class,
@@ -83,6 +98,8 @@ import java.util.ArrayList;
public class AudioStreamMediaServiceTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String CHANNEL_ID = "bluetooth_notification_channel";
private static final String DEVICE_NAME = "name";
@Mock private Resources mResources;
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
@@ -91,17 +108,21 @@ public class AudioStreamMediaServiceTest {
@Mock private MediaSessionManager mMediaSessionManager;
@Mock private BluetoothEventManager mBluetoothEventManager;
@Mock private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
@Mock private VolumeControlProfile mVolumeControlProfile;
@Mock private CachedBluetoothDevice mCachedBluetoothDevice;
@Mock private BluetoothDevice mDevice;
@Mock private ISession mISession;
@Mock private ISessionController mISessionController;
@Mock private PackageManager mPackageManager;
@Mock private DisplayMetrics mDisplayMetrics;
@Mock private Context mContext;
private FakeFeatureFactory mFeatureFactory;
private AudioStreamMediaService mAudioStreamMediaService;
@Before
public void setUp() {
mFeatureFactory = FakeFeatureFactory.setupForTest();
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
ShadowBluetoothAdapter shadowBluetoothAdapter =
@@ -114,6 +135,9 @@ public class AudioStreamMediaServiceTest {
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
when(mLocalBtManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBtManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mCachedDeviceManager.findDevice(any())).thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
when(mLocalBluetoothProfileManager.getVolumeControlProfile())
.thenReturn(mVolumeControlProfile);
@@ -168,6 +192,25 @@ public class AudioStreamMediaServiceTest {
verify(mVolumeControlProfile).registerCallback(any(), any());
}
@Test
public void onCreate_flagOn_createNewChannel() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mNotificationManager.getNotificationChannel(anyString())).thenReturn(null);
mAudioStreamMediaService.onCreate();
ArgumentCaptor<NotificationChannel> notificationChannelCapture =
ArgumentCaptor.forClass(NotificationChannel.class);
verify(mNotificationManager)
.createNotificationChannel(notificationChannelCapture.capture());
NotificationChannel newChannel = notificationChannelCapture.getValue();
assertThat(newChannel).isNotNull();
assertThat(newChannel.getId()).isEqualTo(CHANNEL_ID);
assertThat(newChannel.getName())
.isEqualTo(mContext.getString(com.android.settings.R.string.bluetooth));
assertThat(newChannel.getImportance()).isEqualTo(NotificationManager.IMPORTANCE_HIGH);
}
@Test
public void onDestroy_flagOff_doNothing() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
@@ -183,8 +226,15 @@ public class AudioStreamMediaServiceTest {
@Test
public void onDestroy_flagOn_cleanup() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
var devices = new ArrayList<BluetoothDevice>();
devices.add(mDevice);
Intent intent = new Intent();
intent.putExtra(BROADCAST_ID, 1);
intent.putParcelableArrayListExtra(DEVICES, devices);
mAudioStreamMediaService.onCreate();
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
mAudioStreamMediaService.onDestroy();
verify(mBluetoothEventManager).unregisterCallback(any());
@@ -196,7 +246,6 @@ public class AudioStreamMediaServiceTest {
public void onStartCommand_noBroadcastId_stopSelf() {
mAudioStreamMediaService.onStartCommand(new Intent(), /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
verify(mAudioStreamMediaService).stopSelf();
}
@@ -207,7 +256,6 @@ public class AudioStreamMediaServiceTest {
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mLocalSession).isNull();
verify(mAudioStreamMediaService).stopSelf();
}
@@ -222,12 +270,179 @@ public class AudioStreamMediaServiceTest {
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mLocalSession).isNotNull();
verify(mAudioStreamMediaService, never()).stopSelf();
ArgumentCaptor<Notification> notificationCapture =
ArgumentCaptor.forClass(Notification.class);
verify(mAudioStreamMediaService).startForeground(anyInt(), notificationCapture.capture());
var notification = notificationCapture.getValue();
assertThat(notification.getSmallIcon()).isNotNull();
assertThat(notification.isStyle(Notification.MediaStyle.class)).isTrue();
ArgumentCaptor<Notification> notification = ArgumentCaptor.forClass(Notification.class);
verify(mAudioStreamMediaService).startForeground(anyInt(), notification.capture());
assertThat(notification.getValue().getSmallIcon()).isNotNull();
assertThat(notification.getValue().isStyle(Notification.MediaStyle.class)).isTrue();
verify(mAudioStreamMediaService, never()).stopSelf();
}
@Test
public void assistantCallback_onSourceLost_stopSelf() {
mAudioStreamMediaService.onCreate();
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
mAudioStreamMediaService.mBroadcastAssistantCallback.onSourceLost(/* broadcastId= */ 0);
verify(mAudioStreamMediaService).stopSelf();
}
@Test
public void assistantCallback_onSourceRemoved_stopSelf() {
mAudioStreamMediaService.onCreate();
assertThat(mAudioStreamMediaService.mBroadcastAssistantCallback).isNotNull();
mAudioStreamMediaService.mBroadcastAssistantCallback.onSourceRemoved(
mDevice, /* sourceId= */ 0, /* reason= */ 0);
verify(mAudioStreamMediaService).stopSelf();
}
@Test
public void bluetoothCallback_onBluetoothOff_stopSelf() {
mAudioStreamMediaService.onCreate();
assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
mAudioStreamMediaService.mBluetoothCallback.onBluetoothStateChanged(
BluetoothAdapter.STATE_OFF);
verify(mAudioStreamMediaService).stopSelf();
}
@Test
public void bluetoothCallback_onDeviceDisconnect_stopSelf() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioStreamMediaService.onCreate();
assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
mAudioStreamMediaService.mBluetoothCallback.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothAdapter.STATE_DISCONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
verify(mAudioStreamMediaService).stopSelf();
}
@Test
public void bluetoothCallback_onMemberDeviceDisconnect_stopSelf() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mock(BluetoothDevice.class));
CachedBluetoothDevice member = mock(CachedBluetoothDevice.class);
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(Set.of(member));
when(member.getDevice()).thenReturn(mDevice);
var devices = new ArrayList<BluetoothDevice>();
devices.add(mDevice);
Intent intent = new Intent();
intent.putExtra(BROADCAST_ID, 1);
intent.putParcelableArrayListExtra(DEVICES, devices);
mAudioStreamMediaService.onCreate();
assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
mAudioStreamMediaService.mBluetoothCallback.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothAdapter.STATE_DISCONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
verify(mAudioStreamMediaService).stopSelf();
}
@Test
public void mediaSessionCallback_onSeekTo_updateNotification() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioStreamMediaService.onCreate();
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
mAudioStreamMediaService.mMediaSessionCallback.onSeekTo(100);
verify(mNotificationManager).notify(anyInt(), any());
}
@Test
public void mediaSessionCallback_onPause_setVolume() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioStreamMediaService.onCreate();
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
mAudioStreamMediaService.mMediaSessionCallback.onPause();
verify(mVolumeControlProfile).setDeviceVolume(any(), anyInt(), anyBoolean());
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(),
eq(SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK),
eq(1));
}
@Test
public void mediaSessionCallback_onPlay_setVolume() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioStreamMediaService.onCreate();
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
mAudioStreamMediaService.mMediaSessionCallback.onPlay();
verify(mVolumeControlProfile).setDeviceVolume(any(), anyInt(), anyBoolean());
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(),
eq(SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_MUTE_BUTTON_CLICK),
eq(0));
}
@Test
public void mediaSessionCallback_onCustomAction_leaveBroadcast() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioStreamMediaService.onCreate();
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
assertThat(mAudioStreamMediaService.mMediaSessionCallback).isNotNull();
mAudioStreamMediaService.mMediaSessionCallback.onCustomAction(
LEAVE_BROADCAST_ACTION, Bundle.EMPTY);
verify(mAudioStreamsHelper).removeSource(anyInt());
verify(mFeatureFactory.metricsFeatureProvider)
.action(
any(),
eq(SettingsEnums.ACTION_AUDIO_STREAM_NOTIFICATION_LEAVE_BUTTON_CLICK));
}
@Test
public void volumeControlCallback_onDeviceVolumeChanged_updateNotification() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mAudioStreamMediaService.onCreate();
assertThat(mAudioStreamMediaService.mVolumeControlCallback).isNotNull();
mAudioStreamMediaService.onStartCommand(setupIntent(), /* flags= */ 0, /* startId= */ 0);
mAudioStreamMediaService.mVolumeControlCallback.onDeviceVolumeChanged(
mDevice, /* volume= */ 0);
verify(mNotificationManager).notify(anyInt(), any());
}
@Test
public void onBind_returnNull() {
IBinder binder = mAudioStreamMediaService.onBind(new Intent());
assertThat(binder).isNull();
}
private Intent setupIntent() {
when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
var devices = new ArrayList<BluetoothDevice>();
devices.add(mDevice);
Intent intent = new Intent();
intent.putExtra(BROADCAST_ID, 1);
intent.putParcelableArrayListExtra(DEVICES, devices);
return intent;
}
}

View File

@@ -28,7 +28,6 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
@@ -93,17 +92,6 @@ public class AudioStreamPreferenceTest {
assertThat(divider.getVisibility()).isEqualTo(View.GONE);
}
@Test
public void setConnected_shouldUpdatePreferenceUI() {
String summary = "Connected";
OnPreferenceClickListener listener = mock(OnPreferenceClickListener.class);
mPreference.setIsConnected(true, summary, listener);
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo(summary);
assertThat(mPreference.getOnPreferenceClickListener()).isEqualTo(listener);
}
@Test
public void setAudioStreamMetadata_shouldUpdateMetadata() {
AudioStreamPreference p =
@@ -147,7 +135,7 @@ public class AudioStreamPreferenceTest {
@Test
public void shouldHideSecondTarget_connected() {
mPreference.setIsConnected(true, "", null);
mPreference.setIsConnected(true);
assertThat(mPreference.shouldHideSecondTarget()).isTrue();
}

View File

@@ -0,0 +1,182 @@
/*
* 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.audiostreams;
import static com.android.settingslib.flags.Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX;
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.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
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.content.Context;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.SpannableString;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
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.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioStreamStateHandlerTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final int SUMMARY_RES = 1;
private static final String SUMMARY = "summary";
private final Context mContext = spy(ApplicationProvider.getApplicationContext());
@Mock private AudioStreamsProgressCategoryController mController;
@Mock private AudioStreamsHelper mHelper;
@Mock private AudioStreamPreference mPreference;
private AudioStreamStateHandler mHandler;
@Before
public void setUp() {
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
mHandler = spy(new AudioStreamStateHandler());
}
@Test
public void testHandleStateChange_noChange_doNothing() {
when(mHandler.getStateEnum())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference, never()).setAudioStreamState(any());
verify(mHandler, never()).performAction(any(), any(), any());
verify(mPreference, never()).setIsConnected(anyBoolean());
verify(mPreference, never()).setSummary(any());
verify(mPreference, never()).setOnPreferenceClickListener(any());
}
@Test
public void testHandleStateChange_setNewState() {
when(mHandler.getStateEnum())
.thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference)
.setAudioStreamState(
AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_ADDED);
verify(mHandler).performAction(any(), any(), any());
verify(mPreference).setIsConnected(eq(true));
verify(mPreference).setSummary(eq(""));
verify(mPreference).setOnPreferenceClickListener(eq(null));
}
@Test
public void testHandleStateChange_setNewState_sourcePresent() {
mSetFlagsRule.enableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
when(mHandler.getStateEnum())
.thenReturn(AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference)
.setAudioStreamState(
AudioStreamsProgressCategoryController.AudioStreamState.SOURCE_PRESENT);
verify(mHandler).performAction(any(), any(), any());
verify(mPreference).setIsConnected(eq(true));
verify(mPreference).setSummary(eq(""));
verify(mPreference).setOnPreferenceClickListener(eq(null));
}
@Test
public void testHandleStateChange_setNewState_newSummary_newListener() {
Preference.OnPreferenceClickListener listener =
mock(Preference.OnPreferenceClickListener.class);
when(mHandler.getStateEnum())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
when(mHandler.getSummary()).thenReturn(SUMMARY_RES);
when(mHandler.getOnClickListener(any())).thenReturn(listener);
when(mPreference.getAudioStreamState())
.thenReturn(
AudioStreamsProgressCategoryController.AudioStreamState.ADD_SOURCE_FAILED);
when(mPreference.getContext()).thenReturn(mContext);
doReturn(SUMMARY).when(mContext).getString(anyInt());
mHandler.handleStateChange(mPreference, mController, mHelper);
verify(mPreference)
.setAudioStreamState(
AudioStreamsProgressCategoryController.AudioStreamState
.ADD_SOURCE_BAD_CODE);
verify(mHandler).performAction(any(), any(), any());
verify(mPreference).setIsConnected(eq(false));
ArgumentCaptor<SpannableString> argumentCaptor =
ArgumentCaptor.forClass(SpannableString.class);
verify(mPreference).setSummary(argumentCaptor.capture());
assertThat(argumentCaptor.getValue()).isNotNull();
assertThat(argumentCaptor.getValue().toString()).isEqualTo(SUMMARY);
verify(mPreference).setOnPreferenceClickListener(eq(listener));
}
@Test
public void testGetSummary() {
int res = mHandler.getSummary();
assertThat(res).isEqualTo(AudioStreamStateHandler.EMPTY_STRING_RES);
}
@Test
public void testGetOnClickListener() {
Preference.OnPreferenceClickListener listener = mHandler.getOnClickListener(mController);
assertThat(listener).isNull();
}
@Test
public void testGetStateEnum() {
var state = mHandler.getStateEnum();
assertThat(state)
.isEqualTo(AudioStreamsProgressCategoryController.AudioStreamState.UNKNOWN);
}
}

View File

@@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
@@ -76,25 +77,46 @@ public class AudioStreamsActiveDeviceSummaryUpdaterTest {
}
@Test
public void onActiveDeviceChanged_notLeProfile_doNothing() {
mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, 0);
public void unregister_doNothing() {
mUpdater.register(false);
assertThat(mUpdatedSummary).isNull();
}
@Test
public void onActiveDeviceChanged_leProfile_summaryUpdated() {
public void onProfileConnectionStateChanged_notLeAssistProfile_doNothing() {
mUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice, 0, 0);
assertThat(mUpdatedSummary).isNull();
}
@Test
public void onProfileConnectionStateChanged_leAssistantProfile_summaryUpdated() {
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(
mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, BluetoothProfile.LE_AUDIO);
mUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertThat(mUpdatedSummary).isEqualTo(DEVICE_NAME);
}
@Test
public void onActiveDeviceChanged_leProfile_noDevice_summaryUpdated() {
mUpdater.onActiveDeviceChanged(mCachedBluetoothDevice, BluetoothProfile.LE_AUDIO);
public void onActiveDeviceChanged_leAssistantProfile_noDevice_summaryUpdated() {
mUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
assertThat(mUpdatedSummary)
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title));
}
@Test
public void onBluetoothStateOff_summaryUpdated() {
mUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
assertThat(mUpdatedSummary)
.isEqualTo(mContext.getString(R.string.audio_streams_dialog_no_le_device_title));

View File

@@ -23,11 +23,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
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 static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.os.Looper;
@@ -42,6 +44,7 @@ import com.android.settings.bluetooth.Utils;
import com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows.ShadowAudioStreamsHelper;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
@@ -57,6 +60,7 @@ 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.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -116,7 +120,7 @@ public class AudioStreamsCategoryControllerTest {
when(mBroadcast.isProfileReady()).thenReturn(true);
when(mAssistant.isProfileReady()).thenReturn(true);
when(mVolumeControl.isProfileReady()).thenReturn(true);
mController = new AudioStreamsCategoryController(mContext, KEY);
mController = spy(new AudioStreamsCategoryController(mContext, KEY));
mPreference = new Preference(mContext);
when(mScreen.findPreference(KEY)).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -228,4 +232,21 @@ public class AudioStreamsCategoryControllerTest {
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void onProfileConnectionStateChanged_updateVisibility() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_QR_CODE_PRIVATE_BROADCAST_SHARING);
ArgumentCaptor<BluetoothCallback> argumentCaptor =
ArgumentCaptor.forClass(BluetoothCallback.class);
mController.onStart(mLifecycleOwner);
verify(mBluetoothEventManager).registerCallback(argumentCaptor.capture());
BluetoothCallback callback = argumentCaptor.getValue();
callback.onProfileConnectionStateChanged(
mCachedBluetoothDevice,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT,
BluetoothAdapter.STATE_DISCONNECTED);
verify(mController).updateVisibility();
}
}

Some files were not shown because too many files have changed in this diff Show More