fix: 引入Settings的Module
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 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 android.content.ContentInterface;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserProperties;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/** Unittest for DefaultRingtonePreference. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DefaultRingtonePreferenceTest {
|
||||
|
||||
private static final int OWNER_USER_ID = 1;
|
||||
private static final int OTHER_USER_ID = 10;
|
||||
private static final int INVALID_RINGTONE_TYPE = 0;
|
||||
private DefaultRingtonePreference mDefaultRingtonePreference;
|
||||
|
||||
@Mock
|
||||
private ContentResolver mContentResolver;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
private Uri mRingtoneUri;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Context context = spy(ApplicationProvider.getApplicationContext());
|
||||
mContentResolver = ContentResolver.wrap(Mockito.mock(ContentInterface.class));
|
||||
when(context.getContentResolver()).thenReturn(mContentResolver);
|
||||
|
||||
mDefaultRingtonePreference = spy(new DefaultRingtonePreference(context, null /* attrs */));
|
||||
doReturn(context).when(mDefaultRingtonePreference).getContext();
|
||||
|
||||
// Use INVALID_RINGTONE_TYPE to return early in RingtoneManager.setActualDefaultRingtoneUri
|
||||
when(mDefaultRingtonePreference.getRingtoneType())
|
||||
.thenReturn(INVALID_RINGTONE_TYPE);
|
||||
|
||||
mDefaultRingtonePreference.setUserId(OWNER_USER_ID);
|
||||
mDefaultRingtonePreference.mUserContext = context;
|
||||
when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(false);
|
||||
|
||||
when(context.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
|
||||
when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
|
||||
UserProperties userProperties = new UserProperties.Builder().setMediaSharedWithParent(false)
|
||||
.build();
|
||||
when(mUserManager.getUserProperties(UserHandle.of(OTHER_USER_ID))).thenReturn(
|
||||
userProperties);
|
||||
|
||||
mRingtoneUri = Uri.parse("content://none");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSaveRingtone_nullMimeType_shouldNotSetRingtone() {
|
||||
when(mContentResolver.getType(mRingtoneUri)).thenReturn(null);
|
||||
|
||||
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
|
||||
|
||||
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSaveRingtone_notAudioMimeType_shouldNotSetRingtone() {
|
||||
when(mContentResolver.getType(mRingtoneUri)).thenReturn("text/plain");
|
||||
|
||||
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
|
||||
|
||||
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSaveRingtone_notManagedProfile_shouldNotSetRingtone() {
|
||||
mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
|
||||
when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
|
||||
when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
|
||||
when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
|
||||
UserHandle.of(OWNER_USER_ID));
|
||||
when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(false);
|
||||
|
||||
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
|
||||
|
||||
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSaveRingtone_notSameUser_shouldNotSetRingtone() {
|
||||
mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
|
||||
when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
|
||||
when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(false);
|
||||
|
||||
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
|
||||
|
||||
verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSaveRingtone_isManagedProfile_shouldSetRingtone() {
|
||||
mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
|
||||
when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
|
||||
when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
|
||||
when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
|
||||
UserHandle.of(OWNER_USER_ID));
|
||||
when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(true);
|
||||
|
||||
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
|
||||
|
||||
verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSaveRingtone_defaultUri_shouldSetRingtone() {
|
||||
mRingtoneUri = Uri.parse("default_ringtone");
|
||||
when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(true);
|
||||
|
||||
mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
|
||||
|
||||
verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.text.Spannable;
|
||||
import android.text.style.ClickableSpan;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LinkifyUtilsTest {
|
||||
private static final String TEST_STRING = "to LINK_BEGINscanning settingsLINK_END.";
|
||||
private static final String WRONG_STRING = "to scanning settingsLINK_END.";
|
||||
private final LinkifyUtils.OnClickListener mClickListener = () -> { /* Do nothing */ };
|
||||
|
||||
private StringBuilder mSpanStringBuilder;
|
||||
private StringBuilder mWrongSpanStringBuilder;
|
||||
TextView mTextView;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mSpanStringBuilder = new StringBuilder(TEST_STRING);
|
||||
mWrongSpanStringBuilder = new StringBuilder(WRONG_STRING);
|
||||
mTextView = new TextView(ApplicationProvider.getApplicationContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linkify_whenSpanStringCorrect_shouldReturnTrue() {
|
||||
final boolean linkifyResult = LinkifyUtils.linkify(mTextView, mSpanStringBuilder,
|
||||
mClickListener);
|
||||
|
||||
assertThat(linkifyResult).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linkify_whenSpanStringWrong_shouldReturnFalse() {
|
||||
final boolean linkifyResult = LinkifyUtils.linkify(mTextView, mWrongSpanStringBuilder,
|
||||
mClickListener);
|
||||
|
||||
assertThat(linkifyResult).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void linkify_whenSpanStringCorrect_shouldContainClickableSpan() {
|
||||
LinkifyUtils.linkify(mTextView, mSpanStringBuilder, mClickListener);
|
||||
final Spannable spannableContent = (Spannable) mTextView.getText();
|
||||
final int len = spannableContent.length();
|
||||
final Object[] spans = spannableContent.getSpans(0, len, Object.class);
|
||||
|
||||
assertThat(spans[1] instanceof ClickableSpan).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.platform.test.flag.junit.SetFlagsRule
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.Settings.FactoryResetActivity
|
||||
import com.android.settings.flags.Flags
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/** Test [MainClear]. */
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MainClearTest {
|
||||
@get:Rule
|
||||
val mSetFlagsRule = SetFlagsRule()
|
||||
|
||||
@Test
|
||||
fun factoryResetCancelButton_flagDisabled_noCancelButton() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_SHOW_FACTORY_RESET_CANCEL_BUTTON)
|
||||
ActivityScenario.launch(FactoryResetActivity::class.java).use {
|
||||
ensurePrimaryButton()
|
||||
onView(withText(android.R.string.cancel)).check(doesNotExist())
|
||||
it.onActivity { activity -> assertThat(activity.isFinishing).isFalse() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun factoryResetCancelButton_flagEnabled_showCancelButton() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_SHOW_FACTORY_RESET_CANCEL_BUTTON)
|
||||
ActivityScenario.launch(FactoryResetActivity::class.java).use {
|
||||
ensurePrimaryButton()
|
||||
it.onActivity { activity -> assertThat(activity.isFinishing).isFalse() }
|
||||
|
||||
// Note: onView CANNOT be called within onActivity block, which runs in the main thread
|
||||
onView(withText(android.R.string.cancel)).check(matches(isDisplayed())).perform(click())
|
||||
|
||||
it.onActivity { activity -> assertThat(activity.isFinishing).isTrue() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensurePrimaryButton() {
|
||||
onView(withText(R.string.main_clear_button_text)).check(matches(isDisplayed()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ResetSubscriptionContractTest {
|
||||
|
||||
private static final int SUB_ID_1 = 3;
|
||||
private static final int SUB_ID_2 = 8;
|
||||
|
||||
@Mock
|
||||
private SubscriptionManager mSubscriptionManager;
|
||||
@Mock
|
||||
private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
|
||||
@Mock
|
||||
private SubscriptionInfo mSubscriptionInfo1;
|
||||
@Mock
|
||||
private SubscriptionInfo mSubscriptionInfo2;
|
||||
|
||||
private Context mContext;
|
||||
private ResetNetworkRequest mRequestArgs;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mRequestArgs = new ResetNetworkRequest(new Bundle());
|
||||
}
|
||||
|
||||
private ResetSubscriptionContract createTestObject() {
|
||||
return new ResetSubscriptionContract(mContext, mRequestArgs) {
|
||||
@Override
|
||||
protected SubscriptionManager getSubscriptionManager() {
|
||||
return mSubscriptionManager;
|
||||
}
|
||||
@Override
|
||||
protected OnSubscriptionsChangedListener getChangeListener() {
|
||||
return mOnSubscriptionsChangedListener;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnyMissingSubscriptionId_returnNull_whenNoSubscriptionChange() {
|
||||
mRequestArgs.setResetTelephonyAndNetworkPolicyManager(SUB_ID_1);
|
||||
doReturn(mSubscriptionInfo1).when(mSubscriptionManager)
|
||||
.getActiveSubscriptionInfo(SUB_ID_1);
|
||||
mRequestArgs.setResetApn(SUB_ID_2);
|
||||
doReturn(mSubscriptionInfo2).when(mSubscriptionManager)
|
||||
.getActiveSubscriptionInfo(SUB_ID_2);
|
||||
|
||||
ResetSubscriptionContract target = createTestObject();
|
||||
|
||||
verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), any());
|
||||
|
||||
assertNull(target.getAnyMissingSubscriptionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnyMissingSubscriptionId_returnSubId_whenSubscriptionNotActive() {
|
||||
mRequestArgs.setResetTelephonyAndNetworkPolicyManager(SUB_ID_1);
|
||||
doReturn(mSubscriptionInfo1).when(mSubscriptionManager)
|
||||
.getActiveSubscriptionInfo(SUB_ID_1);
|
||||
mRequestArgs.setResetApn(SUB_ID_2);
|
||||
doReturn(null).when(mSubscriptionManager)
|
||||
.getActiveSubscriptionInfo(SUB_ID_2);
|
||||
|
||||
ResetSubscriptionContract target = createTestObject();
|
||||
|
||||
verify(mSubscriptionManager).addOnSubscriptionsChangedListener(any(), any());
|
||||
|
||||
assertEquals(target.getAnyMissingSubscriptionId(), new Integer(SUB_ID_2));
|
||||
}
|
||||
}
|
||||
66
Settings/tests/unit/src/com/android/settings/TestUtils.java
Normal file
66
Settings/tests/unit/src/com/android/settings/TestUtils.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.android.settings.core.PreferenceXmlParserUtils.METADATA_KEY;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_INCLUDE_PREF_SCREEN;
|
||||
import static com.android.settings.core.PreferenceXmlParserUtils.MetadataFlag.FLAG_NEED_KEY;
|
||||
|
||||
import android.annotation.XmlRes;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.provider.SearchIndexableResource;
|
||||
|
||||
import com.android.settings.core.PreferenceXmlParserUtils;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Convenience methods and constants for testing.
|
||||
*/
|
||||
public class TestUtils {
|
||||
public static final long KILOBYTE = 1024L; // TODO: Change to 1000 in O Robolectric.
|
||||
public static final long MEGABYTE = KILOBYTE * KILOBYTE;
|
||||
public static final long GIGABYTE = KILOBYTE * MEGABYTE;
|
||||
|
||||
public static List<String> getAllXmlKeys(
|
||||
Context context, BaseSearchIndexProvider indexProvider)
|
||||
throws Exception {
|
||||
final List<SearchIndexableResource> resources = indexProvider.getXmlResourcesToIndex(
|
||||
context, true /* not used*/);
|
||||
if (resources == null || resources.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
final List<String> keys = new ArrayList<>();
|
||||
for (SearchIndexableResource res : resources) {
|
||||
keys.addAll(getKeysFromXml(res.xmlResId, context));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static List<String> getKeysFromXml(@XmlRes int xmlResId, Context context)
|
||||
throws Exception {
|
||||
final List<String> keys = new ArrayList<>();
|
||||
final List<Bundle> metadata = PreferenceXmlParserUtils.extractMetadata(context, xmlResId,
|
||||
FLAG_NEED_KEY | FLAG_INCLUDE_PREF_SCREEN);
|
||||
for (Bundle bundle : metadata) {
|
||||
keys.add(bundle.getString(METADATA_KEY));
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TestingSettingsTest {
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private TestingSettings mTestingSettings;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isRadioInfoVisible_returnFalse_whenUserRestricted() {
|
||||
mockService(mContext, Context.USER_SERVICE, UserManager.class, mUserManager);
|
||||
|
||||
doReturn(true).when(mUserManager).isAdminUser();
|
||||
doReturn(false).when(mUserManager).isGuestUser();
|
||||
doReturn(true).when(mUserManager)
|
||||
.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
|
||||
|
||||
assertThat(mTestingSettings.isRadioInfoVisible(mContext)).isFalse();
|
||||
}
|
||||
|
||||
private <T> void mockService(Context context, String serviceName,
|
||||
Class<T> serviceClass, T service) {
|
||||
when(context.getSystemServiceName(serviceClass)).thenReturn(serviceName);
|
||||
when(context.getSystemService(serviceName)).thenReturn(service);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
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.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
import com.android.settings.wifi.helper.SavedWifiHelper;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class UserCredentialsSettingsTest {
|
||||
static final String TEST_ALIAS = "test_alias";
|
||||
static final String TEST_USER_BY_NAME = "test_used_by_name";
|
||||
|
||||
static final String TEXT_PURPOSE_SYSTEM = "credential_for_vpn_and_apps";
|
||||
static final String TEXT_PURPOSE_WIFI = "credential_for_wifi";
|
||||
static final String TEXT_PURPOSE_WIFI_IN_USE = "credential_for_wifi_in_use";
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
@Spy
|
||||
final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@Mock
|
||||
SavedWifiHelper mSavedWifiHelper;
|
||||
@Mock
|
||||
View mView;
|
||||
|
||||
UserCredentialsSettings mSettings;
|
||||
UserCredentialsSettings.Credential mSysCredential =
|
||||
new UserCredentialsSettings.Credential(TEST_ALIAS, Process.SYSTEM_UID);
|
||||
UserCredentialsSettings.Credential mWifiCredential =
|
||||
new UserCredentialsSettings.Credential(TEST_ALIAS, Process.WIFI_UID);
|
||||
List<String> mUsedByNames = Arrays.asList(TEST_USER_BY_NAME);
|
||||
TextView mPurposeView = new TextView(ApplicationProvider.getApplicationContext());
|
||||
TextView mUsedByTitleView = new TextView(ApplicationProvider.getApplicationContext());
|
||||
TextView mUsedByContentView = new TextView(ApplicationProvider.getApplicationContext());
|
||||
|
||||
@Before
|
||||
@UiThreadTest
|
||||
public void setUp() {
|
||||
when(mSavedWifiHelper.isCertificateInUse(any(String.class))).thenReturn(false);
|
||||
when(mSavedWifiHelper.getCertificateNetworkNames(any(String.class)))
|
||||
.thenReturn(new ArrayList<>());
|
||||
when(mView.getTag()).thenReturn(mWifiCredential);
|
||||
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
mSettings = spy(new UserCredentialsSettings());
|
||||
when(mSettings.getContext()).thenReturn(mContext);
|
||||
mSettings.mSavedWifiHelper = mSavedWifiHelper;
|
||||
doNothing().when(mSettings)
|
||||
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onClick_noCredentialInTag_doNothing() {
|
||||
when(mView.getTag()).thenReturn(null);
|
||||
|
||||
mSettings.onClick(mView);
|
||||
|
||||
verify(mSavedWifiHelper, never()).getCertificateNetworkNames(any(String.class));
|
||||
verify(mSettings, never())
|
||||
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onClick_credentialInNotUse_notSetUsedByNamesThenShowDialog() {
|
||||
mWifiCredential.setInUse(false);
|
||||
when(mView.getTag()).thenReturn(mWifiCredential);
|
||||
|
||||
mSettings.onClick(mView);
|
||||
|
||||
verify(mSavedWifiHelper, never()).getCertificateNetworkNames(any(String.class));
|
||||
verify(mSettings)
|
||||
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void onClick_credentialInUse_setUsedByNamesThenShowDialog() {
|
||||
mWifiCredential.setInUse(true);
|
||||
when(mView.getTag()).thenReturn(mWifiCredential);
|
||||
when(mSavedWifiHelper.getCertificateNetworkNames(any(String.class)))
|
||||
.thenReturn(mUsedByNames);
|
||||
|
||||
mSettings.onClick(mView);
|
||||
|
||||
verify(mSavedWifiHelper).getCertificateNetworkNames(any(String.class));
|
||||
assertThat(mWifiCredential.getUsedByNames()).isEqualTo(mUsedByNames);
|
||||
verify(mSettings)
|
||||
.showCredentialDialogFragment(any(UserCredentialsSettings.Credential.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void updatePurposeView_getSystemCert_setTextCorrectly() {
|
||||
mSettings.updatePurposeView(mPurposeView, mSysCredential);
|
||||
|
||||
assertThat(mPurposeView.getText()).isEqualTo(getResString(TEXT_PURPOSE_SYSTEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void updatePurposeView_getWifiCert_setTextCorrectly() {
|
||||
mWifiCredential.setInUse(false);
|
||||
|
||||
mSettings.updatePurposeView(mPurposeView, mWifiCredential);
|
||||
|
||||
assertThat(mPurposeView.getText()).isEqualTo(getResString(TEXT_PURPOSE_WIFI));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void updatePurposeView_isWifiCertInUse_setTextCorrectly() {
|
||||
mWifiCredential.setInUse(true);
|
||||
|
||||
mSettings.updatePurposeView(mPurposeView, mWifiCredential);
|
||||
|
||||
assertThat(mPurposeView.getText()).isEqualTo(getResString(TEXT_PURPOSE_WIFI_IN_USE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void updateUsedByViews_noUsedByName_hideViews() {
|
||||
mWifiCredential.setUsedByNames(new ArrayList<>());
|
||||
|
||||
mSettings.updateUsedByViews(mUsedByTitleView, mUsedByContentView, mWifiCredential);
|
||||
|
||||
assertThat(mUsedByTitleView.getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(mUsedByContentView.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void updateUsedByViews_hasUsedByName_showViews() {
|
||||
mWifiCredential.setUsedByNames(mUsedByNames);
|
||||
|
||||
mSettings.updateUsedByViews(mUsedByTitleView, mUsedByContentView, mWifiCredential);
|
||||
|
||||
assertThat(mUsedByTitleView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(mUsedByContentView.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(mUsedByContentView.getText().toString().contains(TEST_USER_BY_NAME)).isTrue();
|
||||
}
|
||||
|
||||
static String getResString(String name) {
|
||||
return ResourcesUtils.getResourcesString(ApplicationProvider.getApplicationContext(), name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.android.settings.accessibility.AccessibilityUtil.State.OFF;
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AccessibilityShortcutPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
private AccessibilityShortcutPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mController = new AccessibilityShortcutPreferenceController(mContext,
|
||||
"accessibility_shortcut_preference");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledShortcutOnLockScreen_shouldReturnTrue() {
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, ON, UserHandle.USER_CURRENT);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledShortcutOnLockScreen_shouldReturnFalse() {
|
||||
Settings.Secure.putIntForUser(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, OFF,
|
||||
UserHandle.USER_CURRENT);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setTrue_shouldEnableShortcutOnLockScreen() {
|
||||
mController.setChecked(true);
|
||||
|
||||
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, OFF,
|
||||
UserHandle.USER_CURRENT)).isEqualTo(ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setFalse_shouldDisableShortcutOnLockScreen() {
|
||||
mController.setChecked(false);
|
||||
|
||||
assertThat(Settings.Secure.getIntForUser(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, ON,
|
||||
UserHandle.USER_CURRENT)).isEqualTo(OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.android.settings.accessibility.DisableAnimationsPreferenceController.ANIMATION_OFF_VALUE;
|
||||
import static com.android.settings.accessibility.DisableAnimationsPreferenceController.ANIMATION_ON_VALUE;
|
||||
import static com.android.settings.accessibility.DisableAnimationsPreferenceController.TOGGLE_ANIMATION_TARGETS;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DisableAnimationsPreferenceControllerTest {
|
||||
|
||||
private static final String TEST_PREFERENCE_KEY = "disable_animation";
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private Looper mLooper;
|
||||
|
||||
private PreferenceScreen mScreen;
|
||||
private SwitchPreference mPreference;
|
||||
private DisableAnimationsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
mLooper = Looper.myLooper();
|
||||
PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
mScreen = preferenceManager.createPreferenceScreen(mContext);
|
||||
final SwitchPreference preference = new SwitchPreference(mContext);
|
||||
preference.setKey(TEST_PREFERENCE_KEY);
|
||||
preference.setPersistent(false);
|
||||
mScreen.addPreference(preference);
|
||||
|
||||
mController = new DisableAnimationsPreferenceController(mContext, TEST_PREFERENCE_KEY);
|
||||
mController.displayPreference(mScreen);
|
||||
mPreference = mScreen.findPreference(TEST_PREFERENCE_KEY);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
// calling Settings.Global.resetToDefaults doesn't work somehow
|
||||
// one could check if it works by running the test ones, and see if the settings
|
||||
// that were changed being restored to default
|
||||
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_shouldReturnAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledAnimation_shouldReturnFalse() {
|
||||
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledAnimation_shouldReturnTrue() {
|
||||
setAnimationScaleAndWaitForUpdate(ANIMATION_OFF_VALUE);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_disabledAnimation_shouldDisableAnimationTargets() {
|
||||
mController.setChecked(true);
|
||||
|
||||
for (String animationSetting : TOGGLE_ANIMATION_TARGETS) {
|
||||
final float value = Settings.Global.getFloat(mContext.getContentResolver(),
|
||||
animationSetting, /* def= */ -1.0f);
|
||||
assertThat(Float.compare(value, ANIMATION_OFF_VALUE)).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_enabledAnimation_shouldEnableAnimationTargets() {
|
||||
mController.setChecked(false);
|
||||
|
||||
for (String animationSetting : TOGGLE_ANIMATION_TARGETS) {
|
||||
final float value = Settings.Global.getFloat(mContext.getContentResolver(),
|
||||
animationSetting, /* def= */ -1.0f);
|
||||
assertThat(Float.compare(value, ANIMATION_ON_VALUE)).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_enabledAnimation_shouldReturnFalse() {
|
||||
mController.onStart();
|
||||
|
||||
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_disabledAnimation_shouldReturnTrue() {
|
||||
mController.onStart();
|
||||
|
||||
setAnimationScaleAndWaitForUpdate(ANIMATION_OFF_VALUE);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_shouldNotUpdateTargets() {
|
||||
mPreference.setChecked(true);
|
||||
mController.onStart();
|
||||
mController.onStop();
|
||||
|
||||
setAnimationScaleAndWaitForUpdate(ANIMATION_ON_VALUE);
|
||||
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
private void setAnimationScaleAndWaitForUpdate(float newValue) {
|
||||
ContentResolver resolver = mContext.getContentResolver();
|
||||
CountDownLatch countDownLatch = new CountDownLatch(TOGGLE_ANIMATION_TARGETS.size());
|
||||
ContentObserver settingsObserver = new ContentObserver(new Handler(mLooper)) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, @Nullable Uri uri) {
|
||||
countDownLatch.countDown();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
for (String key : TOGGLE_ANIMATION_TARGETS) {
|
||||
resolver.registerContentObserver(Settings.Global.getUriFor(key),
|
||||
false, settingsObserver, UserHandle.USER_ALL);
|
||||
Settings.Global.putFloat(mContext.getContentResolver(), key,
|
||||
newValue);
|
||||
}
|
||||
countDownLatch.await(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Assert.fail(e.getMessage());
|
||||
} finally {
|
||||
resolver.unregisterContentObserver(settingsObserver);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.os.AsyncTask;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DisplaySizeDataTest {
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private DisplaySizeData mDisplaySizeData;
|
||||
private int mInitialIndex;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mDisplaySizeData = new DisplaySizeData(mContext);
|
||||
mInitialIndex = mDisplaySizeData.getInitialIndex();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() throws InterruptedException {
|
||||
mDisplaySizeData.commit(mInitialIndex);
|
||||
waitForDisplayChangesSynchronously();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commit_success() throws InterruptedException {
|
||||
final int progress = mDisplaySizeData.getValues().size() - 1;
|
||||
Assume.assumeTrue("We need more default display size to make the test effective",
|
||||
mInitialIndex != progress && progress > 0);
|
||||
|
||||
mDisplaySizeData.commit(progress);
|
||||
waitForDisplayChangesSynchronously();
|
||||
|
||||
final int density = mContext.getResources().getDisplayMetrics().densityDpi;
|
||||
assertThat(density).isEqualTo(mDisplaySizeData.getValues().get(progress));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the display change propagated synchronously.
|
||||
* <p/>
|
||||
* Note: Currently, DisplayDensityUtils uses AsyncTask to change the display density
|
||||
* asynchronously. If in the future we stop using the deprecated AsyncTask, we will need to
|
||||
* update the wait mechanism in the test.
|
||||
*/
|
||||
private void waitForDisplayChangesSynchronously() throws InterruptedException {
|
||||
// The default AsyncTask.execute run tasks in serial order.
|
||||
// Posting a new runnable and wait for it to finish means the previous tasks are all done.
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AsyncTask.execute(latch::countDown);
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.provider.Settings;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for {@link FontWeightAdjustmentPreferenceController}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FontWeightAdjustmentPreferenceControllerTest {
|
||||
private static final int ON = FontWeightAdjustmentPreferenceController.BOLD_TEXT_ADJUSTMENT;
|
||||
private static final int OFF = 0;
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
private FontWeightAdjustmentPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mController = new FontWeightAdjustmentPreferenceController(
|
||||
mContext, "font_weight_adjustment");
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
Settings.Secure.resetToDefaults(mContext.getContentResolver(), /* tag= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledBoldText_shouldReturnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, ON);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledBoldText_shouldReturnFalse() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setTrue_shouldEnableBoldText() {
|
||||
mController.setChecked(true);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF)).isEqualTo(ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setFalse_shouldDisableBoldText() {
|
||||
mController.setChecked(false);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF)).isEqualTo(OFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetState_shouldDisableBoldText() {
|
||||
mController.setChecked(true);
|
||||
|
||||
mController.resetState();
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.FONT_WEIGHT_ADJUSTMENT, OFF)).isEqualTo(OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for {@link HighTextContrastPreferenceController}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class HighTextContrastPreferenceControllerTest {
|
||||
|
||||
private static final String PREF_KEY = "text_contrast";
|
||||
private static final int ON = 1;
|
||||
private static final int OFF = 0;
|
||||
private static final int UNKNOWN = -1;
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
private HighTextContrastPreferenceController mController;
|
||||
private PreferenceScreen mScreen;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
mScreen = preferenceManager.createPreferenceScreen(mContext);
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mPreference.setKey(PREF_KEY);
|
||||
mScreen.addPreference(mPreference);
|
||||
mController = new HighTextContrastPreferenceController(mContext, PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledTextContrast_shouldReturnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, ON);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledTextContrast_shouldReturnFalse() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setTrue_shouldEnableTextContrast() {
|
||||
mController.setChecked(true);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(ON);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setFalse_shouldDisableTextContrast() {
|
||||
mController.setChecked(false);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(OFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetState_shouldDisableTextContrast() {
|
||||
mController.displayPreference(mScreen);
|
||||
mController.setChecked(true);
|
||||
mPreference.setChecked(true);
|
||||
|
||||
mController.resetState();
|
||||
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.android.settings.accessibility.LargePointerIconPreferenceController.OFF;
|
||||
import static com.android.settings.accessibility.LargePointerIconPreferenceController.ON;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LargePointerIconPreferenceControllerTest {
|
||||
|
||||
private static final int UNKNOWN = -1;
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
private LargePointerIconPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mController = new LargePointerIconPreferenceController(mContext, "large_pointer");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_shouldReturnAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledLargePointer_shouldReturnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, ON);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledLargePointer_shouldReturnFalse() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, OFF);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_enabled_shouldEnableLargePointer() {
|
||||
mController.setChecked(true);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, UNKNOWN)).isEqualTo(ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_disabled_shouldDisableLargePointer() {
|
||||
mController.setChecked(false);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON, UNKNOWN)).isEqualTo(OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MagnificationPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private MagnificationPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new MagnificationPreferenceController(mContext, "magnification");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_shouldReturnAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.ComponentName;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Tests for {@link PreferredShortcut} */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferredShortcutTest {
|
||||
|
||||
private static final String STUB_COMPONENT_NAME = new ComponentName("com.example",
|
||||
"com.example.testActivity").flattenToString();
|
||||
private static final int STUB_TYPE = 3;
|
||||
|
||||
@Test
|
||||
public void fromString_matchMemberObject() {
|
||||
final String preferredShortcutString = STUB_COMPONENT_NAME + ":" + STUB_TYPE;
|
||||
|
||||
final PreferredShortcut shortcut = PreferredShortcut.fromString(preferredShortcutString);
|
||||
|
||||
assertThat(shortcut.getComponentName()).isEqualTo(STUB_COMPONENT_NAME);
|
||||
assertThat(shortcut.getType()).isEqualTo(STUB_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toString_matchString() {
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(STUB_COMPONENT_NAME, STUB_TYPE);
|
||||
|
||||
final String preferredShortcutString = shortcut.toString();
|
||||
|
||||
assertThat(preferredShortcutString).isEqualTo(STUB_COMPONENT_NAME + ":" + STUB_TYPE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assertSameObject() {
|
||||
final String preferredShortcutString = STUB_COMPONENT_NAME + ":" + STUB_TYPE;
|
||||
final PreferredShortcut targetShortcut = PreferredShortcut.fromString(
|
||||
preferredShortcutString);
|
||||
|
||||
assertThat(targetShortcut).isEqualTo(new PreferredShortcut(STUB_COMPONENT_NAME, STUB_TYPE));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
|
||||
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.accessibility.common.ShortcutConstants;
|
||||
import com.android.internal.accessibility.util.ShortcutUtils;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/** Tests for {@link PreferredShortcuts} */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferredShortcutsTest {
|
||||
|
||||
private static final String PACKAGE_NAME_1 = "com.test1.example";
|
||||
private static final String CLASS_NAME_1 = PACKAGE_NAME_1 + ".test1";
|
||||
private static final ComponentName COMPONENT_NAME_1 = new ComponentName(PACKAGE_NAME_1,
|
||||
CLASS_NAME_1);
|
||||
private static final String PACKAGE_NAME_2 = "com.test2.example";
|
||||
private static final String CLASS_NAME_2 = PACKAGE_NAME_2 + ".test2";
|
||||
private static final ComponentName COMPONENT_NAME_2 = new ComponentName(PACKAGE_NAME_2,
|
||||
CLASS_NAME_2);
|
||||
private static final ContentResolver sContentResolver =
|
||||
ApplicationProvider.getApplicationContext().getContentResolver();
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
clearShortcuts();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanUp() {
|
||||
clearShortcuts();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveUserShortcutType_fromSingleData_matchSavedType() {
|
||||
final int type = 1;
|
||||
final PreferredShortcut shortcut = new PreferredShortcut(COMPONENT_NAME_1.flattenToString(),
|
||||
type);
|
||||
|
||||
PreferredShortcuts.saveUserShortcutType(mContext, shortcut);
|
||||
final int retrieveType = PreferredShortcuts.retrieveUserShortcutType(mContext,
|
||||
COMPONENT_NAME_1.flattenToString());
|
||||
|
||||
assertThat(retrieveType).isEqualTo(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retrieveUserShortcutType_fromMultiData_matchSavedType() {
|
||||
final int type1 = 1;
|
||||
final int type2 = 2;
|
||||
final PreferredShortcut shortcut1 = new PreferredShortcut(
|
||||
COMPONENT_NAME_1.flattenToString(), type1);
|
||||
final PreferredShortcut shortcut2 = new PreferredShortcut(
|
||||
COMPONENT_NAME_2.flattenToString(), type2);
|
||||
|
||||
PreferredShortcuts.saveUserShortcutType(mContext, shortcut1);
|
||||
PreferredShortcuts.saveUserShortcutType(mContext, shortcut2);
|
||||
final int retrieveType = PreferredShortcuts.retrieveUserShortcutType(mContext,
|
||||
COMPONENT_NAME_1.flattenToString());
|
||||
|
||||
assertThat(retrieveType).isEqualTo(type1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferredShortcutsFromSetting_magnificationWithTripleTapAndVolumeKeyShortcuts_preferredShortcutsMatches() {
|
||||
ShortcutUtils.optInValueToSettings(mContext, ShortcutConstants.UserShortcutType.HARDWARE,
|
||||
MAGNIFICATION_CONTROLLER_NAME);
|
||||
Settings.Secure.putInt(
|
||||
sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||
AccessibilityUtil.State.ON);
|
||||
|
||||
PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext,
|
||||
Set.of(MAGNIFICATION_CONTROLLER_NAME));
|
||||
int expectedShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
|
||||
| ShortcutConstants.UserShortcutType.TRIPLETAP;
|
||||
|
||||
assertThat(
|
||||
PreferredShortcuts.retrieveUserShortcutType(
|
||||
mContext, MAGNIFICATION_CONTROLLER_NAME
|
||||
))
|
||||
.isEqualTo(expectedShortcutTypes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferredShortcutsFromSetting_magnificationWithNoActiveShortcuts_noChangesOnPreferredShortcutTypes() {
|
||||
int expectedShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
|
||||
| ShortcutConstants.UserShortcutType.SOFTWARE;
|
||||
PreferredShortcuts.saveUserShortcutType(mContext,
|
||||
new PreferredShortcut(MAGNIFICATION_CONTROLLER_NAME, expectedShortcutTypes));
|
||||
|
||||
|
||||
PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext,
|
||||
Set.of(MAGNIFICATION_CONTROLLER_NAME));
|
||||
|
||||
|
||||
assertThat(
|
||||
PreferredShortcuts.retrieveUserShortcutType(
|
||||
mContext, MAGNIFICATION_CONTROLLER_NAME
|
||||
))
|
||||
.isEqualTo(expectedShortcutTypes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferredShortcutsFromSetting_multipleComponents_preferredShortcutsMatches() {
|
||||
String target1 = COLOR_INVERSION_COMPONENT_NAME.flattenToString();
|
||||
String target2 = DALTONIZER_COMPONENT_NAME.flattenToString();
|
||||
|
||||
Settings.Secure.putString(sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, target1);
|
||||
Settings.Secure.putString(sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
|
||||
target1 + ShortcutConstants.SERVICES_SEPARATOR + target2);
|
||||
|
||||
int target1ShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE
|
||||
| ShortcutConstants.UserShortcutType.SOFTWARE;
|
||||
int target2ShortcutTypes = ShortcutConstants.UserShortcutType.HARDWARE;
|
||||
|
||||
PreferredShortcuts.updatePreferredShortcutsFromSettings(mContext, Set.of(target1, target2));
|
||||
|
||||
assertThat(
|
||||
PreferredShortcuts.retrieveUserShortcutType(
|
||||
mContext, target1
|
||||
))
|
||||
.isEqualTo(target1ShortcutTypes);
|
||||
assertThat(
|
||||
PreferredShortcuts.retrieveUserShortcutType(
|
||||
mContext, target2
|
||||
))
|
||||
.isEqualTo(target2ShortcutTypes);
|
||||
}
|
||||
|
||||
private static void clearShortcuts() {
|
||||
Settings.Secure.putString(sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, "");
|
||||
Settings.Secure.putString(sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "");
|
||||
Settings.Secure.putInt(
|
||||
sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
|
||||
AccessibilityUtil.State.OFF);
|
||||
Settings.Secure.putInt(
|
||||
sContentResolver,
|
||||
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED,
|
||||
AccessibilityUtil.State.OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PrimaryMonoPreferenceControllerTest {
|
||||
|
||||
private static final int ON = 1;
|
||||
private static final int OFF = 0;
|
||||
private static final int UNKNOWN = -1;
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
private PrimaryMonoPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mController = new PrimaryMonoPreferenceController(mContext, "test_key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_byDefault_shouldReturnAvailable() {
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledMonoAudio_shouldReturnTrue() {
|
||||
Settings.System.putIntForUser(mContext.getContentResolver(),
|
||||
Settings.System.MASTER_MONO, ON, UserHandle.USER_CURRENT);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledMonoAudio_shouldReturnFalse() {
|
||||
Settings.System.putIntForUser(mContext.getContentResolver(),
|
||||
Settings.System.MASTER_MONO, OFF, UserHandle.USER_CURRENT);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setTrue_shouldEnableMonoAudio() {
|
||||
mController.setChecked(true);
|
||||
|
||||
assertThat(Settings.System.getIntForUser(mContext.getContentResolver(),
|
||||
Settings.System.MASTER_MONO, UNKNOWN, UserHandle.USER_CURRENT)).isEqualTo(ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setFalse_shouldDisableMonoAudio() {
|
||||
mController.setChecked(false);
|
||||
|
||||
assertThat(Settings.System.getIntForUser(mContext.getContentResolver(),
|
||||
Settings.System.MASTER_MONO, UNKNOWN, UserHandle.USER_CURRENT)).isEqualTo(OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ReduceBrightColorsIntensityPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
private ReduceBrightColorsIntensityPreferenceController mPreferenceController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mResources = spy(mContext.getResources());
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
mPreferenceController = new ReduceBrightColorsIntensityPreferenceController(mContext,
|
||||
"rbc_intensity");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_configuredRbcAvailable_enabledRbc_shouldReturnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
|
||||
doReturn(true).when(mResources).getBoolean(
|
||||
R.bool.config_reduceBrightColorsAvailable);
|
||||
assertThat(mPreferenceController.isAvailable()).isTrue();
|
||||
}
|
||||
@Test
|
||||
public void isAvailable_configuredRbcAvailable_disabledRbc_shouldReturnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
|
||||
doReturn(true).when(mResources).getBoolean(
|
||||
R.bool.config_reduceBrightColorsAvailable);
|
||||
assertThat(mPreferenceController.isAvailable()).isTrue();
|
||||
}
|
||||
@Test
|
||||
public void isAvailable_configuredRbcUnavailable_enabledRbc_shouldReturnFalse() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
|
||||
doReturn(false).when(mResources).getBoolean(
|
||||
R.bool.config_reduceBrightColorsAvailable);
|
||||
assertThat(mPreferenceController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_changesTemperature() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
|
||||
mPreferenceController.onPreferenceChange(/* preference= */ null, 20);
|
||||
assertThat(
|
||||
Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0))
|
||||
.isEqualTo(80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rangeOfSlider_staysWithinValidRange() {
|
||||
when(mResources.getInteger(
|
||||
R.integer.config_reduceBrightColorsStrengthMax)).thenReturn(90);
|
||||
when(mResources.getInteger(
|
||||
R.integer.config_reduceBrightColorsStrengthMin)).thenReturn(15);
|
||||
assertThat(mPreferenceController.getMax()).isEqualTo(85);
|
||||
assertThat(mPreferenceController.getMin()).isEqualTo(10);
|
||||
assertThat(mPreferenceController.getMax() - mPreferenceController.getMin())
|
||||
.isEqualTo(75);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 android.provider.Settings;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ReduceBrightColorsPersistencePreferenceControllerTest {
|
||||
private static final String PREF_KEY = "rbc_persist";
|
||||
private static final String RBC_PERSIST =
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_PERSIST_ACROSS_REBOOTS;
|
||||
private static final int ON = 1;
|
||||
private static final int OFF = 0;
|
||||
private static final int UNKNOWN = -1;
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
private final SwitchPreference mPreference = new SwitchPreference(mContext);
|
||||
private final ReduceBrightColorsPersistencePreferenceController mController =
|
||||
new ReduceBrightColorsPersistencePreferenceController(mContext, PREF_KEY);
|
||||
|
||||
@Test
|
||||
public void isChecked_enabledRbc_shouldReturnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(), RBC_PERSIST, ON);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_disabledRbc_shouldReturnFalse() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(), RBC_PERSIST, OFF);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setTrue_shouldEnableRbc() {
|
||||
mController.setChecked(true);
|
||||
|
||||
assertThat(
|
||||
Settings.Secure.getInt(mContext.getContentResolver(), RBC_PERSIST, UNKNOWN))
|
||||
.isEqualTo(ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setFalse_shouldDisableRbc() {
|
||||
mController.setChecked(false);
|
||||
|
||||
assertThat(
|
||||
Settings.Secure.getInt(mContext.getContentResolver(), RBC_PERSIST, UNKNOWN))
|
||||
.isEqualTo(OFF);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ReduceBrightColorsPreferenceControllerTest {
|
||||
private static final String PREF_KEY = "rbc_preference";
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;;
|
||||
private ReduceBrightColorsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mResources = spy(mContext.getResources());
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
mController = new ReduceBrightColorsPreferenceController(mContext,
|
||||
PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_returnSummary() {
|
||||
assertThat(mController.getSummary().toString().contains(
|
||||
resourceString("reduce_bright_colors_preference_summary"))).isTrue();
|
||||
}
|
||||
|
||||
@Ignore("ColorDisplayManager runs in a different thread which results in a race condition")
|
||||
@Test
|
||||
public void isChecked_reduceBrightColorsIsActivated_returnTrue() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Ignore("ColorDisplayManager runs in a different thread which results in a race condition")
|
||||
@Test
|
||||
public void isChecked_reduceBrightColorsIsNotActivated_returnFalse() {
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_configuredRbcAvailable_shouldReturnTrue() {
|
||||
doReturn(true).when(mResources).getBoolean(
|
||||
R.bool.config_reduceBrightColorsAvailable);
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
@Test
|
||||
public void isAvailable_configuredRbcUnAvailable_shouldReturnFalse() {
|
||||
doReturn(false).when(mResources).getBoolean(
|
||||
R.bool.config_reduceBrightColorsAvailable);
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
private int resourceId(String type, String name) {
|
||||
return mContext.getResources().getIdentifier(name, type, mContext.getPackageName());
|
||||
}
|
||||
|
||||
private String resourceString(String name) {
|
||||
return mContext.getResources().getString(resourceId("string", name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"imports": [
|
||||
{
|
||||
"path": "packages/apps/Settings/src/com/android/settings/accessibility/TEST_MAPPING"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.accounts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class RemoveUserFragmentTest {
|
||||
|
||||
@Test
|
||||
public void testClassModifier_shouldBePublic() {
|
||||
final int modifiers = RemoveUserFragment.class.getModifiers();
|
||||
|
||||
assertThat(Modifier.isPublic(modifiers)).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.accounts;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SyncStateSwitchPreferenceTest {
|
||||
|
||||
private Context mContext;
|
||||
private SyncStateSwitchPreference mPreference;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setup_validAuthority_shouldBeVisible() {
|
||||
mPreference = new SyncStateSwitchPreference(mContext, null /* attrs */);
|
||||
|
||||
mPreference.setup(new Account("name", "type"), "authority", mContext.getPackageName(),
|
||||
UserHandle.USER_CURRENT);
|
||||
|
||||
assertThat(mPreference.isVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setup_emptyAuthority_shouldBeInvisible() {
|
||||
mPreference = new SyncStateSwitchPreference(mContext, null /* attrs */);
|
||||
|
||||
mPreference.setup(new Account("name", "type"), null /* authority */,
|
||||
mContext.getPackageName(), UserHandle.USER_CURRENT);
|
||||
|
||||
assertThat(mPreference.isVisible()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.LocaleConfig;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.LocaleList;
|
||||
import android.util.FeatureFlagUtils;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AppLocaleUtilTest {
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private ActivityManager mActivityManager;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private LocaleConfig mLocaleConfig;
|
||||
|
||||
private Context mContext;
|
||||
private String mDisallowedPackage = "com.disallowed.package";
|
||||
private String mAllowedPackage = "com.allowed.package";
|
||||
private List<ResolveInfo> mListResolveInfo;
|
||||
private ApplicationInfo mAppInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
|
||||
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_APP_LOCALE_OPT_IN_ENABLED,
|
||||
true);
|
||||
setDisallowedPackageName(mDisallowedPackage);
|
||||
mAppInfo = new ApplicationInfo();
|
||||
mLocaleConfig = mock(LocaleConfig.class);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
AppLocaleUtil.sLocaleConfig = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canDisplayLocaleUi_showUI() {
|
||||
when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS);
|
||||
when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US"));
|
||||
AppLocaleUtil.sLocaleConfig = mLocaleConfig;
|
||||
setActivityInfo(mAllowedPackage);
|
||||
mAppInfo.packageName = mAllowedPackage;
|
||||
|
||||
assertTrue(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canDisplayLocaleUi_notShowUI_hasPlatformKey() {
|
||||
setActivityInfo(mAllowedPackage);
|
||||
mAppInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
|
||||
mAppInfo.packageName = mAllowedPackage;
|
||||
|
||||
assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canDisplayLocaleUi_notShowUI_noLauncherEntry() {
|
||||
setActivityInfo("");
|
||||
|
||||
assertFalse(AppLocaleUtil.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canDisplayLocaleUi_notShowUI_matchDisallowedPackageList() {
|
||||
setActivityInfo("");
|
||||
mAppInfo.packageName = mDisallowedPackage;
|
||||
|
||||
assertFalse(AppLocaleUtil
|
||||
.canDisplayLocaleUi(mContext, mAppInfo, mListResolveInfo));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPackageLocales_getLocales_success() {
|
||||
when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_SUCCESS);
|
||||
when(mLocaleConfig.getSupportedLocales()).thenReturn(LocaleList.forLanguageTags("en-US"));
|
||||
|
||||
LocaleList result = AppLocaleUtil.getPackageLocales(mLocaleConfig);
|
||||
|
||||
assertFalse(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPackageLocales_getLocales_failed() {
|
||||
when(mLocaleConfig.getStatus()).thenReturn(LocaleConfig.STATUS_PARSING_FAILED);
|
||||
|
||||
LocaleList result = AppLocaleUtil.getPackageLocales(mLocaleConfig);
|
||||
|
||||
assertNull(result);
|
||||
}
|
||||
|
||||
private void setDisallowedPackageName(String packageName) {
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mResources.getStringArray(anyInt())).thenReturn(new String[]{packageName});
|
||||
}
|
||||
|
||||
private void setActivityInfo(String packageName) {
|
||||
ResolveInfo resolveInfo = mock(ResolveInfo.class);
|
||||
ActivityInfo activityInfo = mock(ActivityInfo.class);
|
||||
activityInfo.packageName = packageName;
|
||||
resolveInfo.activityInfo = activityInfo;
|
||||
mListResolveInfo = new ArrayList<>();
|
||||
mListResolveInfo.add(resolveInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AlarmManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import libcore.util.EmptyArray;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AppStateAlarmsAndRemindersBridgeTest {
|
||||
private static final String TEST_PACKAGE = "com.example.test.1";
|
||||
private static final int TEST_UID = 12345;
|
||||
|
||||
@Mock
|
||||
private AlarmManager mAlarmManager;
|
||||
@Mock
|
||||
private PowerExemptionManager mPowerExemptionManager;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void initMocks() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alarmsAndRemindersState_shouldBeVisible() {
|
||||
boolean seaPermissionRequested;
|
||||
boolean ueaPermissionRequested;
|
||||
boolean seaPermissionGranted;
|
||||
boolean allowListed;
|
||||
|
||||
for (int i = 0; i < (1 << 4); i++) {
|
||||
seaPermissionRequested = (i & 1) != 0;
|
||||
ueaPermissionRequested = (i & (1 << 1)) != 0;
|
||||
seaPermissionGranted = (i & (1 << 2)) != 0;
|
||||
allowListed = (i & (1 << 3)) != 0;
|
||||
|
||||
final boolean visible = new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
|
||||
seaPermissionRequested,
|
||||
ueaPermissionRequested,
|
||||
seaPermissionGranted,
|
||||
allowListed).shouldBeVisible();
|
||||
|
||||
assertWithMessage("Wrong return value " + visible
|
||||
+ " for {seaPermissionRequested = " + seaPermissionRequested
|
||||
+ ", ueaPermissionRequested = " + ueaPermissionRequested
|
||||
+ ", seaPermissionGranted = " + seaPermissionGranted
|
||||
+ ", allowListed = " + allowListed + "}")
|
||||
.that(visible)
|
||||
.isEqualTo(seaPermissionRequested && !ueaPermissionRequested && !allowListed);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alarmsAndRemindersState_isAllowed() {
|
||||
boolean seaPermissionRequested;
|
||||
boolean ueaPermissionRequested;
|
||||
boolean seaPermissionGranted;
|
||||
boolean allowListed;
|
||||
|
||||
for (int i = 0; i < (1 << 4); i++) {
|
||||
seaPermissionRequested = (i & 1) != 0;
|
||||
ueaPermissionRequested = (i & (1 << 1)) != 0;
|
||||
seaPermissionGranted = (i & (1 << 2)) != 0;
|
||||
allowListed = (i & (1 << 3)) != 0;
|
||||
|
||||
final boolean allowed = new AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState(
|
||||
seaPermissionRequested,
|
||||
ueaPermissionRequested,
|
||||
seaPermissionGranted,
|
||||
allowListed).isAllowed();
|
||||
|
||||
assertWithMessage("Wrong return value " + allowed
|
||||
+ " for {seaPermissionRequested = " + seaPermissionRequested
|
||||
+ ", ueaPermissionRequested = " + ueaPermissionRequested
|
||||
+ ", seaPermissionGranted = " + seaPermissionGranted
|
||||
+ ", allowListed = " + allowListed + "}")
|
||||
.that(allowed)
|
||||
.isEqualTo(seaPermissionGranted || ueaPermissionRequested || allowListed);
|
||||
}
|
||||
}
|
||||
|
||||
private PackageInfo createPackageInfoWithPermissions(String... requestedPermissions) {
|
||||
final PackageInfo info = new PackageInfo();
|
||||
info.requestedPermissions = requestedPermissions;
|
||||
return info;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPermissionState_SeaGrantedNoUeaNoAllowlist() throws Exception {
|
||||
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
|
||||
null, null);
|
||||
bridge.mAlarmManager = mAlarmManager;
|
||||
bridge.mPackageManager = mPackageManager;
|
||||
bridge.mPowerExemptionManager = mPowerExemptionManager;
|
||||
|
||||
doReturn(true).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
|
||||
UserHandle.getUserId(TEST_UID));
|
||||
doReturn(createPackageInfoWithPermissions(Manifest.permission.SCHEDULE_EXACT_ALARM))
|
||||
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
|
||||
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
|
||||
|
||||
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
|
||||
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
|
||||
assertThat(state.shouldBeVisible()).isTrue();
|
||||
assertThat(state.isAllowed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPermissionState_requestsBothSeaDeniedNoAllowlist() throws Exception {
|
||||
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
|
||||
null, null);
|
||||
bridge.mAlarmManager = mAlarmManager;
|
||||
bridge.mPackageManager = mPackageManager;
|
||||
bridge.mPowerExemptionManager = mPowerExemptionManager;
|
||||
|
||||
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
|
||||
UserHandle.getUserId(TEST_UID));
|
||||
doReturn(createPackageInfoWithPermissions(
|
||||
Manifest.permission.SCHEDULE_EXACT_ALARM,
|
||||
Manifest.permission.USE_EXACT_ALARM))
|
||||
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
|
||||
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
|
||||
|
||||
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
|
||||
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
|
||||
assertThat(state.shouldBeVisible()).isFalse();
|
||||
assertThat(state.isAllowed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPermissionState_requestsNoneNoAllowlist() throws Exception {
|
||||
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
|
||||
null, null);
|
||||
bridge.mAlarmManager = mAlarmManager;
|
||||
bridge.mPackageManager = mPackageManager;
|
||||
bridge.mPowerExemptionManager = mPowerExemptionManager;
|
||||
|
||||
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
|
||||
UserHandle.getUserId(TEST_UID));
|
||||
doReturn(createPackageInfoWithPermissions(EmptyArray.STRING))
|
||||
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
|
||||
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
|
||||
|
||||
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
|
||||
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
|
||||
assertThat(state.shouldBeVisible()).isFalse();
|
||||
assertThat(state.isAllowed()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPermissionState_requestsOnlyUeaNoAllowlist() throws Exception {
|
||||
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
|
||||
null, null);
|
||||
bridge.mAlarmManager = mAlarmManager;
|
||||
bridge.mPackageManager = mPackageManager;
|
||||
bridge.mPowerExemptionManager = mPowerExemptionManager;
|
||||
|
||||
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
|
||||
UserHandle.getUserId(TEST_UID));
|
||||
doReturn(createPackageInfoWithPermissions(Manifest.permission.USE_EXACT_ALARM))
|
||||
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
|
||||
doReturn(false).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
|
||||
|
||||
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
|
||||
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
|
||||
assertThat(state.shouldBeVisible()).isFalse();
|
||||
assertThat(state.isAllowed()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPermissionState_requestsNoneButAllowlisted() throws Exception {
|
||||
AppStateAlarmsAndRemindersBridge bridge = new AppStateAlarmsAndRemindersBridge(mContext,
|
||||
null, null);
|
||||
bridge.mAlarmManager = mAlarmManager;
|
||||
bridge.mPackageManager = mPackageManager;
|
||||
bridge.mPowerExemptionManager = mPowerExemptionManager;
|
||||
|
||||
doReturn(false).when(mAlarmManager).hasScheduleExactAlarm(TEST_PACKAGE,
|
||||
UserHandle.getUserId(TEST_UID));
|
||||
doReturn(createPackageInfoWithPermissions(EmptyArray.STRING))
|
||||
.when(mPackageManager).getPackageInfoAsUser(eq(TEST_PACKAGE), anyInt(), anyInt());
|
||||
doReturn(true).when(mPowerExemptionManager).isAllowListed(TEST_PACKAGE, true);
|
||||
|
||||
AppStateAlarmsAndRemindersBridge.AlarmsAndRemindersState state =
|
||||
bridge.createPermissionState(TEST_PACKAGE, TEST_UID);
|
||||
assertThat(state.shouldBeVisible()).isFalse();
|
||||
assertThat(state.isAllowed()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.android.settings.applications;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settingslib.applications.ApplicationsState.AppEntry;
|
||||
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class AppStateAppBatteryUsageBridgeTest {
|
||||
private static final String TEST_PACKAGE_1 = "com.example.test.pkg1";
|
||||
private static final String TEST_PACKAGE_2 = "com.example.test.pkg2";
|
||||
private static final int UID_1 = 12345;
|
||||
private static final int UID_2 = 7654321;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private AppOpsManager mAppOpsManager;
|
||||
@Mock
|
||||
private PowerAllowlistBackend mPowerAllowlistBackend;
|
||||
|
||||
@Before
|
||||
public void initMocks() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtraInfo_updatesRestricted() {
|
||||
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(false);
|
||||
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
|
||||
UID_1, TEST_PACKAGE_1)).thenReturn(AppOpsManager.MODE_IGNORED);
|
||||
AppStateAppBatteryUsageBridge bridge =
|
||||
new AppStateAppBatteryUsageBridge(mContext, null, null);
|
||||
bridge.mAppOpsManager = mAppOpsManager;
|
||||
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
|
||||
AppEntry entry = new AppEntry(mContext, null, 0);
|
||||
|
||||
bridge.updateExtraInfo(entry, TEST_PACKAGE_1, UID_1);
|
||||
|
||||
assertThat(entry.extraInfo.getClass())
|
||||
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
|
||||
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
|
||||
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_RESTRICTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateExtraInfo_updatesUnrestricted() {
|
||||
when(mPowerAllowlistBackend.isAllowlisted(TEST_PACKAGE_1, UID_1)).thenReturn(true);
|
||||
when(mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
|
||||
UID_2, TEST_PACKAGE_2)).thenReturn(AppOpsManager.MODE_ALLOWED);
|
||||
AppStateAppBatteryUsageBridge bridge =
|
||||
new AppStateAppBatteryUsageBridge(mContext, null, null);
|
||||
bridge.mAppOpsManager = mAppOpsManager;
|
||||
bridge.mPowerAllowlistBackend = mPowerAllowlistBackend;
|
||||
AppEntry entry = new AppEntry(mContext, null, 0);
|
||||
|
||||
bridge.updateExtraInfo(entry, TEST_PACKAGE_2, UID_2);
|
||||
|
||||
assertThat(entry.extraInfo.getClass())
|
||||
.isEqualTo(AppStateAppBatteryUsageBridge.AppBatteryUsageDetails.class);
|
||||
assertThat(AppStateAppBatteryUsageBridge.getAppBatteryUsageDetailsMode(entry))
|
||||
.isEqualTo(AppStateAppBatteryUsageBridge.MODE_UNRESTRICTED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.app.AppOpsManager;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AppStateInstallAppsBridgeTest {
|
||||
|
||||
@Test
|
||||
public void testInstallAppsStateCanInstallApps() {
|
||||
AppStateInstallAppsBridge.InstallAppsState appState =
|
||||
new AppStateInstallAppsBridge.InstallAppsState();
|
||||
assertThat(appState.canInstallApps()).isFalse();
|
||||
|
||||
appState.permissionRequested = true;
|
||||
assertThat(appState.canInstallApps()).isFalse();
|
||||
|
||||
appState.appOpMode = AppOpsManager.MODE_ALLOWED;
|
||||
assertThat(appState.canInstallApps()).isTrue();
|
||||
|
||||
appState.appOpMode = AppOpsManager.MODE_ERRORED;
|
||||
assertThat(appState.canInstallApps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstallAppsStateIsPotentialAppSource() {
|
||||
AppStateInstallAppsBridge.InstallAppsState appState =
|
||||
new AppStateInstallAppsBridge.InstallAppsState();
|
||||
assertThat(appState.isPotentialAppSource()).isFalse();
|
||||
|
||||
appState.appOpMode = AppOpsManager.MODE_ERRORED;
|
||||
assertThat(appState.isPotentialAppSource()).isTrue();
|
||||
|
||||
appState.permissionRequested = true;
|
||||
appState.appOpMode = AppOpsManager.MODE_DEFAULT;
|
||||
assertThat(appState.isPotentialAppSource()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EnterpriseDefaultAppsTest {
|
||||
@Test
|
||||
public void testNumberOfIntentsCorrelateWithUI() {
|
||||
final int[] concatenation_templates =
|
||||
new int[]{0 /* no need for single app name */,
|
||||
R.string.app_names_concatenation_template_2,
|
||||
R.string.app_names_concatenation_template_3};
|
||||
for (EnterpriseDefaultApps app : EnterpriseDefaultApps.values()) {
|
||||
assertTrue("Number of intents should be limited by number of apps the UI can show",
|
||||
app.getIntents().length <= concatenation_templates.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
|
||||
|
||||
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
|
||||
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.when;
|
||||
|
||||
import android.app.usage.IUsageStatsManager;
|
||||
import android.app.usage.UsageStatsManager;
|
||||
import android.apphibernation.AppHibernationManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Looper;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class HibernatedAppsPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
PackageManager mPackageManager;
|
||||
@Mock
|
||||
AppHibernationManager mAppHibernationManager;
|
||||
@Mock
|
||||
IUsageStatsManager mIUsageStatsManager;
|
||||
PreferenceScreen mPreferenceScreen;
|
||||
private static final String KEY = "key";
|
||||
private Context mContext;
|
||||
private HibernatedAppsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
MockitoAnnotations.initMocks(this);
|
||||
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
|
||||
"true", false);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mContext.getSystemService(AppHibernationManager.class))
|
||||
.thenReturn(mAppHibernationManager);
|
||||
when(mContext.getSystemService(UsageStatsManager.class)).thenReturn(
|
||||
new UsageStatsManager(mContext, mIUsageStatsManager));
|
||||
|
||||
PreferenceManager manager = new PreferenceManager(mContext);
|
||||
mPreferenceScreen = manager.createPreferenceScreen(mContext);
|
||||
Preference preference = mock(Preference.class);
|
||||
when(preference.getKey()).thenReturn(KEY);
|
||||
mPreferenceScreen.addPreference(preference);
|
||||
|
||||
mController = new HibernatedAppsPreferenceController(mContext, KEY,
|
||||
command -> command.run());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_featureDisabled_shouldNotReturnAvailable() {
|
||||
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
|
||||
"false", true);
|
||||
|
||||
assertThat((mController).getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.appcompat;
|
||||
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
|
||||
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
|
||||
import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
|
||||
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE;
|
||||
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE;
|
||||
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE;
|
||||
|
||||
import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN;
|
||||
import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS;
|
||||
import static com.android.window.flags.Flags.FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
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.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* To run this test: atest SettingsUnitTests:UserAspectRatioManagerTest
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class UserAspectRatioManagerTest {
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
|
||||
private final String mPackageName = "com.test.mypackage";
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
private FakeUserAspectRatioManager mUtils;
|
||||
private String mOriginalSettingsFlag;
|
||||
private String mOriginalFullscreenFlag;
|
||||
private IPackageManager mIPm;
|
||||
private PackageManager mPm;
|
||||
private List<LauncherActivityInfo> mLauncherActivities;
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException, PackageManager.NameNotFoundException {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mResources = mock(Resources.class);
|
||||
final LauncherApps launcherApps = mock(LauncherApps.class);
|
||||
mLauncherActivities = mock(List.class);
|
||||
mIPm = mock(IPackageManager.class);
|
||||
mPm = mock(PackageManager.class);
|
||||
when(mContext.getPackageManager()).thenReturn(mPm);
|
||||
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mContext.getSystemService(LauncherApps.class)).thenReturn(launcherApps);
|
||||
enableAllDefaultAspectRatioOptions();
|
||||
mSetFlagsRule.disableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
|
||||
|
||||
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
|
||||
|
||||
doReturn(mLauncherActivities).when(launcherApps).getActivityList(anyString(), any());
|
||||
|
||||
mOriginalSettingsFlag = DeviceConfig.getProperty(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS);
|
||||
setAspectRatioSettingsBuildTimeFlagEnabled(true);
|
||||
setAspectRatioSettingsDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */);
|
||||
|
||||
mOriginalFullscreenFlag = DeviceConfig.getProperty(
|
||||
DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN);
|
||||
setAspectRatioFullscreenBuildTimeFlagEnabled(true);
|
||||
setAspectRatioFullscreenDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */);
|
||||
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true);
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true);
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
setAspectRatioSettingsDeviceConfigEnabled(mOriginalSettingsFlag, true /* makeDefault */);
|
||||
setAspectRatioFullscreenDeviceConfigEnabled(mOriginalFullscreenFlag,
|
||||
true /* makeDefault */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanDisplayAspectRatioUi() {
|
||||
final ApplicationInfo canDisplay = new ApplicationInfo();
|
||||
canDisplay.packageName = mPackageName;
|
||||
|
||||
doReturn(false).when(mLauncherActivities).isEmpty();
|
||||
assertTrue(mUtils.canDisplayAspectRatioUi(canDisplay));
|
||||
|
||||
final ApplicationInfo noLauncherEntry = new ApplicationInfo();
|
||||
noLauncherEntry.packageName = mPackageName;
|
||||
|
||||
doReturn(true).when(mLauncherActivities).isEmpty();
|
||||
assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanDisplayAspectRatioUi_hasLauncher_propertyFalse_returnFalse()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, false);
|
||||
doReturn(false).when(mLauncherActivities).isEmpty();
|
||||
|
||||
final ApplicationInfo canDisplay = new ApplicationInfo();
|
||||
canDisplay.packageName = mPackageName;
|
||||
|
||||
assertFalse(mUtils.canDisplayAspectRatioUi(canDisplay));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanDisplayAspectRatioUi_noLauncher_propertyTrue_returnFalse()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true);
|
||||
doReturn(true).when(mLauncherActivities).isEmpty();
|
||||
|
||||
final ApplicationInfo noLauncherEntry = new ApplicationInfo();
|
||||
noLauncherEntry.packageName = mPackageName;
|
||||
|
||||
assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFeatureEnabled() {
|
||||
assertTrue(UserAspectRatioManager.isFeatureEnabled(mContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFeatureEnabled_disabledBuildTimeFlag_returnFalse() {
|
||||
setAspectRatioSettingsBuildTimeFlagEnabled(false);
|
||||
assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFeatureEnabled_disabledRuntimeFlag_returnFalse() {
|
||||
setAspectRatioSettingsDeviceConfigEnabled("false" /* enabled */, false /* makeDefault */);
|
||||
assertFalse(UserAspectRatioManager.isFeatureEnabled(mContext));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFullscreenOptionEnabled() {
|
||||
assertTrue(mUtils.isFullscreenOptionEnabled(mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFullscreenOptionEnabled_settingsDisabled_returnFalse() {
|
||||
setAspectRatioFullscreenBuildTimeFlagEnabled(false);
|
||||
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFullscreenOptionEnabled_disabledBuildTimeFlag_returnFalse() {
|
||||
setAspectRatioFullscreenBuildTimeFlagEnabled(false);
|
||||
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFullscreenOptionEnabled_disabledRuntimeFlag_returnFalse() {
|
||||
setAspectRatioFullscreenDeviceConfigEnabled("false" /* enabled */, false /*makeDefault */);
|
||||
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFullscreenOptionEnabled_propertyFalse_returnsFalse()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, false);
|
||||
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsFullscreenOptionEnabled_propertyTrue_configDisabled_returnsFalse()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true);
|
||||
setAspectRatioFullscreenDeviceConfigEnabled("false" /* enabled */, false /*makeDefault */);
|
||||
|
||||
assertFalse(mUtils.isFullscreenOptionEnabled(mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasAspectRatioOption_fullscreen() {
|
||||
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN,
|
||||
mPackageName));
|
||||
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
|
||||
mPackageName));
|
||||
|
||||
// Only fullscreen option should be disabled
|
||||
when(mUtils.isFullscreenOptionEnabled(mPackageName)).thenReturn(false);
|
||||
assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN,
|
||||
mPackageName));
|
||||
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
|
||||
mPackageName));
|
||||
}
|
||||
|
||||
private String getUserMinAspectRatioEntry(int aspectRatio, String packageName) {
|
||||
return mUtils.getUserMinAspectRatioEntry(aspectRatio, packageName, mContext.getUserId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioEntry() {
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
// R.string.user_aspect_ratio_app_default
|
||||
final String appDefault = ResourcesUtils.getResourcesString(context,
|
||||
"user_aspect_ratio_app_default");
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
|
||||
.isEqualTo(appDefault);
|
||||
// should always return default if value does not correspond to anything
|
||||
assertThat(getUserMinAspectRatioEntry(-1, mPackageName))
|
||||
.isEqualTo(appDefault);
|
||||
// R.string.user_aspect_ratio_half_screen
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
|
||||
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context,
|
||||
"user_aspect_ratio_half_screen"));
|
||||
// R.string.user_aspect_ratio_display_size
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
|
||||
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context,
|
||||
"user_aspect_ratio_device_size"));
|
||||
// R.string.user_aspect_ratio_16_9
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName))
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_16_9"));
|
||||
// R.string.user_aspect_ratio_4_3
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName))
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_4_3"));
|
||||
// R.string.user_aspect_ratio_3_2
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName))
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_3_2"));
|
||||
// R.string.user_aspect_ratio_fullscreen
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
|
||||
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context,
|
||||
"user_aspect_ratio_fullscreen"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioEntry_fullscreenDisabled_shouldReturnDefault() {
|
||||
setAspectRatioFullscreenBuildTimeFlagEnabled(false);
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN,
|
||||
mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
"user_aspect_ratio_app_default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioEntry_nonDefaultString_shouldReturnNewString() {
|
||||
final String newOptionName = "new_option_name";
|
||||
when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_UNSET});
|
||||
when(mResources.getStringArray(anyInt())).thenReturn(new String[] {newOptionName});
|
||||
|
||||
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
|
||||
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
|
||||
.isEqualTo(newOptionName);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioMapping_noAppDefault_shouldThrowException() {
|
||||
when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_4_3});
|
||||
when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"});
|
||||
|
||||
assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioMapping_configLengthMismatch_shouldThrowException() {
|
||||
when(mResources.getIntArray(anyInt())).thenReturn(new int[] {
|
||||
USER_MIN_ASPECT_RATIO_UNSET,
|
||||
USER_MIN_ASPECT_RATIO_4_3});
|
||||
when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"});
|
||||
|
||||
assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioMapping_appDefaultFlagEnabled() {
|
||||
// Flag is disabled by default, app default not loaded
|
||||
assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName));
|
||||
|
||||
mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
|
||||
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
|
||||
|
||||
assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName));
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName))
|
||||
.isEqualTo(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioEntry_enabledFullscreenOverride_returnsFullscreen() {
|
||||
setIsOverrideToFullscreenEnabled(true);
|
||||
// Fullscreen option is pre-selected
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
"user_aspect_ratio_fullscreen"));
|
||||
|
||||
// App default exists
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName))
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
"user_aspect_ratio_app_default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUserMinAspectRatioEntry_disabledFullscreenOverride_returnsUnchanged() {
|
||||
setIsOverrideToFullscreenEnabled(false);
|
||||
// Fullscreen option is not pre-selected
|
||||
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
"user_aspect_ratio_app_default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOverrideToFullscreenEnabled_returnsTrue()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
setIsOverrideToFullscreenEnabled(true);
|
||||
assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
|
||||
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true);
|
||||
assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOverrideToFullscreenEnabled_optOut_returnsFalse()
|
||||
throws PackageManager.NameNotFoundException {
|
||||
setIsOverrideToFullscreenEnabled(true);
|
||||
mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, false);
|
||||
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOverrideToFullscreenEnabled_flagDisabled_returnsFalse() {
|
||||
mUtils.setFullscreenCompatChange(true);
|
||||
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsOverrideToFullscreenEnabled_optionDisabled_returnsFalse() {
|
||||
mUtils.setFullscreenCompatChange(true);
|
||||
when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName))
|
||||
.thenReturn(false);
|
||||
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
|
||||
}
|
||||
|
||||
private void setIsOverrideToFullscreenEnabled(boolean enabled) {
|
||||
if (enabled) {
|
||||
mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
|
||||
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
|
||||
}
|
||||
mUtils.setFullscreenCompatChange(enabled);
|
||||
when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName))
|
||||
.thenReturn(enabled);
|
||||
}
|
||||
|
||||
private void enableAllDefaultAspectRatioOptions() {
|
||||
final int[] aspectRatioOptions = new int[] {
|
||||
USER_MIN_ASPECT_RATIO_UNSET,
|
||||
USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
|
||||
USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
|
||||
USER_MIN_ASPECT_RATIO_4_3,
|
||||
USER_MIN_ASPECT_RATIO_16_9,
|
||||
USER_MIN_ASPECT_RATIO_3_2,
|
||||
USER_MIN_ASPECT_RATIO_FULLSCREEN};
|
||||
when(mResources.getIntArray(anyInt())).thenReturn(aspectRatioOptions);
|
||||
// String array config overlay with @null values so default strings should be used
|
||||
when(mResources.getStringArray(anyInt())).thenReturn(new String[aspectRatioOptions.length]);
|
||||
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
mockString(context, "user_aspect_ratio_app_default");
|
||||
mockString(context, "user_aspect_ratio_half_screen");
|
||||
mockString(context, "user_aspect_ratio_device_size");
|
||||
mockString(context, "user_aspect_ratio_4_3");
|
||||
mockString(context, "user_aspect_ratio_16_9");
|
||||
mockString(context, "user_aspect_ratio_3_2");
|
||||
mockString(context, "user_aspect_ratio_fullscreen");
|
||||
}
|
||||
|
||||
private void mockString(Context context, String stringResName) {
|
||||
final int resId = ResourcesUtils.getResourcesId(context, "string", stringResName);
|
||||
final String string = ResourcesUtils.getResourcesString(context, stringResName);
|
||||
when(mContext.getString(resId)).thenReturn(string);
|
||||
}
|
||||
|
||||
private void mockProperty(String propertyName, boolean value)
|
||||
throws PackageManager.NameNotFoundException {
|
||||
PackageManager.Property prop = new PackageManager.Property(
|
||||
propertyName, value, mPackageName, "" /* className */);
|
||||
when(mPm.getProperty(propertyName, mPackageName)).thenReturn(prop);
|
||||
}
|
||||
|
||||
private void setAspectRatioSettingsBuildTimeFlagEnabled(boolean enabled) {
|
||||
when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled))
|
||||
.thenReturn(enabled);
|
||||
}
|
||||
|
||||
private void setAspectRatioSettingsDeviceConfigEnabled(String enabled, boolean makeDefault) {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
||||
KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, enabled, makeDefault);
|
||||
}
|
||||
|
||||
private void setAspectRatioFullscreenBuildTimeFlagEnabled(boolean enabled) {
|
||||
when(mResources.getBoolean(R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled))
|
||||
.thenReturn(enabled);
|
||||
}
|
||||
|
||||
private void setAspectRatioFullscreenDeviceConfigEnabled(String enabled, boolean makeDefault) {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
|
||||
KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault);
|
||||
}
|
||||
|
||||
private static class FakeUserAspectRatioManager extends UserAspectRatioManager {
|
||||
private boolean mFullscreenCompatChange = false;
|
||||
|
||||
private FakeUserAspectRatioManager(@NonNull Context context, IPackageManager pm) {
|
||||
super(context, pm);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) {
|
||||
return mFullscreenCompatChange;
|
||||
}
|
||||
|
||||
void setFullscreenCompatChange(boolean enabled) {
|
||||
mFullscreenCompatChange = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.appinfo;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AppLocalePreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private boolean mCanDisplayLocaleUi;
|
||||
private AppLocalePreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new AppLocalePreferenceController(mContext, "test_key") {
|
||||
@Override
|
||||
boolean canDisplayLocaleUi() {
|
||||
return mCanDisplayLocaleUi;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_canShowUi_shouldReturnAvailable() {
|
||||
mCanDisplayLocaleUi = true;
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_canNotShowUi_shouldReturnUnavailable() {
|
||||
mCanDisplayLocaleUi = false;
|
||||
|
||||
assertThat(mController.getAvailabilityStatus())
|
||||
.isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.appinfo;
|
||||
|
||||
import static android.app.AppOpsManager.MODE_ALLOWED;
|
||||
import static android.app.AppOpsManager.MODE_DEFAULT;
|
||||
import static android.app.AppOpsManager.MODE_IGNORED;
|
||||
import static android.app.AppOpsManager.OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED;
|
||||
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
|
||||
|
||||
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
|
||||
import static com.android.settings.Utils.PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS;
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
|
||||
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.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.apphibernation.AppHibernationManager;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class HibernationSwitchPreferenceControllerTest {
|
||||
private static final int PACKAGE_UID = 1;
|
||||
private static final String INVALID_PACKAGE_NAME = "invalid_package";
|
||||
private static final String KEY = "key";
|
||||
private static final String VALID_PACKAGE_NAME = "package";
|
||||
private static final String EXEMPTED_PACKAGE_NAME = "exempted_package";
|
||||
private static final String UNEXEMPTED_PACKAGE_NAME = "unexempted_package";
|
||||
@Mock
|
||||
private AppOpsManager mAppOpsManager;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private AppHibernationManager mAppHibernationManager;
|
||||
@Mock
|
||||
private SwitchPreference mPreference;
|
||||
|
||||
private HibernationSwitchPreferenceController mController;
|
||||
private Context mContext;
|
||||
private String mOriginalPreSFlagValue;
|
||||
|
||||
@Before
|
||||
public void setUp() throws PackageManager.NameNotFoundException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
|
||||
when(mContext.getSystemService(AppHibernationManager.class))
|
||||
.thenReturn(mAppHibernationManager);
|
||||
when(mPackageManager.getPackageUid(eq(VALID_PACKAGE_NAME), anyInt()))
|
||||
.thenReturn(PACKAGE_UID);
|
||||
when(mPackageManager.getPackageUid(eq(INVALID_PACKAGE_NAME), anyInt()))
|
||||
.thenThrow(new PackageManager.NameNotFoundException());
|
||||
when(mPackageManager.getTargetSdkVersion(eq(EXEMPTED_PACKAGE_NAME)))
|
||||
.thenReturn(android.os.Build.VERSION_CODES.Q);
|
||||
when(mPackageManager.getTargetSdkVersion(eq(UNEXEMPTED_PACKAGE_NAME)))
|
||||
.thenReturn(android.os.Build.VERSION_CODES.S);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
|
||||
"true", true /* makeDefault */);
|
||||
mController = new HibernationSwitchPreferenceController(mContext, KEY);
|
||||
when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
|
||||
|
||||
mOriginalPreSFlagValue = DeviceConfig.getProperty(NAMESPACE_APP_HIBERNATION,
|
||||
PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
// Restore original device config values.
|
||||
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS,
|
||||
mOriginalPreSFlagValue, true /* makeDefault */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() {
|
||||
mController.setPackage(VALID_PACKAGE_NAME);
|
||||
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
|
||||
"false", true /* makeDefault */);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_invalidPackage_shouldReturnNotAvailable() {
|
||||
mController.setPackage(INVALID_PACKAGE_NAME);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_validPackage_shouldReturnAvailable() {
|
||||
mController.setPackage(VALID_PACKAGE_NAME);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_unhibernatesWhenExempted() {
|
||||
mController.setPackage(VALID_PACKAGE_NAME);
|
||||
mController.onPreferenceChange(mPreference, false);
|
||||
|
||||
verify(mAppHibernationManager).setHibernatingForUser(VALID_PACKAGE_NAME, false);
|
||||
verify(mAppHibernationManager).setHibernatingGlobally(VALID_PACKAGE_NAME, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPackageHibernationExemptByUser_preSAppShouldBeExemptByDefault() {
|
||||
when(mAppOpsManager.unsafeCheckOpNoThrow(
|
||||
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME)))
|
||||
.thenReturn(MODE_DEFAULT);
|
||||
mController.setPackage(EXEMPTED_PACKAGE_NAME);
|
||||
|
||||
assertTrue(mController.isPackageHibernationExemptByUser());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPackageHibernationExemptByUser_preSAppShouldNotBeExemptWithUserSetting() {
|
||||
when(mAppOpsManager.unsafeCheckOpNoThrow(
|
||||
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME)))
|
||||
.thenReturn(MODE_ALLOWED);
|
||||
mController.setPackage(EXEMPTED_PACKAGE_NAME);
|
||||
|
||||
assertFalse(mController.isPackageHibernationExemptByUser());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPackageHibernationExemptByUser_SAppShouldBeExemptWithUserSetting() {
|
||||
when(mAppOpsManager.unsafeCheckOpNoThrow(
|
||||
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(UNEXEMPTED_PACKAGE_NAME)))
|
||||
.thenReturn(MODE_IGNORED);
|
||||
mController.setPackage(UNEXEMPTED_PACKAGE_NAME);
|
||||
|
||||
assertTrue(mController.isPackageHibernationExemptByUser());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPackageHibernationExemptByUser_preSAppShouldNotBeExemptByDefaultWithPreSFlag() {
|
||||
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_HIBERNATION_TARGETS_PRE_S_APPS,
|
||||
"true", true /* makeDefault */);
|
||||
when(mAppOpsManager.unsafeCheckOpNoThrow(
|
||||
eq(OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED), anyInt(), eq(EXEMPTED_PACKAGE_NAME)))
|
||||
.thenReturn(MODE_DEFAULT);
|
||||
mController.setPackage(EXEMPTED_PACKAGE_NAME);
|
||||
|
||||
assertFalse(mController.isPackageHibernationExemptByUser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.assist;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AssistSettingObserverTest {
|
||||
|
||||
@Test
|
||||
public void callConstructor_shouldNotCrash() {
|
||||
new AssistSettingObserver() {
|
||||
@Override
|
||||
protected List<Uri> getSettingUris() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingChange() {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include /src/com/android/settings/applications/autofill/OWNERS
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.autofill;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
|
||||
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.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Looper;
|
||||
import android.os.UserHandle;
|
||||
import android.service.autofill.AutofillServiceInfo;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PasswordsPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private PreferenceScreen mScreen;
|
||||
private PreferenceCategory mPasswordsPreferenceCategory;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare(); // needed to create the preference screen
|
||||
}
|
||||
mScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||
mPasswordsPreferenceCategory = new PreferenceCategory(mContext);
|
||||
mPasswordsPreferenceCategory.setKey("passwords");
|
||||
mScreen.addPreference(mPasswordsPreferenceCategory);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Tests that getAvailabilityStatus() does not throw an exception if it's called before the
|
||||
// Controller is initialized (this can happen during indexing).
|
||||
public void getAvailabilityStatus_withoutInit_returnsUnavailable() {
|
||||
PasswordsPreferenceController controller =
|
||||
new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey());
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noServices_returnsUnavailable() {
|
||||
PasswordsPreferenceController controller =
|
||||
createControllerWithServices(Collections.emptyList());
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noPasswords_returnsUnavailable() {
|
||||
AutofillServiceInfo service = new AutofillServiceInfo.TestDataBuilder().build();
|
||||
PasswordsPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(service));
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_withPasswords_returnsAvailable() {
|
||||
PasswordsPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(createServiceWithPasswords()));
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_noServices_noPreferencesAdded() {
|
||||
PasswordsPreferenceController controller =
|
||||
createControllerWithServices(Collections.emptyList());
|
||||
controller.displayPreference(mScreen);
|
||||
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_noPasswords_noPreferencesAdded() {
|
||||
AutofillServiceInfo service = new AutofillServiceInfo.TestDataBuilder().build();
|
||||
PasswordsPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(service));
|
||||
controller.displayPreference(mScreen);
|
||||
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void displayPreference_withPasswords_addsPreference() {
|
||||
AutofillServiceInfo service = createServiceWithPasswords();
|
||||
service.getServiceInfo().packageName = "";
|
||||
service.getServiceInfo().name = "";
|
||||
PasswordsPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(service));
|
||||
doReturn(false).when(mContext).bindServiceAsUser(any(), any(), anyInt(), any());
|
||||
|
||||
controller.displayPreference(mScreen);
|
||||
|
||||
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
|
||||
Preference pref = mPasswordsPreferenceCategory.getPreference(0);
|
||||
assertThat(pref.getIcon()).isNotNull();
|
||||
pref.performClick();
|
||||
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
UserHandle user = mContext.getUser();
|
||||
verify(mContext).startActivityAsUser(intentCaptor.capture(), eq(user));
|
||||
assertThat(intentCaptor.getValue().getComponent())
|
||||
.isEqualTo(
|
||||
new ComponentName(
|
||||
service.getServiceInfo().packageName,
|
||||
service.getPasswordsActivity()));
|
||||
}
|
||||
|
||||
private PasswordsPreferenceController createControllerWithServices(
|
||||
List<AutofillServiceInfo> availableServices) {
|
||||
PasswordsPreferenceController controller =
|
||||
new PasswordsPreferenceController(mContext, mPasswordsPreferenceCategory.getKey());
|
||||
controller.init(() -> mock(Lifecycle.class), availableServices);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private AutofillServiceInfo createServiceWithPasswords() {
|
||||
return new AutofillServiceInfo.TestDataBuilder()
|
||||
.setPasswordsActivity("com.android.test.Passwords")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.credentials;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.credentials.CredentialProviderInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.settings.tests.unit.R;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CredentialManagerPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private PreferenceScreen mScreen;
|
||||
private PreferenceCategory mCredentialsPreferenceCategory;
|
||||
private CredentialManagerPreferenceController.Delegate mDelegate;
|
||||
|
||||
private static final String TEST_PACKAGE_NAME_A = "com.android.providerA";
|
||||
private static final String TEST_PACKAGE_NAME_B = "com.android.providerB";
|
||||
private static final String TEST_PACKAGE_NAME_C = "com.android.providerC";
|
||||
private static final String TEST_TITLE_APP_A = "test app A";
|
||||
private static final String TEST_TITLE_APP_B = "test app B";
|
||||
private static final String TEST_TITLE_APP_C = "test app C1";
|
||||
private static final String PRIMARY_INTENT = "android.settings.CREDENTIAL_PROVIDER";
|
||||
private static final String ALTERNATE_INTENT = "android.settings.SYNC_SETTINGS";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare(); // needed to create the preference screen
|
||||
}
|
||||
mScreen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
|
||||
mCredentialsPreferenceCategory = new PreferenceCategory(mContext);
|
||||
mCredentialsPreferenceCategory.setKey("credentials_test");
|
||||
mScreen.addPreference(mCredentialsPreferenceCategory);
|
||||
mDelegate =
|
||||
new CredentialManagerPreferenceController.Delegate() {
|
||||
public void setActivityResult(int resultCode) {}
|
||||
|
||||
public void forceDelegateRefresh() {}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
// Tests that getAvailabilityStatus() does not throw an exception if it's called before the
|
||||
// Controller is initialized (this can happen during indexing).
|
||||
public void getAvailabilityStatus_withoutInit_returnsUnavailable() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare(); // needed to create the preference screen
|
||||
}
|
||||
|
||||
CredentialManagerPreferenceController controller =
|
||||
new CredentialManagerPreferenceController(
|
||||
mContext, mCredentialsPreferenceCategory.getKey());
|
||||
assertThat(controller.isConnected()).isFalse();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_noServices_returnsUnavailable() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Collections.emptyList());
|
||||
assertThat(controller.isConnected()).isFalse();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_withServices_returnsAvailable() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
controller.setSimulateHiddenForTests(Optional.of(false));
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_isHidden_returnsConditionallyUnavailable() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
controller.setSimulateHiddenForTests(Optional.of(true));
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isTrue();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_withServices_preferencesAdded() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
controller.setSimulateHiddenForTests(Optional.of(false));
|
||||
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
controller.displayPreference(mScreen);
|
||||
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
|
||||
|
||||
Preference pref = mCredentialsPreferenceCategory.getPreference(0);
|
||||
assertThat(pref.getTitle()).isNotEqualTo("Add account");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildPreference() {
|
||||
CredentialProviderInfo providerInfo1 =
|
||||
createCredentialProviderInfoWithSubtitle(
|
||||
"com.android.provider1", "ClassA", "Service Title", null);
|
||||
CredentialProviderInfo providerInfo2 =
|
||||
createCredentialProviderInfoWithSubtitle(
|
||||
"com.android.provider2", "ClassA", "Service Title", "Summary Text");
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
controller.setSimulateHiddenForTests(Optional.of(false));
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
// Test the data is correct.
|
||||
assertThat(providerInfo1.isEnabled()).isFalse();
|
||||
assertThat(providerInfo2.isEnabled()).isFalse();
|
||||
assertThat(controller.getEnabledProviders().size()).isEqualTo(0);
|
||||
|
||||
// Toggle one provider and make sure it worked.
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider1")).isTrue();
|
||||
Set<String> enabledProviders = controller.getEnabledProviders();
|
||||
assertThat(enabledProviders.size()).isEqualTo(1);
|
||||
assertThat(enabledProviders.contains("com.android.provider1")).isTrue();
|
||||
|
||||
// Create the pref (checked).
|
||||
CredentialManagerPreferenceController.CombiPreference pref =
|
||||
controller.createPreference(mContext, providerInfo1);
|
||||
assertThat(pref.getTitle().toString()).isEqualTo("Service Title");
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
assertThat(pref.getSummary()).isNull();
|
||||
|
||||
// Create the pref (not checked).
|
||||
CredentialManagerPreferenceController.CombiPreference pref2 =
|
||||
controller.createPreference(mContext, providerInfo2);
|
||||
assertThat(pref2.getTitle().toString()).isEqualTo("Service Title");
|
||||
assertThat(pref2.isChecked()).isFalse();
|
||||
assertThat(pref2.getSummary().toString()).isEqualTo("Summary Text");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_handlesToggleAndSave() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(
|
||||
Lists.newArrayList(
|
||||
createCredentialProviderInfo("com.android.provider1", "ClassA"),
|
||||
createCredentialProviderInfo("com.android.provider1", "ClassB"),
|
||||
createCredentialProviderInfo("com.android.provider2", "ClassA"),
|
||||
createCredentialProviderInfo("com.android.provider3", "ClassA"),
|
||||
createCredentialProviderInfo("com.android.provider4", "ClassA"),
|
||||
createCredentialProviderInfo("com.android.provider5", "ClassA"),
|
||||
createCredentialProviderInfo("com.android.provider6", "ClassA")));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
controller.setSimulateHiddenForTests(Optional.of(false));
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
// Ensure that we stay under 5 providers (one is reserved for primary).
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider1")).isTrue();
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider2")).isTrue();
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider3")).isTrue();
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider4")).isTrue();
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider5")).isFalse();
|
||||
assertThat(controller.togglePackageNameEnabled("com.android.provider6")).isFalse();
|
||||
|
||||
// Check that they are all actually registered.
|
||||
Set<String> enabledProviders = controller.getEnabledProviders();
|
||||
assertThat(enabledProviders.size()).isEqualTo(4);
|
||||
assertThat(enabledProviders.contains("com.android.provider1")).isTrue();
|
||||
assertThat(enabledProviders.contains("com.android.provider2")).isTrue();
|
||||
assertThat(enabledProviders.contains("com.android.provider3")).isTrue();
|
||||
assertThat(enabledProviders.contains("com.android.provider4")).isTrue();
|
||||
assertThat(enabledProviders.contains("com.android.provider5")).isFalse();
|
||||
assertThat(enabledProviders.contains("com.android.provider6")).isFalse();
|
||||
|
||||
// Check that the settings string has the right component names.
|
||||
List<String> enabledServices = controller.getEnabledSettings();
|
||||
assertThat(enabledServices.size()).isEqualTo(5);
|
||||
assertThat(enabledServices.contains("com.android.provider1/ClassA")).isTrue();
|
||||
assertThat(enabledServices.contains("com.android.provider1/ClassB")).isTrue();
|
||||
assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue();
|
||||
assertThat(enabledServices.contains("com.android.provider3/ClassA")).isTrue();
|
||||
assertThat(enabledServices.contains("com.android.provider4/ClassA")).isTrue();
|
||||
assertThat(enabledServices.contains("com.android.provider5/ClassA")).isFalse();
|
||||
assertThat(enabledServices.contains("com.android.provider6/ClassA")).isFalse();
|
||||
|
||||
// Toggle the provider disabled.
|
||||
controller.togglePackageNameDisabled("com.android.provider2");
|
||||
|
||||
// Check that the provider was removed from the list of providers.
|
||||
Set<String> currentlyEnabledProviders = controller.getEnabledProviders();
|
||||
assertThat(currentlyEnabledProviders.size()).isEqualTo(3);
|
||||
assertThat(currentlyEnabledProviders.contains("com.android.provider1")).isTrue();
|
||||
assertThat(currentlyEnabledProviders.contains("com.android.provider2")).isFalse();
|
||||
assertThat(currentlyEnabledProviders.contains("com.android.provider3")).isTrue();
|
||||
assertThat(currentlyEnabledProviders.contains("com.android.provider4")).isTrue();
|
||||
assertThat(currentlyEnabledProviders.contains("com.android.provider5")).isFalse();
|
||||
assertThat(currentlyEnabledProviders.contains("com.android.provider6")).isFalse();
|
||||
|
||||
// Check that the provider was removed from the list of services stored in the setting.
|
||||
List<String> currentlyEnabledServices = controller.getEnabledSettings();
|
||||
assertThat(currentlyEnabledServices.size()).isEqualTo(4);
|
||||
assertThat(currentlyEnabledServices.contains("com.android.provider1/ClassA")).isTrue();
|
||||
assertThat(currentlyEnabledServices.contains("com.android.provider1/ClassB")).isTrue();
|
||||
assertThat(currentlyEnabledServices.contains("com.android.provider3/ClassA")).isTrue();
|
||||
assertThat(currentlyEnabledServices.contains("com.android.provider4/ClassA")).isTrue();
|
||||
assertThat(currentlyEnabledServices.contains("com.android.provider5/ClassA")).isFalse();
|
||||
assertThat(currentlyEnabledServices.contains("com.android.provider6/ClassA")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesCredentialProviderInfoEnabledDisabled() {
|
||||
CredentialProviderInfo providerInfo1 =
|
||||
createCredentialProviderInfoWithIsEnabled(
|
||||
"com.android.provider1", "ClassA", "Service Title", false);
|
||||
CredentialProviderInfo providerInfo2 =
|
||||
createCredentialProviderInfoWithIsEnabled(
|
||||
"com.android.provider2", "ClassA", "Service Title", true);
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
controller.setSimulateHiddenForTests(Optional.of(false));
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
// Test the data is correct.
|
||||
assertThat(providerInfo1.isEnabled()).isFalse();
|
||||
assertThat(providerInfo2.isEnabled()).isTrue();
|
||||
|
||||
// Check that they are all actually registered.
|
||||
Set<String> enabledProviders = controller.getEnabledProviders();
|
||||
assertThat(enabledProviders.size()).isEqualTo(1);
|
||||
assertThat(enabledProviders.contains("com.android.provider1")).isFalse();
|
||||
assertThat(enabledProviders.contains("com.android.provider2")).isTrue();
|
||||
|
||||
// Check that the settings string has the right component names.
|
||||
List<String> enabledServices = controller.getEnabledSettings();
|
||||
assertThat(enabledServices.size()).isEqualTo(1);
|
||||
assertThat(enabledServices.contains("com.android.provider1/ClassA")).isFalse();
|
||||
assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_withServices_preferencesAdded_sameAppShouldBeMerged() {
|
||||
CredentialProviderInfo serviceA1 =
|
||||
createCredentialProviderInfoWithAppLabel(
|
||||
TEST_PACKAGE_NAME_A,
|
||||
"CredManProviderA1",
|
||||
TEST_TITLE_APP_A,
|
||||
"test service A1");
|
||||
CredentialProviderInfo serviceB1 =
|
||||
createCredentialProviderInfoWithAppLabel(
|
||||
TEST_PACKAGE_NAME_B,
|
||||
"CredManProviderB1",
|
||||
TEST_TITLE_APP_B,
|
||||
"test service B");
|
||||
CredentialProviderInfo serviceC1 =
|
||||
createCredentialProviderInfoWithAppLabel(
|
||||
TEST_PACKAGE_NAME_C, "CredManProviderC1", "test app C1", TEST_TITLE_APP_C);
|
||||
CredentialProviderInfo serviceC2 =
|
||||
createCredentialProviderInfoWithAppLabel(
|
||||
TEST_PACKAGE_NAME_C, "CredManProviderC2", "test app C2", TEST_TITLE_APP_C);
|
||||
CredentialProviderInfo serviceC3 =
|
||||
createCredentialProviderInfoBuilder(
|
||||
TEST_PACKAGE_NAME_C,
|
||||
"CredManProviderC3",
|
||||
"test app C3",
|
||||
TEST_TITLE_APP_C)
|
||||
.setEnabled(true)
|
||||
.build();
|
||||
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(
|
||||
Lists.newArrayList(serviceA1, serviceB1, serviceC1, serviceC2, serviceC3));
|
||||
controller.setSimulateConnectedForTests(true);
|
||||
controller.setSimulateHiddenForTests(Optional.of(false));
|
||||
|
||||
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
|
||||
assertThat(controller.isConnected()).isTrue();
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
controller.displayPreference(mScreen);
|
||||
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(3);
|
||||
|
||||
Map<String, CredentialManagerPreferenceController.CombiPreference> prefs =
|
||||
controller.buildPreferenceList(mContext, mCredentialsPreferenceCategory);
|
||||
assertThat(prefs.keySet())
|
||||
.containsExactly(TEST_PACKAGE_NAME_A, TEST_PACKAGE_NAME_B, TEST_PACKAGE_NAME_C);
|
||||
assertThat(prefs.size()).isEqualTo(3);
|
||||
assertThat(prefs.containsKey(TEST_PACKAGE_NAME_A)).isTrue();
|
||||
assertThat(prefs.get(TEST_PACKAGE_NAME_A).getTitle()).isEqualTo(TEST_TITLE_APP_A);
|
||||
assertThat(prefs.get(TEST_PACKAGE_NAME_A).isChecked()).isFalse();
|
||||
assertThat(prefs.containsKey(TEST_PACKAGE_NAME_B)).isTrue();
|
||||
assertThat(prefs.get(TEST_PACKAGE_NAME_B).getTitle()).isEqualTo(TEST_TITLE_APP_B);
|
||||
assertThat(prefs.get(TEST_PACKAGE_NAME_B).isChecked()).isFalse();
|
||||
assertThat(prefs.containsKey(TEST_PACKAGE_NAME_C)).isTrue();
|
||||
assertThat(prefs.get(TEST_PACKAGE_NAME_C).getTitle()).isEqualTo(TEST_TITLE_APP_C);
|
||||
assertThat(prefs.get(TEST_PACKAGE_NAME_C).isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntentWithProviderServiceInfo_handleBadIntent_missingData() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
|
||||
// Create an intent with missing data.
|
||||
Intent missingDataIntent = new Intent(PRIMARY_INTENT);
|
||||
assertThat(controller.verifyReceivedIntent(missingDataIntent)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntentWithProviderServiceInfo_handleBadIntent_successDialog() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
String packageName = cpi.getServiceInfo().packageName;
|
||||
|
||||
// Create an intent with valid data.
|
||||
Intent intent = new Intent(PRIMARY_INTENT);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
|
||||
int resultCode =
|
||||
controller.completeEnableProviderDialogBox(
|
||||
DialogInterface.BUTTON_POSITIVE, packageName, true);
|
||||
assertThat(resultCode).isEqualTo(Activity.RESULT_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntentWithProviderServiceInfo_handleIntent_cancelDialog() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
String packageName = cpi.getServiceInfo().packageName;
|
||||
|
||||
// Create an intent with valid data.
|
||||
Intent intent = new Intent(PRIMARY_INTENT);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
|
||||
int resultCode =
|
||||
controller.completeEnableProviderDialogBox(
|
||||
DialogInterface.BUTTON_NEGATIVE, packageName, true);
|
||||
assertThat(resultCode).isEqualTo(Activity.RESULT_CANCELED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleOtherIntentWithProviderServiceInfo_handleBadIntent_missingData() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
|
||||
// Create an intent with missing data.
|
||||
Intent missingDataIntent = new Intent(ALTERNATE_INTENT);
|
||||
assertThat(controller.verifyReceivedIntent(missingDataIntent)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleOtherIntentWithProviderServiceInfo_handleBadIntent_successDialog() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
String packageName = cpi.getServiceInfo().packageName;
|
||||
|
||||
// Create an intent with valid data.
|
||||
Intent intent = new Intent(ALTERNATE_INTENT);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
|
||||
int resultCode =
|
||||
controller.completeEnableProviderDialogBox(
|
||||
DialogInterface.BUTTON_POSITIVE, packageName, true);
|
||||
assertThat(resultCode).isEqualTo(Activity.RESULT_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleOtherIntentWithProviderServiceInfo_handleIntent_cancelDialog() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
String packageName = cpi.getServiceInfo().packageName;
|
||||
|
||||
// Create an intent with valid data.
|
||||
Intent intent = new Intent(ALTERNATE_INTENT);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
assertThat(controller.verifyReceivedIntent(intent)).isTrue();
|
||||
int resultCode =
|
||||
controller.completeEnableProviderDialogBox(
|
||||
DialogInterface.BUTTON_NEGATIVE, packageName, true);
|
||||
assertThat(resultCode).isEqualTo(Activity.RESULT_CANCELED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntentWithProviderServiceInfo_handleIntent_incorrectAction() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
String packageName = cpi.getServiceInfo().packageName;
|
||||
|
||||
// Create an intent with valid data.
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE);
|
||||
intent.setData(Uri.parse("package:" + packageName));
|
||||
assertThat(controller.verifyReceivedIntent(intent)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntentWithProviderServiceInfo_handleNullIntent() {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
|
||||
// Use a null intent.
|
||||
assertThat(controller.verifyReceivedIntent(null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIconResizer_resizeLargeImage() throws Throwable {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
|
||||
final Drawable d =
|
||||
InstrumentationRegistry.getInstrumentation()
|
||||
.getContext()
|
||||
.getResources()
|
||||
.getDrawable(R.drawable.credman_icon_32_32);
|
||||
assertThat(d).isNotNull();
|
||||
assertThat(d.getIntrinsicHeight() >= 0).isTrue();
|
||||
assertThat(d.getIntrinsicWidth() >= 0).isTrue();
|
||||
|
||||
Drawable thumbnail = controller.processIcon(d);
|
||||
assertThat(thumbnail).isNotNull();
|
||||
assertThat(thumbnail.getIntrinsicHeight()).isEqualTo(getIconSize());
|
||||
assertThat(thumbnail.getIntrinsicWidth()).isEqualTo(getIconSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIconResizer_resizeNullImage() throws Throwable {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
|
||||
Drawable thumbnail = controller.processIcon(null);
|
||||
assertThat(thumbnail).isNotNull();
|
||||
assertThat(thumbnail.getIntrinsicHeight()).isEqualTo(getIconSize());
|
||||
assertThat(thumbnail.getIntrinsicWidth()).isEqualTo(getIconSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIconResizer_resizeSmallImage() throws Throwable {
|
||||
CredentialProviderInfo cpi = createCredentialProviderInfo();
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(Lists.newArrayList(cpi));
|
||||
|
||||
final Drawable d =
|
||||
InstrumentationRegistry.getInstrumentation()
|
||||
.getContext()
|
||||
.getResources()
|
||||
.getDrawable(R.drawable.credman_icon_1_1);
|
||||
assertThat(d).isNotNull();
|
||||
assertThat(d.getIntrinsicHeight() >= 0).isTrue();
|
||||
assertThat(d.getIntrinsicWidth() >= 0).isTrue();
|
||||
|
||||
Drawable thumbnail = controller.processIcon(null);
|
||||
assertThat(thumbnail).isNotNull();
|
||||
assertThat(thumbnail.getIntrinsicHeight()).isEqualTo(getIconSize());
|
||||
assertThat(thumbnail.getIntrinsicWidth()).isEqualTo(getIconSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasNonPrimaryServices_allServicesArePrimary() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(
|
||||
Lists.newArrayList(createCredentialProviderPrimary()));
|
||||
assertThat(controller.hasNonPrimaryServices()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasNonPrimaryServices_mixtureOfServices() {
|
||||
CredentialManagerPreferenceController controller =
|
||||
createControllerWithServices(
|
||||
Lists.newArrayList(createCredentialProviderInfo(),
|
||||
createCredentialProviderPrimary()));
|
||||
assertThat(controller.hasNonPrimaryServices()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderLimitReached() {
|
||||
// The limit is 5 with one slot reserved for primary.
|
||||
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(0)).isFalse();
|
||||
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(1)).isFalse();
|
||||
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(2)).isFalse();
|
||||
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(3)).isFalse();
|
||||
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(4)).isTrue();
|
||||
assertThat(CredentialManagerPreferenceController.hasProviderLimitBeenReached(5)).isTrue();
|
||||
}
|
||||
|
||||
private int getIconSize() {
|
||||
final Resources resources = mContext.getResources();
|
||||
return (int) resources.getDimension(android.R.dimen.app_icon_size);
|
||||
}
|
||||
|
||||
private CredentialManagerPreferenceController createControllerWithServices(
|
||||
List<CredentialProviderInfo> availableServices) {
|
||||
return createControllerWithServicesAndAddServiceOverride(availableServices, null);
|
||||
}
|
||||
|
||||
private CredentialManagerPreferenceController createControllerWithServicesAndAddServiceOverride(
|
||||
List<CredentialProviderInfo> availableServices, String addServiceOverride) {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare(); // needed to create the preference screen
|
||||
}
|
||||
|
||||
CredentialManagerPreferenceController controller =
|
||||
new CredentialManagerPreferenceController(
|
||||
mContext, mCredentialsPreferenceCategory.getKey());
|
||||
controller.setAvailableServices(availableServices, addServiceOverride);
|
||||
controller.setDelegate(mDelegate);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private CredentialProviderInfo createCredentialProviderInfo() {
|
||||
return createCredentialProviderInfo("com.android.provider", "CredManProvider");
|
||||
}
|
||||
|
||||
private CredentialProviderInfo createCredentialProviderInfo(
|
||||
String packageName, String className) {
|
||||
return createCredentialProviderInfoBuilder(packageName, className, null, "App Name")
|
||||
.build();
|
||||
}
|
||||
|
||||
private CredentialProviderInfo createCredentialProviderInfoWithIsEnabled(
|
||||
String packageName, String className, CharSequence serviceLabel, boolean isEnabled) {
|
||||
return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, "App Name")
|
||||
.setEnabled(isEnabled)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CredentialProviderInfo createCredentialProviderPrimary() {
|
||||
return createCredentialProviderInfoBuilder(
|
||||
"com.android.primary", "CredManProvider", "Service Label", "App Name")
|
||||
.setPrimary(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CredentialProviderInfo createCredentialProviderInfoWithSubtitle(
|
||||
String packageName, String className, CharSequence label, CharSequence subtitle) {
|
||||
ServiceInfo si = new ServiceInfo();
|
||||
si.packageName = packageName;
|
||||
si.name = className;
|
||||
si.nonLocalizedLabel = "test";
|
||||
|
||||
si.applicationInfo = new ApplicationInfo();
|
||||
si.applicationInfo.packageName = packageName;
|
||||
si.applicationInfo.nonLocalizedLabel = "test";
|
||||
|
||||
return new CredentialProviderInfo.Builder(si)
|
||||
.setOverrideLabel(label)
|
||||
.setSettingsSubtitle(subtitle)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CredentialProviderInfo createCredentialProviderInfoWithAppLabel(
|
||||
String packageName, String className, CharSequence serviceLabel, String appLabel) {
|
||||
return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, appLabel)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CredentialProviderInfo.Builder createCredentialProviderInfoBuilder(
|
||||
String packageName, String className, CharSequence serviceLabel, String appLabel) {
|
||||
ServiceInfo si = new ServiceInfo();
|
||||
si.packageName = packageName;
|
||||
si.name = className;
|
||||
si.nonLocalizedLabel = serviceLabel;
|
||||
|
||||
si.applicationInfo = new ApplicationInfo();
|
||||
si.applicationInfo.packageName = packageName;
|
||||
si.applicationInfo.nonLocalizedLabel = appLabel;
|
||||
|
||||
return new CredentialProviderInfo.Builder(si).setOverrideLabel(serviceLabel);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.credentials;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CredentialsPickerActivityTest {
|
||||
|
||||
@Mock private UserManager mUserManager;
|
||||
|
||||
private Context mMockContext;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mMockContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mMockContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectFragmentIntoIntent_normalProfile() {
|
||||
Intent intent = new Intent();
|
||||
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
|
||||
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(DefaultCombinedPicker.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectFragmentIntoIntent_workProfile() {
|
||||
Intent intent = new Intent();
|
||||
|
||||
// Simulate managed / work profile.
|
||||
when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
|
||||
assertThat(DefaultCombinedPickerWork.isUserHandledByFragment(mUserManager, 10)).isTrue();
|
||||
|
||||
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
|
||||
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(DefaultCombinedPickerWork.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInjectFragmentIntoIntent_privateProfile() {
|
||||
Intent intent = new Intent();
|
||||
|
||||
// Simulate private profile.
|
||||
UserHandle privateUser = new UserHandle(100);
|
||||
when(mUserManager.getUserInfo(100))
|
||||
.thenReturn(new UserInfo(100, "", "", 0, UserManager.USER_TYPE_PROFILE_PRIVATE));
|
||||
when(mUserManager.getUserProfiles()).thenReturn(Lists.newArrayList(privateUser));
|
||||
assertThat(DefaultCombinedPickerPrivate.isUserHandledByFragment(mUserManager)).isTrue();
|
||||
|
||||
CredentialsPickerActivity.injectFragmentIntoIntent(mMockContext, intent);
|
||||
assertThat(intent.getStringExtra(CredentialsPickerActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(DefaultCombinedPickerPrivate.class.getName());
|
||||
}
|
||||
}
|
||||
@@ -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.applications.credentials;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DefaultCombinedPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private PrimaryProviderPreference.Delegate mDelegate;
|
||||
private AttributeSet mAttributes;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare(); // needed to create the preference screen
|
||||
}
|
||||
mDelegate =
|
||||
new PrimaryProviderPreference.Delegate() {
|
||||
public void onOpenButtonClicked() {}
|
||||
|
||||
public void onChangeButtonClicked() {}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureSettingIntentNullForNewDesign() {
|
||||
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The setting intent should be null for the new design since this
|
||||
// is handled by the delegate for the PrimaryProviderPreference.
|
||||
DefaultCombinedPreferenceController dcpc =
|
||||
new DefaultCombinedPreferenceController(mContext);
|
||||
assertThat(dcpc.getSettingIntent(null).getPackage()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureSettingIntentNotNullForOldDesign() {
|
||||
if (PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For the old design the setting intent should still be used.
|
||||
DefaultCombinedPreferenceController dcpc =
|
||||
new DefaultCombinedPreferenceController(mContext);
|
||||
assertThat(dcpc.getSettingIntent(null).getPackage()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureSettingsActivityIntentCreatedSuccessfully() {
|
||||
// Ensure that the settings activity is only created if we haved the right combination
|
||||
// of package and class name.
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent(null, null)).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("", null)).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("", "")).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "")).isNull();
|
||||
assertThat(CombinedProviderInfo.createSettingsActivityIntent("com.test", "ClassName"))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureUpdatePreferenceForProviderPopulatesInfo() {
|
||||
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
DefaultCombinedPreferenceController dcpc =
|
||||
new DefaultCombinedPreferenceController(mContext);
|
||||
PrimaryProviderPreference ppp = createTestPreference();
|
||||
Drawable appIcon = mContext.getResources().getDrawable(R.drawable.ic_settings_delete);
|
||||
|
||||
// Update the preference to use the provider and make sure the view
|
||||
// was updated.
|
||||
dcpc.updatePreferenceForProvider(ppp, "App Name", "Subtitle", appIcon, null, null);
|
||||
assertThat(ppp.getTitle().toString()).isEqualTo("App Name");
|
||||
assertThat(ppp.getSummary().toString()).isEqualTo("Subtitle");
|
||||
assertThat(ppp.getIcon()).isEqualTo(appIcon);
|
||||
|
||||
// Set the preference back to none and make sure the view was updated.
|
||||
dcpc.updatePreferenceForProvider(ppp, null, null, null, null, null);
|
||||
assertThat(ppp.getTitle().toString()).isEqualTo("None");
|
||||
assertThat(ppp.getSummary()).isNull();
|
||||
assertThat(ppp.getIcon()).isNull();
|
||||
}
|
||||
|
||||
private PrimaryProviderPreference createTestPreference() {
|
||||
int layoutId =
|
||||
ResourcesUtils.getResourcesId(
|
||||
mContext, "layout", "preference_credential_manager_with_buttons");
|
||||
PreferenceViewHolder holder =
|
||||
PreferenceViewHolder.createInstanceForTests(
|
||||
LayoutInflater.from(mContext).inflate(layoutId, null));
|
||||
PreferenceViewHolder holderForTest = spy(holder);
|
||||
View gearView = new View(mContext, null);
|
||||
int gearId = ResourcesUtils.getResourcesId(mContext, "id", "settings_button");
|
||||
when(holderForTest.findViewById(gearId)).thenReturn(gearView);
|
||||
|
||||
PrimaryProviderPreference ppp = new PrimaryProviderPreference(mContext, mAttributes);
|
||||
ppp.setDelegate(mDelegate);
|
||||
ppp.onBindViewHolder(holderForTest);
|
||||
return ppp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include /src/com/android/settings/applications/credentials/OWNERS
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.credentials;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PrimaryProviderPreferenceTest {
|
||||
|
||||
private Context mContext;
|
||||
private PrimaryProviderPreference.Delegate mDelegate;
|
||||
private boolean mReceivedOpenButtonClicked = false;
|
||||
private boolean mReceivedChangeButtonClicked = false;
|
||||
private AttributeSet mAttributes;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare(); // needed to create the preference screen
|
||||
}
|
||||
mReceivedOpenButtonClicked = false;
|
||||
mReceivedChangeButtonClicked = false;
|
||||
mDelegate =
|
||||
new PrimaryProviderPreference.Delegate() {
|
||||
public void onOpenButtonClicked() {
|
||||
mReceivedOpenButtonClicked = true;
|
||||
}
|
||||
|
||||
public void onChangeButtonClicked() {
|
||||
mReceivedChangeButtonClicked = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureButtonsClicksCallDelegate_newDesign() {
|
||||
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
|
||||
|
||||
// Test that all the views & buttons are bound correctly.
|
||||
assertThat(ppp.getOpenButton()).isNotNull();
|
||||
assertThat(ppp.getChangeButton()).isNotNull();
|
||||
assertThat(ppp.getButtonFrameView()).isNotNull();
|
||||
|
||||
// Test that clicking the open button results in the delegate being
|
||||
// called.
|
||||
assertThat(mReceivedOpenButtonClicked).isFalse();
|
||||
ppp.getOpenButton().performClick();
|
||||
assertThat(mReceivedOpenButtonClicked).isTrue();
|
||||
|
||||
// Test that clicking the change button results in the delegate being
|
||||
// called.
|
||||
assertThat(mReceivedChangeButtonClicked).isFalse();
|
||||
ppp.getChangeButton().performClick();
|
||||
assertThat(mReceivedChangeButtonClicked).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureButtonsClicksCallDelegate_newDesign_openButtonVisibility() {
|
||||
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
|
||||
|
||||
// Test that the open button is visible.
|
||||
assertThat(ppp.getOpenButton()).isNotNull();
|
||||
assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.GONE);
|
||||
|
||||
// Show the button and make sure the view was updated.
|
||||
ppp.setOpenButtonVisible(true);
|
||||
assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.VISIBLE);
|
||||
|
||||
// Hide the button and make sure the view was updated.
|
||||
ppp.setOpenButtonVisible(false);
|
||||
assertThat(ppp.getOpenButton().getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ensureButtonsClicksCallDelegate_newDesign_buttonsCompactMode() {
|
||||
if (!PrimaryProviderPreference.shouldUseNewSettingsUi()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PrimaryProviderPreference ppp = createTestPreferenceWithNewLayout();
|
||||
int initialPaddingLeft = ppp.getButtonFrameView().getPaddingLeft();
|
||||
|
||||
// If we show the buttons the left padding should be updated.
|
||||
ppp.setButtonsCompactMode(true);
|
||||
assertThat(ppp.getButtonFrameView().getPaddingLeft()).isNotEqualTo(initialPaddingLeft);
|
||||
|
||||
// If we hide the buttons the left padding should be updated.
|
||||
ppp.setButtonsCompactMode(false);
|
||||
assertThat(ppp.getButtonFrameView().getPaddingLeft()).isEqualTo(initialPaddingLeft);
|
||||
}
|
||||
|
||||
private PrimaryProviderPreference createTestPreferenceWithNewLayout() {
|
||||
return createTestPreference("preference_credential_manager_with_buttons");
|
||||
}
|
||||
|
||||
private PrimaryProviderPreference createTestPreference(String layoutName) {
|
||||
int layoutId = ResourcesUtils.getResourcesId(mContext, "layout", layoutName);
|
||||
PreferenceViewHolder holder =
|
||||
PreferenceViewHolder.createInstanceForTests(
|
||||
LayoutInflater.from(mContext).inflate(layoutId, null));
|
||||
PreferenceViewHolder holderForTest = spy(holder);
|
||||
View gearView = new View(mContext, null);
|
||||
int gearId = ResourcesUtils.getResourcesId(mContext, "id", "settings_button");
|
||||
when(holderForTest.findViewById(gearId)).thenReturn(gearView);
|
||||
|
||||
PrimaryProviderPreference ppp = new PrimaryProviderPreference(mContext, mAttributes);
|
||||
ppp.setDelegate(mDelegate);
|
||||
ppp.onBindViewHolder(holderForTest);
|
||||
return ppp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.manageapplications;
|
||||
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_ALARMS_AND_REMINDERS;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_ALL;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_BATTERY_OPTIMIZED;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_INSTALL_SOURCES;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_MEDIA_MANAGEMENT;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_TURN_SCREEN_ON;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_USAGE_ACCESS;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WITH_OVERLAY;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WRITE_SETTINGS;
|
||||
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_LONG_BACKGROUND_TASKS;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_ALARMS_AND_REMINDERS;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_BATTERY_OPTIMIZATION;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_GAMES;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_HIGH_POWER;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_LONG_BACKGROUND_TASKS;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MAIN;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MANAGE_SOURCES;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MEDIA_MANAGEMENT_APPS;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_STORAGE;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_TURN_SCREEN_ON;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_USAGE_ACCESS;
|
||||
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_WRITE_SETTINGS;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AppFilterRegistryTest {
|
||||
|
||||
@Test
|
||||
public void getDefaultType_shouldMatchForAllListType() {
|
||||
final AppFilterRegistry registry = AppFilterRegistry.getInstance();
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_USAGE_ACCESS))
|
||||
.isEqualTo(FILTER_APPS_USAGE_ACCESS);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_HIGH_POWER))
|
||||
.isEqualTo(FILTER_APPS_POWER_ALLOWLIST);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_OVERLAY))
|
||||
.isEqualTo(FILTER_APPS_WITH_OVERLAY);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_WRITE_SETTINGS))
|
||||
.isEqualTo(FILTER_APPS_WRITE_SETTINGS);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_MANAGE_SOURCES))
|
||||
.isEqualTo(FILTER_APPS_INSTALL_SOURCES);
|
||||
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_ALARMS_AND_REMINDERS))
|
||||
.isEqualTo(FILTER_ALARMS_AND_REMINDERS);
|
||||
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_MEDIA_MANAGEMENT_APPS))
|
||||
.isEqualTo(FILTER_APPS_MEDIA_MANAGEMENT);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_BATTERY_OPTIMIZATION))
|
||||
.isEqualTo(FILTER_APPS_BATTERY_OPTIMIZED);
|
||||
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_MAIN)).isEqualTo(FILTER_APPS_ALL);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_NOTIFICATION))
|
||||
.isEqualTo(FILTER_APPS_RECENT);
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_STORAGE)).isEqualTo(FILTER_APPS_ALL);
|
||||
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_GAMES)).isEqualTo(FILTER_APPS_ALL);
|
||||
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_LONG_BACKGROUND_TASKS))
|
||||
.isEqualTo(FILTER_LONG_BACKGROUND_TASKS);
|
||||
|
||||
assertThat(registry.getDefaultFilterType(LIST_TYPE_TURN_SCREEN_ON)).isEqualTo(
|
||||
FILTER_APPS_TURN_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class AlertingTypeFilterPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private AlertingTypeFilterPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
ServiceInfo mSi = new ServiceInfo();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new AlertingTypeFilterPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setServiceInfo(mSi);
|
||||
mController.setUserId(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getType() {
|
||||
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.specialaccess.notificationaccess;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
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.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.Flags;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.platform.test.annotations.EnableFlags;
|
||||
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
import org.junit.Assert;
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ApprovalPreferenceControllerTest {
|
||||
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
|
||||
SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
private Context mContext;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
@Mock
|
||||
private NotificationAccessDetails mFragment;
|
||||
private ApprovalPreferenceController mController;
|
||||
@Mock
|
||||
NotificationManager mNm;
|
||||
@Mock
|
||||
AppOpsManager mAppOpsManager;
|
||||
@Mock
|
||||
PackageManager mPm;
|
||||
PackageInfo mPkgInfo;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
doReturn(mContext).when(mFragment).getContext();
|
||||
|
||||
mPkgInfo = new PackageInfo();
|
||||
mPkgInfo.applicationInfo = mock(ApplicationInfo.class);
|
||||
when(mPkgInfo.applicationInfo.loadLabel(mPm)).thenReturn("LABEL");
|
||||
|
||||
mController = new ApprovalPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setParent(mFragment);
|
||||
mController.setPkgInfo(mPkgInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabled() {
|
||||
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
|
||||
mContext);
|
||||
pref.setAppOps(mAppOpsManager);
|
||||
|
||||
mController.updateState(pref);
|
||||
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_invalidCn_disabled() {
|
||||
ComponentName longCn = new ComponentName("com.example.package",
|
||||
com.google.common.base.Strings.repeat("Blah", 150));
|
||||
mController.setCn(longCn);
|
||||
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
|
||||
mContext);
|
||||
pref.setAppOps(mAppOpsManager);
|
||||
|
||||
mController.updateState(pref);
|
||||
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
|
||||
public void updateState_checked() {
|
||||
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
|
||||
AppOpsManager.MODE_ALLOWED);
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
|
||||
mContext);
|
||||
pref.setAppOps(mAppOpsManager);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
|
||||
public void restrictedSettings_appOpsDisabled() {
|
||||
Assert.assertFalse(android.security.Flags.extendEcmToAllSettings());
|
||||
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString()))
|
||||
.thenReturn(AppOpsManager.MODE_ERRORED);
|
||||
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
|
||||
mContext);
|
||||
pref.setAppOps(mAppOpsManager);
|
||||
mController.setSettingIdentifier(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS);
|
||||
|
||||
mController.updateState(pref);
|
||||
|
||||
verify(mAppOpsManager).noteOpNoThrow(anyInt(), anyInt(), anyString());
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(android.security.Flags.FLAG_EXTEND_ECM_TO_ALL_SETTINGS)
|
||||
public void restrictedSettings_serviceAlreadyEnabled() {
|
||||
when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString())).thenReturn(
|
||||
AppOpsManager.MODE_ERRORED);
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(
|
||||
mContext);
|
||||
pref.setAppOps(mAppOpsManager);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enable() {
|
||||
mController.enable(mCn);
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext,
|
||||
MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW,
|
||||
"a");
|
||||
|
||||
verify(mNm).setNotificationListenerAccessGranted(mCn, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(Flags.FLAG_MODES_API)
|
||||
public void disable() {
|
||||
mController.disable(mCn);
|
||||
verify(mFeatureFactory.metricsFeatureProvider).action(
|
||||
mContext,
|
||||
MetricsProto.MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW,
|
||||
"a");
|
||||
|
||||
verify(mNm).removeAutomaticZenRules(eq(mCn.getPackageName()), eq(true));
|
||||
verify(mNm).setNotificationListenerAccessGranted(mCn, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
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.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BridgedAppsLinkPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private BridgedAppsLinkPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new BridgedAppsLinkPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setUserId(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_notGranted() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(false);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
|
||||
// disables field
|
||||
Preference p = new Preference(mContext);
|
||||
mController.updateState(p);
|
||||
assertThat(p.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_lowTargetSdk_noCustomizations() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.S);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
|
||||
// disables field
|
||||
Preference p = new Preference(mContext);
|
||||
mController.updateState(p);
|
||||
assertThat(p.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_lowTargetSdk_customizations() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.S);
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter();
|
||||
nlf.setTypes(FLAG_FILTER_TYPE_CONVERSATIONS);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
// enables field
|
||||
Preference p = new Preference(mContext);
|
||||
mController.updateState(p);
|
||||
assertThat(p.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_highTargetSdk_noCustomizations() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
|
||||
// enables field
|
||||
Preference p = new Preference(mContext);
|
||||
mController.updateState(p);
|
||||
assertThat(p.isEnabled()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.os.Looper;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settings.widget.AppCheckBoxPreference;
|
||||
import com.android.settingslib.applications.ApplicationsState;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BridgedAppsPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private BridgedAppsPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
PreferenceScreen mScreen;
|
||||
@Mock
|
||||
ApplicationsState mAppState;
|
||||
|
||||
private ApplicationsState.AppEntry mAppEntry;
|
||||
private ApplicationsState.AppEntry mAppEntry2;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
PreferenceManager preferenceManager = new PreferenceManager(mContext);
|
||||
mScreen = preferenceManager.createPreferenceScreen(mContext);
|
||||
|
||||
ApplicationInfo ai = new ApplicationInfo();
|
||||
ai.packageName = "pkg";
|
||||
ai.uid = 12300;
|
||||
ai.sourceDir = "";
|
||||
ApplicationInfo ai2 = new ApplicationInfo();
|
||||
ai2.packageName = "another";
|
||||
ai2.uid = 18800;
|
||||
ai2.sourceDir = "";
|
||||
mAppEntry = new ApplicationsState.AppEntry(mContext, ai, 0);
|
||||
mAppEntry2 = new ApplicationsState.AppEntry(mContext, ai2, 1);
|
||||
|
||||
mAppEntry.info = ai;
|
||||
mAppEntry.label = "hi";
|
||||
|
||||
mController = new BridgedAppsPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setUserId(0);
|
||||
mController.setAppState(mAppState);
|
||||
mController.displayPreference(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRebuildComplete_AddsToScreen() {
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
|
||||
entries.add(mAppEntry);
|
||||
entries.add(mAppEntry2);
|
||||
|
||||
mController.onRebuildComplete(entries);
|
||||
|
||||
assertThat(mScreen.getPreferenceCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRebuildComplete_doesNotReaddToScreen() {
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
AppCheckBoxPreference p = mock(AppCheckBoxPreference.class);
|
||||
when(p.getKey()).thenReturn("pkg|12300");
|
||||
mScreen.addPreference(p);
|
||||
|
||||
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
|
||||
entries.add(mAppEntry);
|
||||
entries.add(mAppEntry2);
|
||||
|
||||
mController.onRebuildComplete(entries);
|
||||
|
||||
assertThat(mScreen.getPreferenceCount()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRebuildComplete_removesExtras() {
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
Preference p = mock(Preference.class);
|
||||
when(p.getKey()).thenReturn("pkg|123");
|
||||
mScreen.addPreference(p);
|
||||
|
||||
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
|
||||
entries.add(mAppEntry);
|
||||
entries.add(mAppEntry2);
|
||||
|
||||
mController.onRebuildComplete(entries);
|
||||
|
||||
assertThat((Preference) mScreen.findPreference("pkg|123")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onRebuildComplete_buildsSetting() {
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
ArrayList<ApplicationsState.AppEntry> entries = new ArrayList<>();
|
||||
entries.add(mAppEntry);
|
||||
|
||||
mController.onRebuildComplete(entries);
|
||||
|
||||
AppCheckBoxPreference actual = mScreen.findPreference("pkg|12300");
|
||||
|
||||
assertThat(actual.isChecked()).isTrue();
|
||||
assertThat(actual.getTitle()).isEqualTo("hi");
|
||||
assertThat(actual.getIcon()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_false() {
|
||||
VersionedPackage vp = new VersionedPackage("pkg", 10567);
|
||||
ArraySet<VersionedPackage> vps = new ArraySet<>();
|
||||
vps.add(vp);
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
|
||||
| FLAG_FILTER_TYPE_CONVERSATIONS, vps);
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
AppCheckBoxPreference pref = new AppCheckBoxPreference(mContext);
|
||||
pref.setKey("pkg|567");
|
||||
|
||||
mController.onPreferenceChange(pref, false);
|
||||
|
||||
ArgumentCaptor<NotificationListenerFilter> captor =
|
||||
ArgumentCaptor.forClass(NotificationListenerFilter.class);
|
||||
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
|
||||
assertThat(captor.getValue().getDisallowedPackages()).contains(
|
||||
new VersionedPackage("pkg", 567));
|
||||
assertThat(captor.getValue().getDisallowedPackages()).contains(
|
||||
new VersionedPackage("pkg", 10567));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_true() {
|
||||
VersionedPackage vp = new VersionedPackage("pkg", 567);
|
||||
VersionedPackage vp2 = new VersionedPackage("pkg", 10567);
|
||||
ArraySet<VersionedPackage> vps = new ArraySet<>();
|
||||
vps.add(vp);
|
||||
vps.add(vp2);
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
|
||||
| FLAG_FILTER_TYPE_CONVERSATIONS, vps);
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
AppCheckBoxPreference pref = new AppCheckBoxPreference(mContext);
|
||||
pref.setKey("pkg|567");
|
||||
|
||||
mController.onPreferenceChange(pref, true);
|
||||
|
||||
ArgumentCaptor<NotificationListenerFilter> captor =
|
||||
ArgumentCaptor.forClass(NotificationListenerFilter.class);
|
||||
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
|
||||
assertThat(captor.getValue().getDisallowedPackages().size()).isEqualTo(1);
|
||||
assertThat(captor.getValue().getDisallowedPackages()).contains(
|
||||
new VersionedPackage("pkg", 10567));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ConversationTypeFilterPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private ConversationTypeFilterPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
ServiceInfo mSi = new ServiceInfo();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new ConversationTypeFilterPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setServiceInfo(mSi);
|
||||
mController.setUserId(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getType() {
|
||||
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.specialaccess.notificationaccess;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MoreSettingsPreferenceControllerTest {
|
||||
|
||||
Context mContext;
|
||||
private MoreSettingsPreferenceController mController;
|
||||
@Mock
|
||||
PackageManager mPm;
|
||||
final String mPkg = "pkg";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new MoreSettingsPreferenceController(mContext);
|
||||
mController.setPackage(mPkg);
|
||||
mController.setPackageManager(mPm);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_available() {
|
||||
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
|
||||
when(mPm.queryIntentActivities(captor.capture(), any())).thenReturn(
|
||||
ImmutableList.of(mock(ResolveInfo.class)));
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
assertThat(captor.getValue().getPackage()).isEqualTo(mPkg);
|
||||
assertThat(captor.getValue().getAction()).contains(
|
||||
NotificationListenerService.ACTION_SETTINGS_HOME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_notAvailable() {
|
||||
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
|
||||
when(mPm.queryIntentActivities(captor.capture(), any())).thenReturn(ImmutableList.of());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState() {
|
||||
Preference preference = new Preference(mContext);
|
||||
mController.updateState(preference);
|
||||
|
||||
assertThat(preference.getIntent().getPackage()).isEqualTo(mPkg);
|
||||
assertThat(preference.getIntent().getAction()).isEqualTo(
|
||||
NotificationListenerService.ACTION_SETTINGS_HOME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
# Default reviewers for this and subdirectories.
|
||||
beverlyt@google.com
|
||||
dsandler@android.com
|
||||
juliacr@google.com
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class OngoingTypeFilterPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private OngoingTypeFilterPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
ServiceInfo mSi = new ServiceInfo();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new OngoingTypeFilterPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setServiceInfo(mSi);
|
||||
mController.setUserId(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getType() {
|
||||
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_ONGOING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreUpgradePreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private PreUpgradePreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new PreUpgradePreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setUserId(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_lowTargetSdk_noCustomizations() {
|
||||
mController.setTargetSdk(Build.VERSION_CODES.S);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_lowTargetSdk_customizations() {
|
||||
mController.setTargetSdk(Build.VERSION_CODES.S);
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter();
|
||||
nlf.setTypes(FLAG_FILTER_TYPE_CONVERSATIONS);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_highTargetSdk_noCustomizations() {
|
||||
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SilentTypeFilterPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private SilentTypeFilterPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
ServiceInfo mSi = new ServiceInfo();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new SilentTypeFilterPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setServiceInfo(mSi);
|
||||
mController.setUserId(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getType() {
|
||||
assertThat(mController.getType()).isEqualTo(FLAG_FILTER_TYPE_SILENT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.specialaccess.notificationaccess;
|
||||
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
|
||||
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
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.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.service.notification.NotificationListenerFilter;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.preference.CheckBoxPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TypeFilterPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private TypeFilterPreferenceController mController;
|
||||
@Mock
|
||||
NotificationBackend mNm;
|
||||
ComponentName mCn = new ComponentName("a", "b");
|
||||
ServiceInfo mSi = new ServiceInfo();
|
||||
|
||||
private static class TestTypeFilterPreferenceController extends TypeFilterPreferenceController {
|
||||
|
||||
public TestTypeFilterPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getType() {
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
mController = new TestTypeFilterPreferenceController(mContext, "key");
|
||||
mController.setCn(mCn);
|
||||
mController.setNm(mNm);
|
||||
mController.setServiceInfo(mSi);
|
||||
mController.setUserId(0);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_notGranted() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(false);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_lowTargetSdk_noCustomizations() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.S);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_lowTargetSdk_customizations() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.S);
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter();
|
||||
nlf.setTypes(FLAG_FILTER_TYPE_CONVERSATIONS);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvailable_highTargetSdk_noCustomizations() {
|
||||
when(mNm.isNotificationListenerAccessGranted(any())).thenReturn(true);
|
||||
mController.setTargetSdk(Build.VERSION_CODES.CUR_DEVELOPMENT + 1);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabled_noMetaData() {
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabled_metaData_notTheDisableFilter() {
|
||||
mSi.metaData = new Bundle();
|
||||
mSi.metaData.putCharSequence("test", "value");
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabled_metaData_disableFilter_notThisField() {
|
||||
mSi.metaData = new Bundle();
|
||||
mSi.metaData.putCharSequence(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
|
||||
"1|alerting");
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_enabled_metaData_disableFilter_thisField_stateIsChecked() {
|
||||
mSi.metaData = new Bundle();
|
||||
mSi.metaData.putCharSequence(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
|
||||
"conversations|2|32");
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(
|
||||
new NotificationListenerFilter(32, new ArraySet<>()));
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_disabled() {
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(false);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(new NotificationListenerFilter());
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_disabled_metaData_disableFilter_thisField_stateIsNotChecked() {
|
||||
mSi.metaData = new Bundle();
|
||||
mSi.metaData.putCharSequence(NotificationListenerService.META_DATA_DISABLED_FILTER_TYPES,
|
||||
"1|2|32");
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
NotificationListenerFilter before = new NotificationListenerFilter(4, new ArraySet<>());
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(before);
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_checked() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(mController.getType(),
|
||||
new ArraySet<>());
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
mController.updateState(pref);
|
||||
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_unchecked() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(mController.getType() - 1,
|
||||
new ArraySet<>());
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
mController.updateState(pref);
|
||||
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_true() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
|
||||
| FLAG_FILTER_TYPE_CONVERSATIONS, new ArraySet<>());
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.onPreferenceChange(pref, true);
|
||||
|
||||
ArgumentCaptor<NotificationListenerFilter> captor =
|
||||
ArgumentCaptor.forClass(NotificationListenerFilter.class);
|
||||
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
|
||||
assertThat(captor.getValue().getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
|
||||
| FLAG_FILTER_TYPE_ONGOING | mController.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_false() {
|
||||
NotificationListenerFilter nlf = new NotificationListenerFilter(FLAG_FILTER_TYPE_ONGOING
|
||||
| FLAG_FILTER_TYPE_CONVERSATIONS | mController.getType(), new ArraySet<>());
|
||||
when(mNm.isNotificationListenerAccessGranted(mCn)).thenReturn(true);
|
||||
when(mNm.getListenerFilter(mCn, 0)).thenReturn(nlf);
|
||||
|
||||
CheckBoxPreference pref = new CheckBoxPreference(mContext);
|
||||
|
||||
mController.onPreferenceChange(pref, false);
|
||||
|
||||
ArgumentCaptor<NotificationListenerFilter> captor =
|
||||
ArgumentCaptor.forClass(NotificationListenerFilter.class);
|
||||
verify(mNm).setListenerFilter(eq(mCn), eq(0), captor.capture());
|
||||
assertThat(captor.getValue().getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
|
||||
| FLAG_FILTER_TYPE_ONGOING);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
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.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BiometricNavigationUtilsTest {
|
||||
|
||||
private static final String SETTINGS_CLASS_NAME = "SettingsClassName";
|
||||
private static final String EXTRA_KEY = "EXTRA_KEY";
|
||||
private static final ComponentName COMPONENT_NAME = new ComponentName("package", "class");
|
||||
private static final int ADMIN_USER_ID = 2;
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private ActivityResultLauncher<Intent> mLauncher;
|
||||
private Context mContext;
|
||||
private BiometricNavigationUtils mBiometricNavigationUtils;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
doNothing().when(mContext).startActivity(any());
|
||||
mBiometricNavigationUtils = new BiometricNavigationUtils(UserHandle.myUserId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_quietMode_launchesQuiteModeDialog() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
|
||||
Bundle.EMPTY, null);
|
||||
|
||||
assertQuietModeDialogLaunchRequested();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_quietMode_returnsFalse() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_quietMode_withLauncher_notThroughLauncher() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
|
||||
Bundle.EMPTY, mLauncher);
|
||||
|
||||
verify(mLauncher, never()).launch(any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_emptyExtras_launchesFragmentWithoutExtras() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null);
|
||||
|
||||
assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_emptyExtras_returnsTrue() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_withExtras_launchesFragmentWithExtras() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
final Bundle extras = createNotEmptyExtras();
|
||||
mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, extras, null);
|
||||
|
||||
assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_withLauncher_launchesThroughLauncher() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
final Bundle extras = createNotEmptyExtras();
|
||||
mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, extras, mLauncher);
|
||||
|
||||
verify(mLauncher).launch(any(Intent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void launchBiometricSettings_noQuietMode_withExtras_returnsTrue() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
|
||||
mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras(), null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBiometricSettingsIntent_quietMode_returnsQuiteModeDialogIntent() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
|
||||
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
|
||||
mContext, SETTINGS_CLASS_NAME, null /* enforcedAdmin */, Bundle.EMPTY);
|
||||
|
||||
assertQuietModeDialogIntent(intent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBiometricSettingsIntent_noQuietMode_emptyExtras_returnsSettingsIntent() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
|
||||
mContext, SETTINGS_CLASS_NAME, null /* enforcedAdmin */, Bundle.EMPTY);
|
||||
|
||||
assertSettingsPageIntent(intent, false /* shouldContainExtras */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBiometricSettingsIntent_noQuietMode_withExtras_returnsSettingsIntent() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
|
||||
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
|
||||
mContext, SETTINGS_CLASS_NAME, null /* enforcedAdmin */, createNotEmptyExtras());
|
||||
|
||||
assertSettingsPageIntent(intent, true /* shouldContainExtras */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBiometricSettingsIntent_whenDisabledByAdmin_quietMode_returnsBlockedIntent() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
|
||||
final EnforcedAdmin enforcedAdmin = new EnforcedAdmin(
|
||||
COMPONENT_NAME, UserHandle.of(ADMIN_USER_ID));
|
||||
|
||||
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
|
||||
mContext, SETTINGS_CLASS_NAME, enforcedAdmin, Bundle.EMPTY);
|
||||
|
||||
assertBlockedByAdminDialogIntent(intent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBiometricSettingsIntent_whenDisabledByAdmin_emptyExtras_returnsBlockedIntent() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
final EnforcedAdmin enforcedAdmin = new EnforcedAdmin(
|
||||
COMPONENT_NAME, UserHandle.of(ADMIN_USER_ID));
|
||||
|
||||
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
|
||||
mContext, SETTINGS_CLASS_NAME, enforcedAdmin, Bundle.EMPTY);
|
||||
|
||||
assertBlockedByAdminDialogIntent(intent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBiometricSettingsIntent_whenDisabledByAdmin_withExtras_returnsBlockedIntent() {
|
||||
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
|
||||
final EnforcedAdmin enforcedAdmin = new EnforcedAdmin(
|
||||
COMPONENT_NAME, UserHandle.of(ADMIN_USER_ID));
|
||||
|
||||
final Intent intent = mBiometricNavigationUtils.getBiometricSettingsIntent(
|
||||
mContext, SETTINGS_CLASS_NAME, enforcedAdmin, Bundle.EMPTY);
|
||||
|
||||
assertBlockedByAdminDialogIntent(intent);
|
||||
}
|
||||
|
||||
private Bundle createNotEmptyExtras() {
|
||||
final Bundle bundle = new Bundle();
|
||||
bundle.putInt(EXTRA_KEY, 0);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
private void assertQuietModeDialogLaunchRequested() {
|
||||
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
verify(mContext).startActivity(intentCaptor.capture());
|
||||
|
||||
Intent intent = intentCaptor.getValue();
|
||||
assertQuietModeDialogIntent(intent);
|
||||
}
|
||||
|
||||
private void assertQuietModeDialogIntent(Intent intent) {
|
||||
assertThat(intent.getComponent().getPackageName())
|
||||
.isEqualTo("android");
|
||||
assertThat(intent.getComponent().getClassName())
|
||||
.isEqualTo("com.android.internal.app.UnlaunchableAppActivity");
|
||||
}
|
||||
|
||||
private void assertBlockedByAdminDialogIntent(Intent intent) {
|
||||
assertThat(intent.getAction()).isEqualTo(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
|
||||
assertThat(
|
||||
(ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN))
|
||||
.isEqualTo(COMPONENT_NAME);
|
||||
}
|
||||
|
||||
private void assertSettingsPageLaunchRequested(boolean shouldContainExtras) {
|
||||
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
verify(mContext).startActivity(intentCaptor.capture());
|
||||
|
||||
Intent intent = intentCaptor.getValue();
|
||||
assertSettingsPageIntent(intent, shouldContainExtras);
|
||||
}
|
||||
|
||||
private void assertSettingsPageIntent(Intent intent, boolean shouldContainExtras) {
|
||||
assertThat(intent.getComponent().getPackageName())
|
||||
.isEqualTo("com.android.settings");
|
||||
assertThat(intent.getComponent().getClassName())
|
||||
.isEqualTo(SETTINGS_CLASS_NAME);
|
||||
assertThat(intent.getExtras().containsKey(EXTRA_KEY)).isEqualTo(shouldContainExtras);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
import static com.android.settings.biometrics.BiometricUtils.GatekeeperCredentialNotMatchException;
|
||||
import static com.android.settings.biometrics.GatekeeperPasswordProvider.containsGatekeeperPasswordHandle;
|
||||
import static com.android.settings.biometrics.GatekeeperPasswordProvider.getGatekeeperPasswordHandle;
|
||||
import static com.android.settings.password.ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.internal.widget.VerifyCredentialResponse;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class GatekeeperPasswordProviderTest {
|
||||
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
@Mock private LockPatternUtils mLockPatternUtils;
|
||||
private GatekeeperPasswordProvider mGatekeeperPasswordProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mGatekeeperPasswordProvider = new GatekeeperPasswordProvider(mLockPatternUtils);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestGatekeeperHatWithHandle_success() {
|
||||
final long gkPwHandle = 1L;
|
||||
final long challenge = 2L;
|
||||
final int userId = 0;
|
||||
final byte[] expectedToken = new byte[] { 3, 2, 1 };
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
|
||||
.thenReturn(newGoodCredential(gkPwHandle, expectedToken));
|
||||
final byte[] actualToken = mGatekeeperPasswordProvider.requestGatekeeperHat(gkPwHandle,
|
||||
challenge, userId);
|
||||
assertThat(actualToken).isNotNull();
|
||||
assertThat(actualToken.length).isEqualTo(expectedToken.length);
|
||||
for (int i = 0; i < actualToken.length; ++i) {
|
||||
assertWithMessage("actualToken[" + i + "] is " + actualToken[i] + " not "
|
||||
+ expectedToken[i]).that(actualToken[i]).isEqualTo(expectedToken[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = GatekeeperCredentialNotMatchException.class)
|
||||
public void testRequestGatekeeperHatWithHandle_GatekeeperCredentialNotMatchException() {
|
||||
final long gkPwHandle = 10L;
|
||||
final long challenge = 20L;
|
||||
final int userId = 300;
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
|
||||
.thenReturn(newBadCredential(0));
|
||||
|
||||
mGatekeeperPasswordProvider.requestGatekeeperHat(gkPwHandle, challenge, userId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestGatekeeperHatWithIntent_success() {
|
||||
final long gkPwHandle = 11L;
|
||||
final long challenge = 21L;
|
||||
final int userId = 145;
|
||||
final byte[] expectedToken = new byte[] { 4, 5, 6, 7 };
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
|
||||
.thenReturn(newGoodCredential(gkPwHandle, expectedToken));
|
||||
final byte[] actualToken = mGatekeeperPasswordProvider.requestGatekeeperHat(
|
||||
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle), challenge, userId);
|
||||
assertThat(actualToken).isNotNull();
|
||||
assertThat(actualToken.length).isEqualTo(expectedToken.length);
|
||||
for (int i = 0; i < actualToken.length; ++i) {
|
||||
assertWithMessage("actualToken[" + i + "] is " + actualToken[i] + " not "
|
||||
+ expectedToken[i]).that(actualToken[i]).isEqualTo(expectedToken[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = GatekeeperCredentialNotMatchException.class)
|
||||
public void testRequestGatekeeperHatWithIntent_GatekeeperCredentialNotMatchException() {
|
||||
final long gkPwHandle = 12L;
|
||||
final long challenge = 22L;
|
||||
final int userId = 0;
|
||||
when(mLockPatternUtils.verifyGatekeeperPasswordHandle(gkPwHandle, challenge, userId))
|
||||
.thenReturn(newBadCredential(0));
|
||||
|
||||
mGatekeeperPasswordProvider.requestGatekeeperHat(
|
||||
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle), challenge, userId);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testRequestGatekeeperHatWithIntent_IllegalStateException() {
|
||||
mGatekeeperPasswordProvider.requestGatekeeperHat(new Intent(), 1L, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsGatekeeperPasswordHandle() {
|
||||
assertThat(containsGatekeeperPasswordHandle(null)).isEqualTo(false);
|
||||
assertThat(containsGatekeeperPasswordHandle(new Intent())).isEqualTo(false);
|
||||
assertThat(containsGatekeeperPasswordHandle(
|
||||
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, 2L))).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGatekeeperPasswordHandle() {
|
||||
assertThat(getGatekeeperPasswordHandle(new Intent())).isEqualTo(0L);
|
||||
assertThat(getGatekeeperPasswordHandle(
|
||||
new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, 3L))).isEqualTo(3L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveGatekeeperPasswordHandleAsHandle() {
|
||||
final long gkPwHandle = 1L;
|
||||
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveGatekeeperPasswordHandleAsIntent() {
|
||||
final long gkPwHandle = 1234L;
|
||||
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, false);
|
||||
|
||||
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
assertThat(intent.getLongExtra(EXTRA_KEY_GK_PW_HANDLE, 0L)).isEqualTo(gkPwHandle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveGatekeeperPasswordHandleAsIntent_removeKey() {
|
||||
final long gkPwHandle = 1234L;
|
||||
final Intent intent = new Intent().putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
|
||||
doNothing().when(mLockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
|
||||
mGatekeeperPasswordProvider.removeGatekeeperPasswordHandle(intent, true);
|
||||
|
||||
verify(mLockPatternUtils, only()).removeGatekeeperPasswordHandle(gkPwHandle);
|
||||
assertThat(intent.hasExtra(EXTRA_KEY_GK_PW_HANDLE)).isEqualTo(false);
|
||||
}
|
||||
|
||||
private VerifyCredentialResponse newGoodCredential(long gkPwHandle, @NonNull byte[] hat) {
|
||||
return new VerifyCredentialResponse.Builder()
|
||||
.setGatekeeperPasswordHandle(gkPwHandle)
|
||||
.setGatekeeperHAT(hat)
|
||||
.build();
|
||||
}
|
||||
|
||||
private VerifyCredentialResponse newBadCredential(int timeout) {
|
||||
if (timeout > 0) {
|
||||
return VerifyCredentialResponse.fromTimeout(timeout);
|
||||
} else {
|
||||
return VerifyCredentialResponse.fromError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include /src/com/android/settings/biometrics/OWNERS
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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;
|
||||
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
|
||||
|
||||
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_KEY_MODALITY;
|
||||
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_DENIED;
|
||||
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_CONSENT_GRANTED;
|
||||
|
||||
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.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.biometrics.face.FaceEnrollParentalConsent;
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollParentalConsent;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ParentalConsentHelperTest {
|
||||
|
||||
private static final int REQUEST_CODE = 12;
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mMocks = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
private Activity mRootActivity;
|
||||
@Mock
|
||||
private Intent mRootActivityIntent;
|
||||
@Captor
|
||||
ArgumentCaptor<Intent> mLastStarted;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
when(mRootActivity.getIntent()).thenAnswer(invocation -> mRootActivityIntent);
|
||||
when(mRootActivityIntent.getBundleExtra(any())).thenAnswer(invocation -> null);
|
||||
when(mRootActivityIntent.getStringExtra(any())).thenAnswer(invocation -> null);
|
||||
when(mRootActivityIntent.getBooleanExtra(any(), anyBoolean()))
|
||||
.thenAnswer(invocation -> invocation.getArguments()[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext__fingerprint_all_consent() {
|
||||
testLaunchNext(
|
||||
true /* requireFace */, true /* grantFace */,
|
||||
true /* requireFingerprint */, true /* grantFace */,
|
||||
90 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_nothing_to_consent() {
|
||||
testLaunchNext(
|
||||
false /* requireFace */, false /* grantFace */,
|
||||
false /* requireFingerprint */, false /* grantFace */,
|
||||
80 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_face_and_fingerprint_no_consent() {
|
||||
testLaunchNext(
|
||||
true /* requireFace */, false /* grantFace */,
|
||||
true /* requireFingerprint */, false /* grantFace */,
|
||||
70 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_face_and_fingerprint_only_face_consent() {
|
||||
testLaunchNext(
|
||||
true /* requireFace */, true /* grantFace */,
|
||||
true /* requireFingerprint */, false /* grantFace */,
|
||||
60 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_face_and_fingerprint_only_fingerprint_consent() {
|
||||
testLaunchNext(
|
||||
true /* requireFace */, false /* grantFace */,
|
||||
true /* requireFingerprint */, true /* grantFace */,
|
||||
50 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_face_with_consent() {
|
||||
testLaunchNext(
|
||||
true /* requireFace */, true /* grantFace */,
|
||||
false /* requireFingerprint */, false /* grantFace */,
|
||||
40 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_face_without_consent() {
|
||||
testLaunchNext(
|
||||
true /* requireFace */, false /* grantFace */,
|
||||
false /* requireFingerprint */, false /* grantFace */,
|
||||
30 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_fingerprint_with_consent() {
|
||||
testLaunchNext(
|
||||
false /* requireFace */, false /* grantFace */,
|
||||
true /* requireFingerprint */, true /* grantFace */,
|
||||
20 /* gkpw */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLaunchNext_fingerprint_without_consent() {
|
||||
testLaunchNext(
|
||||
false /* requireFace */, false /* grantFace */,
|
||||
true /* requireFingerprint */, false /* grantFace */,
|
||||
10 /* gkpw */);
|
||||
}
|
||||
|
||||
private void testLaunchNext(
|
||||
boolean requireFace, boolean grantFace,
|
||||
boolean requireFingerprint, boolean grantFingerprint,
|
||||
long gkpw) {
|
||||
final List<Pair<String, Boolean>> expectedLaunches = new ArrayList<>();
|
||||
if (requireFingerprint) {
|
||||
expectedLaunches.add(
|
||||
new Pair(FingerprintEnrollParentalConsent.class.getName(), grantFingerprint));
|
||||
}
|
||||
if (requireFace) {
|
||||
expectedLaunches.add(new Pair(FaceEnrollParentalConsent.class.getName(), grantFace));
|
||||
}
|
||||
|
||||
// initial consent status
|
||||
final ParentalConsentHelper helper = new ParentalConsentHelper(gkpw);
|
||||
helper.setConsentRequirement(requireFace, requireFingerprint);
|
||||
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
|
||||
.isFalse();
|
||||
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
|
||||
.isFalse();
|
||||
|
||||
// check expected launches
|
||||
for (int i = 0; i <= expectedLaunches.size(); i++) {
|
||||
final Pair<String, Boolean> expected = i > 0 ? expectedLaunches.get(i - 1) : null;
|
||||
final boolean launchedNext = i == 0
|
||||
? helper.launchNext(mRootActivity, REQUEST_CODE)
|
||||
: helper.launchNext(mRootActivity, REQUEST_CODE,
|
||||
expected.second ? RESULT_CONSENT_GRANTED : RESULT_CONSENT_DENIED,
|
||||
getResultIntent(getStartedModality(expected.first)));
|
||||
assertThat(launchedNext).isEqualTo(i < expectedLaunches.size());
|
||||
}
|
||||
verify(mRootActivity, times(expectedLaunches.size()))
|
||||
.startActivityForResult(mLastStarted.capture(), eq(REQUEST_CODE));
|
||||
assertThat(mLastStarted.getAllValues()
|
||||
.stream().map(i -> i.getComponent().getClassName()).collect(Collectors.toList()))
|
||||
.containsExactlyElementsIn(
|
||||
expectedLaunches.stream().map(i -> i.first).collect(Collectors.toList()))
|
||||
.inOrder();
|
||||
if (!expectedLaunches.isEmpty()) {
|
||||
assertThat(mLastStarted.getAllValues()
|
||||
.stream().map(BiometricUtils::getGatekeeperPasswordHandle).distinct()
|
||||
.collect(Collectors.toList()))
|
||||
.containsExactly(gkpw);
|
||||
}
|
||||
|
||||
// final consent status
|
||||
assertThat(ParentalConsentHelper.hasFaceConsent(helper.getConsentResult()))
|
||||
.isEqualTo(requireFace && grantFace);
|
||||
assertThat(ParentalConsentHelper.hasFingerprintConsent(helper.getConsentResult()))
|
||||
.isEqualTo(requireFingerprint && grantFingerprint);
|
||||
}
|
||||
|
||||
private static Intent getResultIntent(@BiometricAuthenticator.Modality int modality) {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(EXTRA_KEY_MODALITY, modality);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@BiometricAuthenticator.Modality
|
||||
private static int getStartedModality(String name) {
|
||||
if (name.equals(FaceEnrollParentalConsent.class.getName())) {
|
||||
return TYPE_FACE;
|
||||
}
|
||||
if (name.equals(FingerprintEnrollParentalConsent.class.getName())) {
|
||||
return TYPE_FINGERPRINT;
|
||||
}
|
||||
return TYPE_NONE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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;
|
||||
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
|
||||
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
|
||||
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ParentalControlsUtilsTest {
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private DevicePolicyManager mDpm;
|
||||
private ComponentName mSupervisionComponentName = new ComponentName("pkg", "cls");
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getContentResolver()).thenReturn(mock(ContentResolver.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper that sets the appropriate mocks and testing behavior before returning the actual
|
||||
* EnforcedAdmin from ParentalControlsUtils.
|
||||
*/
|
||||
@Nullable
|
||||
private RestrictedLockUtils.EnforcedAdmin getEnforcedAdminForCombination(
|
||||
@Nullable ComponentName supervisionComponentName,
|
||||
@BiometricAuthenticator.Modality int modality, int keyguardDisabledFlags) {
|
||||
when(mDpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(any(UserHandle.class)))
|
||||
.thenReturn(supervisionComponentName);
|
||||
when(mDpm.getKeyguardDisabledFeatures(eq(supervisionComponentName)))
|
||||
.thenReturn(keyguardDisabledFlags);
|
||||
|
||||
return ParentalControlsUtils.parentConsentRequiredInternal(mDpm, modality,
|
||||
new UserHandle(UserHandle.myUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnforcedAdmin_whenDpmDisablesBiometricsAndSupervisionComponentExists() {
|
||||
int[][] tests = {
|
||||
{TYPE_FINGERPRINT, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT},
|
||||
{TYPE_FACE, DevicePolicyManager.KEYGUARD_DISABLE_FACE},
|
||||
{TYPE_IRIS, DevicePolicyManager.KEYGUARD_DISABLE_IRIS},
|
||||
};
|
||||
|
||||
for (int i = 0; i < tests.length; i++) {
|
||||
RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForCombination(
|
||||
mSupervisionComponentName, tests[i][0] /* modality */,
|
||||
tests[i][1] /* keyguardDisableFlags */);
|
||||
assertNotNull(admin);
|
||||
assertEquals(UserManager.DISALLOW_BIOMETRIC, admin.enforcedRestriction);
|
||||
assertEquals(mSupervisionComponentName, admin.component);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoEnforcedAdmin_whenNoSupervisionComponent() {
|
||||
// Even if DPM flag exists, returns null EnforcedAdmin when no supervision component exists
|
||||
RestrictedLockUtils.EnforcedAdmin admin = getEnforcedAdminForCombination(
|
||||
null /* supervisionComponentName */, TYPE_FINGERPRINT,
|
||||
DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
|
||||
assertNull(admin);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.combination;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BiometricSettingsAppPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private BiometricSettingsAppPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new BiometricSettingsAppPreferenceController(mContext, "key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.combination;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BiometricSettingsKeyguardPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private BiometricSettingsKeyguardPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new BiometricSettingsKeyguardPreferenceController(mContext, "key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.combination;
|
||||
|
||||
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
|
||||
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
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.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CombinedBiometricStatusUtilsTest {
|
||||
|
||||
private static final ComponentName COMPONENT_NAME =
|
||||
new ComponentName("package", "class");
|
||||
private static final int USER_ID = UserHandle.myUserId();
|
||||
private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
@Mock
|
||||
private FingerprintManager mFingerprintManager;
|
||||
@Mock
|
||||
private FaceManager mFaceManager;
|
||||
|
||||
private Context mApplicationContext;
|
||||
private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
|
||||
@Rule
|
||||
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
|
||||
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
|
||||
.thenReturn(COMPONENT_NAME);
|
||||
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
|
||||
.thenReturn(mFingerprintManager);
|
||||
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
|
||||
.thenReturn(mDevicePolicyManager);
|
||||
when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||
mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(
|
||||
mApplicationContext, USER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withoutFingerprint_whenFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_whenFingerprint_withoutFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_whenFingerprint_whenFace_returnsTrue() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withoutFingerprintHardware_withoutFaceHardware_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withoutFingerprintEnroll_withoutFaceEnroll_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withoutFingerprintEnroll_withFaceEnroll_returnsTrue() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withFingerprintEnroll_withoutFaceEnroll_returnsTrue() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withFingerprintEnroll_withFaceEnroll_returnsTrue() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.hasEnrolled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_whenFingerprintDisabled_whenFaceDisabled_returnsEnforcedAdmin() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
|
||||
.thenReturn(KEYGUARD_DISABLE_FACE | KEYGUARD_DISABLE_FINGERPRINT);
|
||||
|
||||
final RestrictedLockUtils.EnforcedAdmin admin =
|
||||
mCombinedBiometricStatusUtils.getDisablingAdmin();
|
||||
|
||||
assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
|
||||
COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_whenFingerprintDisabled_whenFaceEnabled_returnsNull() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
|
||||
.thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_whenFingerprintEnabled_whenFaceDisabled_returnsNull() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
|
||||
.thenReturn(KEYGUARD_DISABLE_FACE);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_whenFingerprintEnabled_whenFaceEnabled_returnsNull() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenFaceEnrolled_whenMultipleFingerprints_returnsBothFpMultiple() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(createFingerprintList(2));
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_biometric_preference_summary_both_fp_multiple"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenFaceEnrolled_whenSingleFingerprint_returnsBothFpSingle() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(createFingerprintList(1));
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_biometric_preference_summary_both_fp_single"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenFaceEnrolled_whenNoFingerprints_returnsFace() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(createFingerprintList(0));
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_face_preference_summary"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenNoFaceEnrolled_whenMultipleFingerprints_returnsFingerprints() {
|
||||
final int enrolledFingerprintsCount = 2;
|
||||
final int stringResId = ResourcesUtils.getResourcesId(
|
||||
ApplicationProvider.getApplicationContext(), "string",
|
||||
"security_settings_fingerprint_preference_summary");
|
||||
final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
|
||||
enrolledFingerprintsCount, stringResId);
|
||||
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(createFingerprintList(enrolledFingerprintsCount));
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenNoFaceEnrolled_whenSingleFingerprints_returnsFingerprints() {
|
||||
final int enrolledFingerprintsCount = 1;
|
||||
final int stringResId = ResourcesUtils.getResourcesId(
|
||||
ApplicationProvider.getApplicationContext(), "string",
|
||||
"security_settings_fingerprint_preference_summary");
|
||||
final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
|
||||
enrolledFingerprintsCount, stringResId);
|
||||
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(createFingerprintList(enrolledFingerprintsCount));
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenNoFaceEnrolled_whenNoFingerprints_returnsNoneEnrolled() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
|
||||
.thenReturn(createFingerprintList(0));
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_biometric_preference_summary_none_enrolled"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSettingsClassName_returnsCombinedBiometricSettings() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getSettingsClassName())
|
||||
.isEqualTo(Settings.CombinedBiometricSettingsActivity.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getProfileSettingsClassName_returnsCombinedBiometricProfileSettings() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getProfileSettingsClassName())
|
||||
.isEqualTo(Settings.CombinedBiometricProfileSettingsActivity.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getPrivateProfileSettingsClassName_returnsPrivateSpaceBiometricSettings() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
mSetFlagsRule.enableFlags(
|
||||
android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE,
|
||||
android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE);
|
||||
|
||||
assertThat(mCombinedBiometricStatusUtils.getPrivateProfileSettingsClassName())
|
||||
.isEqualTo(Settings.PrivateSpaceBiometricSettingsActivity.class.getName());
|
||||
}
|
||||
|
||||
private List<Fingerprint> createFingerprintList(int size) {
|
||||
final List<Fingerprint> fingerprintList = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
|
||||
}
|
||||
return fingerprintList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.face;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceSettingsAppPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private FaceSettingsAppPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new FaceSettingsAppPreferenceController(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.face;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceSettingsAttentionPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private FaceSettingsAttentionPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new FaceSettingsAttentionPreferenceController(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.face;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceSettingsConfirmPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
private FaceSettingsConfirmPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new FaceSettingsConfirmPreferenceController(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.face;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceSettingsEnrollButtonPreferenceControllerTest {
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private Button mButton;
|
||||
@Mock
|
||||
private FaceSettingsEnrollButtonPreferenceController.Listener mListener;
|
||||
|
||||
private FaceSettingsEnrollButtonPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mController = new FaceSettingsEnrollButtonPreferenceController(mContext);
|
||||
mController.setListener(mListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnClick() {
|
||||
mController.onClick(mButton);
|
||||
|
||||
assertThat(mController.isClicked()).isTrue();
|
||||
verify(mListener).onStartEnrolling(any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.face;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceSettingsKeyguardPreferenceControllerTest {
|
||||
private Context mContext;
|
||||
private FaceSettingsKeyguardPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mController = new FaceSettingsKeyguardPreferenceController(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.face;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.Settings;
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceStatusUtilsTest {
|
||||
|
||||
private static final ComponentName COMPONENT_NAME =
|
||||
new ComponentName("package", "class");
|
||||
private static final int USER_ID = UserHandle.myUserId();
|
||||
private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
|
||||
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
@Mock
|
||||
private FingerprintManager mFingerprintManager;
|
||||
@Mock
|
||||
private FaceManager mFaceManager;
|
||||
|
||||
private Context mApplicationContext;
|
||||
private FaceStatusUtils mFaceStatusUtils;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
|
||||
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
|
||||
.thenReturn(COMPONENT_NAME);
|
||||
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
|
||||
.thenReturn(mFingerprintManager);
|
||||
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
|
||||
.thenReturn(mDevicePolicyManager);
|
||||
when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||
mFaceStatusUtils = new FaceStatusUtils(mApplicationContext, mFaceManager, USER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mFaceStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withoutFingerprint_withFace_returnsTrue() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
assertThat(mFaceStatusUtils.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withFingerprint_withoutFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mFaceStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withFingerprint_withFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
assertThat(mFaceStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withEnrolledTemplates_returnsTrue() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mFaceStatusUtils.hasEnrolled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withoutEnrolledTemplates_returnsFalse() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mFaceStatusUtils.hasEnrolled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_whenFaceDisabled_returnsEnforcedAdmin() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
|
||||
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
|
||||
|
||||
final RestrictedLockUtils.EnforcedAdmin admin = mFaceStatusUtils.getDisablingAdmin();
|
||||
|
||||
assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
|
||||
COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_withFaceEnabled_returnsNull() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
|
||||
|
||||
assertThat(mFaceStatusUtils.getDisablingAdmin()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenNotEnrolled_returnsSummaryNone() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mFaceStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_face_preference_summary_none"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenEnrolled_returnsSummary() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mFaceStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_face_preference_summary"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSettingsClassName_whenNotEnrolled_returnsFaceEnrollInduction() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mFaceStatusUtils.getSettingsClassName())
|
||||
.isEqualTo(FaceEnrollIntroductionInternal.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSettingsClassName_whenEnrolled_returnsFaceSettings() {
|
||||
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mFaceStatusUtils.getSettingsClassName())
|
||||
.isEqualTo(Settings.FaceSettingsInternalActivity.class.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.face;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.face.Face;
|
||||
import android.hardware.face.FaceEnrollCell;
|
||||
import android.hardware.face.FaceEnrollStages;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.os.CancellationSignal;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FaceUpdaterTest {
|
||||
|
||||
private static final byte[] HARDWARE_AUTH_TOKEN = new byte[] {0};
|
||||
private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal();
|
||||
private static final int USER_ID = 0;
|
||||
private static final int ERR_MSG_ID = 0;
|
||||
private static final int HELP_MSG_ID = 0;
|
||||
private static final String HELP_STRING = "";
|
||||
private static final String ERR_STRING = "";
|
||||
private static final Face FACE =
|
||||
new Face(/* name= */"", /* faceId */ 0, /* deviceId= */ 0L);
|
||||
private static final int[] DISABLED_FEATURES = new int[] {0};
|
||||
private static final boolean DEBUG_CONSENT = false;
|
||||
private static final Surface PREVIEW_SURFACE = new Surface();
|
||||
private static final int HELP_CODE = 0;
|
||||
private static final CharSequence HELP_MESSAGE = "";
|
||||
private static final FaceEnrollCell CELL =
|
||||
new FaceEnrollCell(/* x= */ 0, /* y= */ 0, /* z= */ 0);
|
||||
private static final int STAGE = FaceEnrollStages.UNKNOWN;
|
||||
private static final float PAN = 0;
|
||||
private static final float TILT = 0;
|
||||
private static final float DISTANCE = 0;
|
||||
|
||||
|
||||
@Mock private FaceManager mFaceManager;
|
||||
@Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
|
||||
|
||||
private FaceUpdater mFaceUpdater;
|
||||
private Context mContext;
|
||||
private FaceManager.EnrollmentCallback mEnrollmentCallback;
|
||||
private FaceManager.RemovalCallback mRemovalCallback;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mFaceUpdater = new FaceUpdater(mContext, mFaceManager);
|
||||
mEnrollmentCallback = spy(new TestEnrollmentCallback());
|
||||
mRemovalCallback = spy(new TestRemovalCallback());
|
||||
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_firstVersion_onEnrollmentCallbacks_triggerGivenCallback() {
|
||||
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
|
||||
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
|
||||
DISABLED_FEATURES, new Intent());
|
||||
verify(mFaceManager).enroll(
|
||||
eq(USER_ID),
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
callbackCaptor.capture(),
|
||||
same(DISABLED_FEATURES),
|
||||
same(null),
|
||||
eq(false),
|
||||
any());
|
||||
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
|
||||
callback.onEnrollmentProgress(/* remaining= */ 2);
|
||||
callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
|
||||
callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
|
||||
|
||||
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING);
|
||||
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2);
|
||||
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
|
||||
verify(mEnrollmentCallback, atLeast(1))
|
||||
.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_firstVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
|
||||
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
|
||||
DISABLED_FEATURES, new Intent());
|
||||
verify(mFaceManager).enroll(
|
||||
eq(USER_ID),
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
callbackCaptor.capture(),
|
||||
same(DISABLED_FEATURES),
|
||||
same(null),
|
||||
eq(false),
|
||||
any());
|
||||
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentProgress(/* remaining= */ 0);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper, atLeast(1)).isEnabled(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_firstVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
|
||||
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
|
||||
DISABLED_FEATURES, new Intent());
|
||||
verify(mFaceManager).enroll(
|
||||
eq(USER_ID),
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
callbackCaptor.capture(),
|
||||
same(DISABLED_FEATURES),
|
||||
same(null),
|
||||
eq(false),
|
||||
any());
|
||||
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentProgress(/* remaining= */ 1);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_secondVersion_onEnrollmentCallbacks_triggerGivenCallback() {
|
||||
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
|
||||
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
|
||||
DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
|
||||
verify(mFaceManager).enroll(
|
||||
eq(USER_ID),
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
callbackCaptor.capture(),
|
||||
same(DISABLED_FEATURES),
|
||||
same(PREVIEW_SURFACE),
|
||||
eq(DEBUG_CONSENT),
|
||||
any());
|
||||
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
|
||||
callback.onEnrollmentProgress(/* remaining= */ 2);
|
||||
callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
|
||||
callback.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
|
||||
|
||||
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentError(ERR_MSG_ID, ERR_STRING);
|
||||
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentProgress(/* remaining= */ 2);
|
||||
verify(mEnrollmentCallback, atLeast(1)).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
|
||||
verify(mEnrollmentCallback, atLeast(1))
|
||||
.onEnrollmentFrame(HELP_CODE, HELP_MESSAGE, CELL, STAGE, PAN, TILT, DISTANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_secondVersion_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
|
||||
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
|
||||
DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
|
||||
verify(mFaceManager).enroll(
|
||||
eq(USER_ID),
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
callbackCaptor.capture(),
|
||||
same(DISABLED_FEATURES),
|
||||
same(PREVIEW_SURFACE),
|
||||
eq(DEBUG_CONSENT),
|
||||
any());
|
||||
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentProgress(/* remaining= */ 0);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_secondVersion_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FaceManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.EnrollmentCallback.class);
|
||||
mFaceUpdater.enroll(USER_ID, HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, mEnrollmentCallback,
|
||||
DISABLED_FEATURES, PREVIEW_SURFACE, DEBUG_CONSENT, new Intent());
|
||||
verify(mFaceManager).enroll(
|
||||
eq(USER_ID),
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
callbackCaptor.capture(),
|
||||
same(DISABLED_FEATURES),
|
||||
same(PREVIEW_SURFACE),
|
||||
eq(DEBUG_CONSENT),
|
||||
any());
|
||||
FaceManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentProgress(/* remaining= */ 1);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_onRemovalCallbacks_triggerGivenCallback() {
|
||||
ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.RemovalCallback.class);
|
||||
mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback);
|
||||
verify(mFaceManager)
|
||||
.remove(same(FACE), eq(USER_ID), callbackCaptor.capture());
|
||||
FaceManager.RemovalCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onRemovalSucceeded(FACE, /* remaining= */ 1);
|
||||
callback.onRemovalError(FACE, ERR_MSG_ID, ERR_STRING);
|
||||
|
||||
verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1));
|
||||
verify(mRemovalCallback).onRemovalError(FACE, ERR_MSG_ID, ERR_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FaceManager.RemovalCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FaceManager.RemovalCallback.class);
|
||||
mFaceUpdater.remove(FACE, USER_ID, mRemovalCallback);
|
||||
verify(mFaceManager)
|
||||
.remove(same(FACE), eq(USER_ID), callbackCaptor.capture());
|
||||
FaceManager.RemovalCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onRemovalSucceeded(FACE, /* remaining= */ 0);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
|
||||
}
|
||||
|
||||
public static class TestEnrollmentCallback extends FaceManager.EnrollmentCallback {
|
||||
@Override
|
||||
public void onEnrollmentError(int errMsgId, CharSequence errString) {}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentProgress(int remaining) {}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentFrame(int helpCode, @Nullable CharSequence helpMessage,
|
||||
@Nullable FaceEnrollCell cell, int stage, float pan, float tilt, float distance) {}
|
||||
}
|
||||
|
||||
public static class TestRemovalCallback extends FaceManager.RemovalCallback {
|
||||
@Override
|
||||
public void onRemovalError(Face fp, int errMsgId, CharSequence errString) {}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(@Nullable Face fp, int remaining) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.content.Context
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeatureImpl
|
||||
import com.android.settings.biometrics.fingerprint.feature.SfpsRestToUnlockFeatureImpl
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FingerprintFeatureProviderImplTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var mContext: Context
|
||||
|
||||
private lateinit var mFingerprintFeatureProviderImpl: FingerprintFeatureProviderImpl
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
mFingerprintFeatureProviderImpl = FingerprintFeatureProviderImpl()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSfpsEnrollmentFeature_returnDefaultImpl() {
|
||||
assertThat(mFingerprintFeatureProviderImpl.sfpsEnrollmentFeature)
|
||||
.isInstanceOf(SfpsEnrollmentFeatureImpl::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getSfpsRestToUnlockFeature_returnDefaultImpl() {
|
||||
assertThat(mFingerprintFeatureProviderImpl.getSfpsRestToUnlockFeature(mContext))
|
||||
.isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.face.FaceManager;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.utils.StringUtil;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintStatusUtilsTest {
|
||||
|
||||
private static final ComponentName COMPONENT_NAME =
|
||||
new ComponentName("package", "class");
|
||||
private static final int USER_ID = UserHandle.myUserId();
|
||||
private static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
|
||||
|
||||
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
@Mock
|
||||
private FingerprintManager mFingerprintManager;
|
||||
@Mock
|
||||
private FaceManager mFaceManager;
|
||||
|
||||
private Context mApplicationContext;
|
||||
private FingerprintStatusUtils mFingerprintStatusUtils;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mApplicationContext = spy(ApplicationProvider.getApplicationContext());
|
||||
when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
|
||||
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
|
||||
when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
|
||||
.thenReturn(COMPONENT_NAME);
|
||||
when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
|
||||
.thenReturn(mFingerprintManager);
|
||||
when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
|
||||
.thenReturn(mDevicePolicyManager);
|
||||
when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
|
||||
mFingerprintStatusUtils =
|
||||
new FingerprintStatusUtils(mApplicationContext, mFingerprintManager, USER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withoutFingerprint_withFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withFingerprint_withoutFace_returnsTrue() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(false);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_withFingerprint_withFace_returnsFalse() {
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
when(mFaceManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withEnrolledFingerprints_returnsTrue() {
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.hasEnrolled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hasEnrolled_withoutEnrolledFingerprints_returnsFalse() {
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.hasEnrolled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_whenFingerprintDisabled_returnsEnforcedAdmin() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
|
||||
.thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
|
||||
|
||||
final RestrictedLockUtils.EnforcedAdmin admin =
|
||||
mFingerprintStatusUtils.getDisablingAdmin();
|
||||
|
||||
assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
|
||||
COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getDisabledAdmin_withFingerprintEnabled_returnsNull() {
|
||||
when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.getDisablingAdmin()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenNotEnrolled_returnsSummaryNone() {
|
||||
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.getSummary())
|
||||
.isEqualTo(ResourcesUtils.getResourcesString(
|
||||
mApplicationContext,
|
||||
"security_settings_fingerprint_preference_summary_none"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenEnrolled_returnsSummary() {
|
||||
final int enrolledFingerprintsCount = 2;
|
||||
final int stringResId = ResourcesUtils.getResourcesId(
|
||||
ApplicationProvider.getApplicationContext(), "string",
|
||||
"security_settings_fingerprint_preference_summary");
|
||||
final String summary = StringUtil.getIcuPluralsString(mApplicationContext,
|
||||
enrolledFingerprintsCount, stringResId);
|
||||
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
|
||||
when(mFingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
|
||||
createFingerprintList(enrolledFingerprintsCount));
|
||||
|
||||
assertThat(mFingerprintStatusUtils.getSummary()).isEqualTo(summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSettingsClassName_whenNotEnrolled_returnsFingerprintSettings() {
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.getSettingsClassName())
|
||||
.isEqualTo(FingerprintSettings.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSettingsClassName_whenEnrolled_returnsFingerprintSettings() {
|
||||
when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
|
||||
|
||||
assertThat(mFingerprintStatusUtils.getSettingsClassName())
|
||||
.isEqualTo(FingerprintSettings.class.getName());
|
||||
}
|
||||
|
||||
private List<Fingerprint> createFingerprintList(int size) {
|
||||
final List<Fingerprint> fingerprintList = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
|
||||
}
|
||||
return fingerprintList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.os.CancellationSignal;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintUpdaterTest {
|
||||
|
||||
private static final byte[] HARDWARE_AUTH_TOKEN = new byte[] {0};
|
||||
private static final CancellationSignal CANCELLATION_SIGNAL = new CancellationSignal();
|
||||
private static final int USER_ID = 0;
|
||||
private static final int ENROLL_REASON = 0;
|
||||
private static final int ERR_MSG_ID = 0;
|
||||
private static final int HELP_MSG_ID = 0;
|
||||
private static final String HELP_STRING = "";
|
||||
private static final String ERR_STRING = "";
|
||||
private static final Fingerprint FINGERPRINT =
|
||||
new Fingerprint(/* name= */"", /* fingerId */ 0, /* deviceId= */ 0L);
|
||||
|
||||
@Mock private FingerprintManager mFingerprintManager;
|
||||
@Mock private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
|
||||
|
||||
private FingerprintUpdater mFingerprintUpdater;
|
||||
private Context mContext;
|
||||
private FingerprintManager.EnrollmentCallback mEnrollmentCallback;
|
||||
private FingerprintManager.RemovalCallback mRemovalCallback;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mFingerprintUpdater = new FingerprintUpdater(mContext, mFingerprintManager);
|
||||
mEnrollmentCallback = spy(new TestEntrollmentCallback());
|
||||
mRemovalCallback = spy(new TestRemovalCallback());
|
||||
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_onEnrollmentCallbacks_triggerGivenCallback() {
|
||||
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
|
||||
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
|
||||
mEnrollmentCallback, ENROLL_REASON, new Intent());
|
||||
verify(mFingerprintManager).enroll(
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
eq(USER_ID),
|
||||
callbackCaptor.capture(),
|
||||
eq(ENROLL_REASON),
|
||||
any());
|
||||
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentError(ERR_MSG_ID, ERR_STRING);
|
||||
callback.onEnrollmentProgress(/* remaining= */ 2);
|
||||
callback.onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
|
||||
|
||||
verify(mEnrollmentCallback).onEnrollmentError(ERR_MSG_ID, ERR_STRING);
|
||||
verify(mEnrollmentCallback).onEnrollmentProgress(/* remaining= */ 2);
|
||||
verify(mEnrollmentCallback).onEnrollmentHelp(HELP_MSG_ID, HELP_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_onEnrollmentSuccess_invokedInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
|
||||
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
|
||||
mEnrollmentCallback, ENROLL_REASON, new Intent());
|
||||
verify(mFingerprintManager).enroll(
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
eq(USER_ID),
|
||||
callbackCaptor.capture(),
|
||||
eq(ENROLL_REASON),
|
||||
any());
|
||||
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentProgress(/* remaining= */ 0);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enroll_onEnrollmentNotYetFinished_didntInvokeInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FingerprintManager.EnrollmentCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FingerprintManager.EnrollmentCallback.class);
|
||||
mFingerprintUpdater.enroll(HARDWARE_AUTH_TOKEN, CANCELLATION_SIGNAL, USER_ID,
|
||||
mEnrollmentCallback, ENROLL_REASON, new Intent());
|
||||
verify(mFingerprintManager).enroll(
|
||||
same(HARDWARE_AUTH_TOKEN),
|
||||
same(CANCELLATION_SIGNAL),
|
||||
eq(USER_ID),
|
||||
callbackCaptor.capture(),
|
||||
eq(ENROLL_REASON),
|
||||
any());
|
||||
FingerprintManager.EnrollmentCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onEnrollmentProgress(/* remaining= */ 1);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper, never()).isEnabled(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_onRemovalCallbacks_triggerGivenCallback() {
|
||||
ArgumentCaptor<FingerprintManager.RemovalCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class);
|
||||
mFingerprintUpdater.remove(FINGERPRINT, USER_ID, mRemovalCallback);
|
||||
verify(mFingerprintManager)
|
||||
.remove(same(FINGERPRINT), eq(USER_ID), callbackCaptor.capture());
|
||||
FingerprintManager.RemovalCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onRemovalSucceeded(FINGERPRINT, /* remaining= */ 1);
|
||||
callback.onRemovalError(FINGERPRINT, ERR_MSG_ID, ERR_STRING);
|
||||
|
||||
verify(mRemovalCallback).onRemovalSucceeded(any(), eq(1));
|
||||
verify(mRemovalCallback).onRemovalError(FINGERPRINT, ERR_MSG_ID, ERR_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_onRemovalSuccess_invokedInteractionWithSafetyCenter() {
|
||||
ArgumentCaptor<FingerprintManager.RemovalCallback> callbackCaptor =
|
||||
ArgumentCaptor.forClass(FingerprintManager.RemovalCallback.class);
|
||||
mFingerprintUpdater.remove(FINGERPRINT, USER_ID, mRemovalCallback);
|
||||
verify(mFingerprintManager)
|
||||
.remove(same(FINGERPRINT), eq(USER_ID), callbackCaptor.capture());
|
||||
FingerprintManager.RemovalCallback callback = callbackCaptor.getValue();
|
||||
|
||||
callback.onRemovalSucceeded(FINGERPRINT, /* remaining= */ 0);
|
||||
|
||||
verify(mSafetyCenterManagerWrapper).isEnabled(mContext);
|
||||
}
|
||||
|
||||
public static class TestEntrollmentCallback extends FingerprintManager.EnrollmentCallback {
|
||||
@Override
|
||||
public void onEnrollmentError(int errMsgId, CharSequence errString) {}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {}
|
||||
|
||||
@Override
|
||||
public void onEnrollmentProgress(int remaining) {}
|
||||
}
|
||||
|
||||
public static class TestRemovalCallback extends FingerprintManager.RemovalCallback {
|
||||
@Override
|
||||
public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {}
|
||||
|
||||
@Override
|
||||
public void onRemovalSucceeded(@Nullable Fingerprint fp, int remaining) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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.feature
|
||||
|
||||
import android.animation.Animator
|
||||
import android.content.Context
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.view.View
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_CENTER
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_FINGERTIP
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_LEFT_EDGE
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_NO_ANIMATION
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.SFPS_STAGE_RIGHT_EDGE
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.STAGE_UNKNOWN
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlin.math.roundToInt
|
||||
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.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SfpsEnrollmentFeatureImplTest {
|
||||
private val STAGE_0 = 0
|
||||
private val STAGE_1 = 1
|
||||
private val STAGE_2 = 2
|
||||
private val STAGE_3 = 3
|
||||
private val STAGE_4 = 4
|
||||
|
||||
private val THRESHOLD_0 = 0f
|
||||
private val THRESHOLD_1 = .36f
|
||||
private val THRESHOLD_2 = .52f
|
||||
private val THRESHOLD_3 = .76f
|
||||
private val THRESHOLD_4 = 1f
|
||||
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Spy
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock
|
||||
private lateinit var mockFingerprintManager: FingerprintManager
|
||||
|
||||
private val mSfpsEnrollmentFeatureImpl: SfpsEnrollmentFeatureImpl = SfpsEnrollmentFeatureImpl()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
whenever(context.getSystemService(FingerprintManager::class.java))
|
||||
.thenReturn(mockFingerprintManager)
|
||||
doReturn(THRESHOLD_0).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_0)
|
||||
doReturn(THRESHOLD_1).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_1)
|
||||
doReturn(THRESHOLD_2).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_2)
|
||||
doReturn(THRESHOLD_3).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_3)
|
||||
doReturn(THRESHOLD_4).`when`(mockFingerprintManager).getEnrollStageThreshold(STAGE_4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetEnrollStageThreshold() {
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_0))
|
||||
.isEqualTo(THRESHOLD_0)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_1))
|
||||
.isEqualTo(THRESHOLD_1)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_2))
|
||||
.isEqualTo(THRESHOLD_2)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_3))
|
||||
.isEqualTo(THRESHOLD_3)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, STAGE_4))
|
||||
.isEqualTo(THRESHOLD_4)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetHelpAnimator() {
|
||||
val mockView: View = mock(View::class.java)
|
||||
val animator: Animator = mSfpsEnrollmentFeatureImpl.getHelpAnimator(mockView)
|
||||
assertThat(animator.duration).isEqualTo(SfpsEnrollmentFeatureImpl.HELP_ANIMATOR_DURATION)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetCurrentSfpsEnrollStage() {
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(0, null))
|
||||
.isEqualTo(STAGE_UNKNOWN)
|
||||
val mapper = { i: Int ->
|
||||
(25 * mSfpsEnrollmentFeatureImpl.getEnrollStageThreshold(context, i)).roundToInt()
|
||||
}
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(-1, mapper))
|
||||
.isEqualTo(SFPS_STAGE_NO_ANIMATION)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(0, mapper))
|
||||
.isEqualTo(SFPS_STAGE_CENTER)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(9, mapper))
|
||||
.isEqualTo(SFPS_STAGE_FINGERTIP)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(13, mapper))
|
||||
.isEqualTo(SFPS_STAGE_LEFT_EDGE)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(19, mapper))
|
||||
.isEqualTo(SFPS_STAGE_RIGHT_EDGE)
|
||||
assertThat(mSfpsEnrollmentFeatureImpl.getCurrentSfpsEnrollStage(25, mapper))
|
||||
.isEqualTo(SFPS_STAGE_RIGHT_EDGE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetFeaturedStageHeaderResource() {
|
||||
val type = "string"
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_NO_ANIMATION)
|
||||
).isEqualTo(
|
||||
getSettingsResourcesId(type, "security_settings_fingerprint_enroll_repeat_title")
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_CENTER)
|
||||
).isEqualTo(
|
||||
getSettingsResourcesId(type, "security_settings_sfps_enroll_finger_center_title")
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_FINGERTIP)
|
||||
).isEqualTo(
|
||||
getSettingsResourcesId(type, "security_settings_sfps_enroll_fingertip_title")
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_LEFT_EDGE)
|
||||
).isEqualTo(
|
||||
getSettingsResourcesId(type, "security_settings_sfps_enroll_left_edge_title")
|
||||
)
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getFeaturedStageHeaderResource(SFPS_STAGE_RIGHT_EDGE)
|
||||
).isEqualTo(
|
||||
getSettingsResourcesId(type, "security_settings_sfps_enroll_right_edge_title")
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSfpsEnrollLottiePerStage() {
|
||||
val type = "raw"
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_NO_ANIMATION)
|
||||
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_no_animation"))
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_CENTER)
|
||||
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_pad_center"))
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_FINGERTIP)
|
||||
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_tip"))
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_LEFT_EDGE)
|
||||
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_left_edge"))
|
||||
assertThat(
|
||||
mSfpsEnrollmentFeatureImpl.getSfpsEnrollLottiePerStage(SFPS_STAGE_RIGHT_EDGE)
|
||||
).isEqualTo(getSettingsResourcesId(type, "sfps_lottie_right_edge"))
|
||||
}
|
||||
|
||||
private fun getSettingsResourcesId(type: String, name: String) : Int {
|
||||
return context.resources.getIdentifier(name, type, context.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.feature
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settingslib.RestrictedSwitchPreference
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class SfpsRestToUnlockFeatureImplTest {
|
||||
|
||||
private lateinit var mContext: Context
|
||||
|
||||
private lateinit var mSfpsRestToUnlockFeatureImpl: SfpsRestToUnlockFeatureImpl
|
||||
|
||||
private lateinit var mRestrictedSwitchPreferenceSpy: RestrictedSwitchPreference
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext()
|
||||
mSfpsRestToUnlockFeatureImpl = SfpsRestToUnlockFeatureImpl()
|
||||
mRestrictedSwitchPreferenceSpy = Mockito.spy(RestrictedSwitchPreference(mContext))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getDescriptionForSfps_isNotNull() {
|
||||
assertThat(mSfpsRestToUnlockFeatureImpl)
|
||||
.isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
|
||||
assertThat(mSfpsRestToUnlockFeatureImpl.getDescriptionForSfps(mContext))
|
||||
.isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getRestToUnlockLayout_isNull() {
|
||||
assertThat(mSfpsRestToUnlockFeatureImpl)
|
||||
.isInstanceOf(SfpsRestToUnlockFeatureImpl::class.java)
|
||||
assertThat(mSfpsRestToUnlockFeatureImpl.getRestToUnlockLayout(mContext))
|
||||
.isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fingerprint_settings_setupFingerprintUnlockCategoryPreferences() {
|
||||
assertThat(mSfpsRestToUnlockFeatureImpl.getRestToUnlockPreference(mContext))
|
||||
.isNull()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include /src/com/android/settings/biometrics/OWNERS
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.repository;
|
||||
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_HOME_BUTTON;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UNKNOWN;
|
||||
|
||||
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
|
||||
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints;
|
||||
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupSuwMaxFingerprintsEnrollable;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintRepositoryTest {
|
||||
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
@Mock private Resources mResources;
|
||||
@Mock private FingerprintManager mFingerprintManager;
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forUnknownSensor() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UNKNOWN, 1);
|
||||
assertThat(repository.canAssumeUdfps()).isFalse();
|
||||
assertThat(repository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forRearSensor() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_REAR, 1);
|
||||
assertThat(repository.canAssumeUdfps()).isFalse();
|
||||
assertThat(repository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forUdfpsUltrasonicSensor() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UDFPS_ULTRASONIC, 1);
|
||||
assertThat(repository.canAssumeUdfps()).isTrue();
|
||||
assertThat(repository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forUdfpsOpticalSensor() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UDFPS_OPTICAL, 1);
|
||||
assertThat(repository.canAssumeUdfps()).isTrue();
|
||||
assertThat(repository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forPowerButtonSensor() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_POWER_BUTTON, 1);
|
||||
assertThat(repository.canAssumeUdfps()).isFalse();
|
||||
assertThat(repository.canAssumeSfps()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSensorType_forHomeButtonSensor() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_HOME_BUTTON, 1);
|
||||
assertThat(repository.canAssumeUdfps()).isFalse();
|
||||
assertThat(repository.canAssumeSfps()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMaxFingerprints() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UNKNOWN, 999);
|
||||
assertThat(repository.getMaxFingerprints()).isEqualTo(999);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNumOfEnrolledFingerprintsSize() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UNKNOWN, 999);
|
||||
setupFingerprintEnrolledFingerprints(mFingerprintManager, 10, 3);
|
||||
setupFingerprintEnrolledFingerprints(mFingerprintManager, 22, 99);
|
||||
|
||||
assertThat(repository.getNumOfEnrolledFingerprintsSize(10)).isEqualTo(3);
|
||||
assertThat(repository.getNumOfEnrolledFingerprintsSize(22)).isEqualTo(99);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMaxFingerprintsInSuw() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UNKNOWN, 999);
|
||||
setupSuwMaxFingerprintsEnrollable(mContext, mResources, 333);
|
||||
assertThat(repository.getMaxFingerprintsInSuw(mResources))
|
||||
.isEqualTo(333);
|
||||
|
||||
setupSuwMaxFingerprintsEnrollable(mContext, mResources, 20);
|
||||
assertThat(repository.getMaxFingerprintsInSuw(mResources)).isEqualTo(20);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstFingerprintSensorPropertiesInternal() {
|
||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
final FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
|
||||
0 /* sensorId */,
|
||||
SensorProperties.STRENGTH_STRONG,
|
||||
5,
|
||||
new ArrayList<>() /* componentInfo */,
|
||||
TYPE_UDFPS_OPTICAL,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */);
|
||||
props.add(prop);
|
||||
doAnswer(invocation -> {
|
||||
final IFingerprintAuthenticatorsRegisteredCallback callback =
|
||||
invocation.getArgument(0);
|
||||
callback.onAllAuthenticatorsRegistered(props);
|
||||
return null;
|
||||
}).when(mFingerprintManager).addAuthenticatorsRegisteredCallback(any());
|
||||
|
||||
final FingerprintRepository repository = new FingerprintRepository(mFingerprintManager);
|
||||
assertThat(repository.getFirstFingerprintSensorPropertiesInternal()).isEqualTo(prop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnrollStageCount() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UNKNOWN, 999);
|
||||
|
||||
final int expectedValue = 24;
|
||||
doReturn(expectedValue).when(mFingerprintManager).getEnrollStageCount();
|
||||
|
||||
assertThat(repository.getEnrollStageCount()).isEqualTo(expectedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnrollStageThreshold() {
|
||||
final FingerprintRepository repository = newFingerprintRepository(mFingerprintManager,
|
||||
TYPE_UNKNOWN, 999);
|
||||
|
||||
final float expectedValue0 = 0.42f;
|
||||
final float expectedValue1 = 0.24f;
|
||||
final float expectedValue2 = 0.33f;
|
||||
final float expectedValue3 = 0.90f;
|
||||
doReturn(expectedValue0).when(mFingerprintManager).getEnrollStageThreshold(0);
|
||||
doReturn(expectedValue1).when(mFingerprintManager).getEnrollStageThreshold(1);
|
||||
doReturn(expectedValue2).when(mFingerprintManager).getEnrollStageThreshold(2);
|
||||
doReturn(expectedValue3).when(mFingerprintManager).getEnrollStageThreshold(3);
|
||||
|
||||
assertThat(repository.getEnrollStageThreshold(2)).isEqualTo(expectedValue2);
|
||||
assertThat(repository.getEnrollStageThreshold(1)).isEqualTo(expectedValue1);
|
||||
assertThat(repository.getEnrollStageThreshold(3)).isEqualTo(expectedValue3);
|
||||
assertThat(repository.getEnrollStageThreshold(0)).isEqualTo(expectedValue0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.model
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.os.UserHandle
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics.BiometricEnrollBase
|
||||
import com.android.settings.password.ChooseLockSettingsHelper
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.Arrays
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CredentialModelTest {
|
||||
|
||||
private val clock = SystemClock.elapsedRealtimeClock()
|
||||
|
||||
@Test
|
||||
fun testNullBundle() {
|
||||
val credentialModel = CredentialModel(null, clock)
|
||||
Truth.assertThat(credentialModel.userId).isEqualTo(UserHandle.myUserId())
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newCredentialModelIntentExtras(
|
||||
userId: Int, challenge: Long,
|
||||
token: ByteArray?, gkPwHandle: Long
|
||||
): Bundle {
|
||||
val bundle = Bundle()
|
||||
bundle.putInt(Intent.EXTRA_USER_ID, userId)
|
||||
bundle.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge)
|
||||
bundle.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token)
|
||||
bundle.putLong(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
|
||||
return bundle
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newValidTokenCredentialIntentExtras(userId: Int): Bundle {
|
||||
return newCredentialModelIntentExtras(
|
||||
userId, 1L, byteArrayOf(0, 1, 2),
|
||||
CredentialModel.INVALID_GK_PW_HANDLE
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newOnlySensorValidCredentialIntentExtras(userId: Int): Bundle {
|
||||
return newCredentialModelIntentExtras(
|
||||
userId, CredentialModel.INVALID_CHALLENGE, null,
|
||||
CredentialModel.INVALID_GK_PW_HANDLE
|
||||
)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newGkPwHandleCredentialIntentExtras(userId: Int, gkPwHandle: Long): Bundle {
|
||||
return newCredentialModelIntentExtras(
|
||||
userId,
|
||||
CredentialModel.INVALID_CHALLENGE,
|
||||
null,
|
||||
gkPwHandle
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkBundleLongValue(
|
||||
bundle1: Bundle, bundle2: Bundle,
|
||||
key: String
|
||||
) {
|
||||
if (!bundle1.containsKey(key)) {
|
||||
return
|
||||
}
|
||||
val value1 = bundle1.getInt(key)
|
||||
val value2 = bundle2.getInt(key)
|
||||
Truth.assertWithMessage(
|
||||
"bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
|
||||
+ value2
|
||||
).that(value1).isEqualTo(value2)
|
||||
}
|
||||
|
||||
private fun checkBundleIntValue(
|
||||
bundle1: Bundle, bundle2: Bundle,
|
||||
key: String
|
||||
) {
|
||||
if (!bundle1.containsKey(key)) {
|
||||
return
|
||||
}
|
||||
val value1 = bundle1.getLong(key)
|
||||
val value2 = bundle2.getLong(key)
|
||||
Truth.assertWithMessage(
|
||||
"bundle not match, key:" + key + ", value1:" + value1 + ", value2:"
|
||||
+ value2
|
||||
).that(value1).isEqualTo(value2)
|
||||
}
|
||||
|
||||
private fun checkBundleByteArrayValue(
|
||||
bundle1: Bundle, bundle2: Bundle,
|
||||
key: String
|
||||
) {
|
||||
if (!bundle1.containsKey(key)) {
|
||||
return
|
||||
}
|
||||
val value1 = bundle1.getByteArray(key)
|
||||
val value2 = bundle2.getByteArray(key)
|
||||
val errMsg = ("bundle not match, key:" + key + ", value1:" + Arrays.toString(value1)
|
||||
+ ", value2:" + Arrays.toString(value2))
|
||||
if (value1 == null) {
|
||||
Truth.assertWithMessage(errMsg).that(value2).isNull()
|
||||
} else {
|
||||
Truth.assertWithMessage(errMsg).that(value1.size).isEqualTo(
|
||||
value2!!.size
|
||||
)
|
||||
for (i in value1.indices) {
|
||||
Truth.assertWithMessage(errMsg).that(value1[i]).isEqualTo(
|
||||
value2[i]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.model
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics.BiometricEnrollActivity
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EnrollmentRequestTest {
|
||||
|
||||
private val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
|
||||
@Test
|
||||
fun testIsSuw() {
|
||||
// Default false
|
||||
Truth.assertThat(EnrollmentRequest(Intent(), context, true).isSuw).isFalse()
|
||||
Truth.assertThat(EnrollmentRequest(Intent(), context, false).isSuw).isFalse()
|
||||
val trueIntent = Intent()
|
||||
trueIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
|
||||
Truth.assertThat(EnrollmentRequest(trueIntent, context, true).isSuw).isTrue()
|
||||
Truth.assertThat(EnrollmentRequest(trueIntent, context, false).isSuw).isFalse()
|
||||
val falseIntent = Intent()
|
||||
trueIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, false)
|
||||
Truth.assertThat(EnrollmentRequest(falseIntent, context, true).isSuw).isFalse()
|
||||
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSuw).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsAfterSuwOrSuwSuggestedAction() {
|
||||
// Default false
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(Intent(), context, true)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(Intent(), context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
val deferredTrueIntent = Intent()
|
||||
deferredTrueIntent.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, true)
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(deferredTrueIntent, context, true)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isTrue()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(deferredTrueIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
val deferredFalseIntent = Intent()
|
||||
deferredFalseIntent.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false)
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(deferredFalseIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(deferredFalseIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
val portalTrueIntent = Intent()
|
||||
portalTrueIntent.putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, true)
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(portalTrueIntent, context, true)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isTrue()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(portalTrueIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
val portalFalseIntent = Intent()
|
||||
portalFalseIntent.putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, false)
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(portalFalseIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(portalFalseIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
val suggestedTrueIntent = Intent()
|
||||
suggestedTrueIntent.putExtra(WizardManagerHelper.EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW, true)
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(suggestedTrueIntent, context, true)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isTrue()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(suggestedTrueIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
val suggestedFalseIntent = Intent()
|
||||
suggestedFalseIntent.putExtra(WizardManagerHelper.EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW, false)
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(suggestedFalseIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
Truth.assertThat(
|
||||
EnrollmentRequest(suggestedFalseIntent, context, false)
|
||||
.isAfterSuwOrSuwSuggestedAction
|
||||
).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSuwExtras_inSuw() {
|
||||
val suwIntent = Intent()
|
||||
suwIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
|
||||
val setupRequest = EnrollmentRequest(suwIntent, context, true)
|
||||
val bundle = setupRequest.suwExtras
|
||||
Truth.assertThat(bundle).isNotNull()
|
||||
Truth.assertThat(bundle.size()).isAtLeast(1)
|
||||
Truth.assertThat(bundle.getBoolean(WizardManagerHelper.EXTRA_IS_SETUP_FLOW)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetSuwExtras_notInSuw() {
|
||||
val suwIntent = Intent()
|
||||
suwIntent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true)
|
||||
val setupRequest = EnrollmentRequest(suwIntent, context, false)
|
||||
val bundle = setupRequest.suwExtras
|
||||
Truth.assertThat(bundle).isNotNull()
|
||||
Truth.assertThat(bundle.size()).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsSkipIntro() {
|
||||
// Default false
|
||||
Truth.assertThat(EnrollmentRequest(Intent(), context, true).isSkipIntro).isFalse()
|
||||
Truth.assertThat(EnrollmentRequest(Intent(), context, false).isSkipIntro).isFalse()
|
||||
val trueIntent = Intent()
|
||||
trueIntent.putExtra(BiometricEnrollActivity.EXTRA_SKIP_INTRO, true)
|
||||
Truth.assertThat(EnrollmentRequest(trueIntent, context, true).isSkipIntro).isTrue()
|
||||
Truth.assertThat(EnrollmentRequest(trueIntent, context, false).isSkipIntro).isTrue()
|
||||
val falseIntent = Intent()
|
||||
falseIntent.putExtra(BiometricEnrollActivity.EXTRA_SKIP_INTRO, false)
|
||||
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipIntro).isFalse()
|
||||
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipIntro).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsSkipFindSensor() {
|
||||
// Default false
|
||||
Truth.assertThat(EnrollmentRequest(Intent(), context, true).isSkipFindSensor)
|
||||
.isFalse()
|
||||
Truth.assertThat(EnrollmentRequest(Intent(), context, false).isSkipFindSensor)
|
||||
.isFalse()
|
||||
val trueIntent = Intent()
|
||||
trueIntent.putExtra(EnrollmentRequest.EXTRA_SKIP_FIND_SENSOR, true)
|
||||
Truth.assertThat(EnrollmentRequest(trueIntent, context, true).isSkipFindSensor).isTrue()
|
||||
Truth.assertThat(EnrollmentRequest(trueIntent, context, false).isSkipFindSensor).isTrue()
|
||||
val falseIntent = Intent()
|
||||
falseIntent.putExtra(EnrollmentRequest.EXTRA_SKIP_FIND_SENSOR, false)
|
||||
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipFindSensor)
|
||||
.isFalse()
|
||||
Truth.assertThat(EnrollmentRequest(falseIntent, context, false).isSkipFindSensor)
|
||||
.isFalse()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.os.UserHandle
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.internal.widget.LockPatternUtils
|
||||
import com.android.internal.widget.VerifyCredentialResponse
|
||||
import com.android.settings.biometrics.BiometricEnrollBase
|
||||
import com.android.settings.biometrics2.ui.model.CredentialModel
|
||||
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newGkPwHandleCredentialIntentExtras
|
||||
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newOnlySensorValidCredentialIntentExtras
|
||||
import com.android.settings.biometrics2.ui.model.CredentialModelTest.Companion.newValidTokenCredentialIntentExtras
|
||||
import com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.ChallengeGenerator
|
||||
import com.android.settings.password.ChooseLockPattern
|
||||
import com.android.settings.password.ChooseLockSettingsHelper
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
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
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AutoCredentialViewModelTest {
|
||||
|
||||
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Mock private lateinit var lockPatternUtils: LockPatternUtils
|
||||
|
||||
private var challengeGenerator: TestChallengeGenerator = TestChallengeGenerator()
|
||||
|
||||
private lateinit var viewModel: AutoCredentialViewModel
|
||||
private fun newAutoCredentialViewModel(bundle: Bundle?): AutoCredentialViewModel {
|
||||
return AutoCredentialViewModel(
|
||||
ApplicationProvider.getApplicationContext(),
|
||||
lockPatternUtils,
|
||||
challengeGenerator,
|
||||
CredentialModel(bundle, SystemClock.elapsedRealtimeClock())
|
||||
)
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
challengeGenerator = TestChallengeGenerator()
|
||||
}
|
||||
|
||||
private fun setupGenerateChallenge(userId: Int, newSensorId: Int, newChallenge: Long) {
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
challengeGenerator.userId = userId
|
||||
challengeGenerator.sensorId = newSensorId
|
||||
challengeGenerator.challenge = newChallenge
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_validCredentialCase() = runTest {
|
||||
val userId = 99
|
||||
viewModel = newAutoCredentialViewModel(newValidTokenCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CredentialAction.CREDENTIAL_VALID)
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_needToChooseLock() = runTest {
|
||||
val userId = 100
|
||||
viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
|
||||
)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CHOOSE_LOCK)
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_needToConfirmLockForSomething() = runTest {
|
||||
val userId = 101
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_needToConfirmLockForNumeric() = runTest {
|
||||
val userId = 102
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
|
||||
)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_needToConfirmLockForAlphabetic() = runTest {
|
||||
val userId = 103
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC
|
||||
)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(this)
|
||||
runCurrent()
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CredentialAction.FAIL_NEED_TO_CONFIRM_LOCK)
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_generateChallenge() = runTest {
|
||||
val userId = 104
|
||||
val gkPwHandle = 1111L
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
val newSensorId = 10
|
||||
val newChallenge = 20L
|
||||
setupGenerateChallenge(userId, newSensorId, newChallenge)
|
||||
whenever(
|
||||
lockPatternUtils.verifyGatekeeperPasswordHandle(
|
||||
gkPwHandle,
|
||||
newChallenge,
|
||||
userId
|
||||
)
|
||||
)
|
||||
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
|
||||
val hasCalledRemoveGkPwHandle = AtomicBoolean()
|
||||
Mockito.doAnswer {
|
||||
hasCalledRemoveGkPwHandle.set(true)
|
||||
null
|
||||
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
// Check viewModel behavior
|
||||
assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
|
||||
// Check data inside CredentialModel
|
||||
assertThat(viewModel.token).isNotNull()
|
||||
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
|
||||
assertThat(hasCalledRemoveGkPwHandle.get()).isFalse()
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
val generatingChallengeExtras = viewModel.createGeneratingChallengeExtras()
|
||||
assertThat(generatingChallengeExtras).isNotNull()
|
||||
assertThat(generatingChallengeExtras!!.getLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE))
|
||||
.isEqualTo(newChallenge)
|
||||
val tokens =
|
||||
generatingChallengeExtras.getByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN)
|
||||
assertThat(tokens).isNotNull()
|
||||
assertThat(tokens!!.size).isEqualTo(1)
|
||||
assertThat(tokens[0]).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckCredential_generateChallengeFail() = runTest {
|
||||
backgroundScope.launch {
|
||||
val userId = 104
|
||||
val gkPwHandle = 1111L
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
val newSensorId = 10
|
||||
val newChallenge = 20L
|
||||
setupGenerateChallenge(userId, newSensorId, newChallenge)
|
||||
whenever(
|
||||
lockPatternUtils.verifyGatekeeperPasswordHandle(
|
||||
gkPwHandle,
|
||||
newChallenge,
|
||||
userId
|
||||
)
|
||||
)
|
||||
.thenReturn(newBadCredential(0))
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run credential check
|
||||
val action = viewModel.checkCredential(this)
|
||||
runCurrent()
|
||||
|
||||
assertThat(action).isEqualTo(CredentialAction.IS_GENERATING_CHALLENGE)
|
||||
assertThat(generateFails.size).isEqualTo(1)
|
||||
assertThat(generateFails[0]).isEqualTo(true)
|
||||
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
|
||||
|
||||
// Check createGeneratingChallengeExtras()
|
||||
assertThat(viewModel.createGeneratingChallengeExtras()).isNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetUserId_fromIntent() {
|
||||
val userId = 106
|
||||
viewModel = newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
|
||||
// Get userId
|
||||
assertThat(viewModel.userId).isEqualTo(userId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateChallengeAsCredentialActivityResult_invalidChooseLock() = runTest {
|
||||
backgroundScope.launch {
|
||||
val userId = 107
|
||||
val gkPwHandle = 3333L
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
|
||||
val intent = Intent()
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run generateChallengeAsCredentialActivityResult()
|
||||
val ret = viewModel.generateChallengeAsCredentialActivityResult(
|
||||
true,
|
||||
ActivityResult(ChooseLockPattern.RESULT_FINISHED + 1, intent),
|
||||
backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(ret).isFalse()
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateChallengeAsCredentialActivityResult_invalidConfirmLock() = runTest {
|
||||
backgroundScope.launch {
|
||||
val userId = 107
|
||||
val gkPwHandle = 3333L
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
|
||||
val intent = Intent()
|
||||
intent.putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run generateChallengeAsCredentialActivityResult()
|
||||
val ret = viewModel.generateChallengeAsCredentialActivityResult(
|
||||
false,
|
||||
ActivityResult(Activity.RESULT_OK + 1, intent),
|
||||
backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(ret).isFalse()
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateChallengeAsCredentialActivityResult_nullDataChooseLock() = runTest {
|
||||
val userId = 108
|
||||
val gkPwHandle = 4444L
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newGkPwHandleCredentialIntentExtras(userId, gkPwHandle))
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run generateChallengeAsCredentialActivityResult()
|
||||
val ret = viewModel.generateChallengeAsCredentialActivityResult(
|
||||
true,
|
||||
ActivityResult(ChooseLockPattern.RESULT_FINISHED, null),
|
||||
backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(ret).isFalse()
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateChallengeAsCredentialActivityResult_nullDataConfirmLock() = runTest {
|
||||
val userId = 109
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run generateChallengeAsCredentialActivityResult()
|
||||
val ret = viewModel.generateChallengeAsCredentialActivityResult(
|
||||
false,
|
||||
ActivityResult(Activity.RESULT_OK, null),
|
||||
backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(ret).isFalse()
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateChallengeAsCredentialActivityResult_validChooseLock() = runTest {
|
||||
val userId = 108
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
val gkPwHandle = 6666L
|
||||
val newSensorId = 50
|
||||
val newChallenge = 60L
|
||||
setupGenerateChallenge(userId, newSensorId, newChallenge)
|
||||
whenever(
|
||||
lockPatternUtils.verifyGatekeeperPasswordHandle(
|
||||
gkPwHandle,
|
||||
newChallenge,
|
||||
userId
|
||||
)
|
||||
)
|
||||
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
|
||||
val hasCalledRemoveGkPwHandle = AtomicBoolean()
|
||||
Mockito.doAnswer {
|
||||
hasCalledRemoveGkPwHandle.set(true)
|
||||
null
|
||||
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run generateChallengeAsCredentialActivityResult()
|
||||
val intent =
|
||||
Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
|
||||
val ret = viewModel.generateChallengeAsCredentialActivityResult(
|
||||
true,
|
||||
ActivityResult(ChooseLockPattern.RESULT_FINISHED, intent),
|
||||
backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(ret).isTrue()
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
assertThat(viewModel.token).isNotNull()
|
||||
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
|
||||
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGenerateChallengeAsCredentialActivityResult_validConfirmLock() = runTest {
|
||||
val userId = 109
|
||||
viewModel =
|
||||
newAutoCredentialViewModel(newOnlySensorValidCredentialIntentExtras(userId))
|
||||
whenever(lockPatternUtils.getActivePasswordQuality(userId)).thenReturn(
|
||||
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
|
||||
)
|
||||
val gkPwHandle = 5555L
|
||||
val newSensorId = 80
|
||||
val newChallenge = 90L
|
||||
setupGenerateChallenge(userId, newSensorId, newChallenge)
|
||||
whenever(
|
||||
lockPatternUtils.verifyGatekeeperPasswordHandle(
|
||||
gkPwHandle,
|
||||
newChallenge,
|
||||
userId
|
||||
)
|
||||
)
|
||||
.thenReturn(newGoodCredential(gkPwHandle, byteArrayOf(1)))
|
||||
val hasCalledRemoveGkPwHandle = AtomicBoolean()
|
||||
Mockito.doAnswer {
|
||||
hasCalledRemoveGkPwHandle.set(true)
|
||||
null
|
||||
}.`when`(lockPatternUtils).removeGatekeeperPasswordHandle(gkPwHandle)
|
||||
|
||||
val generateFails = listOfGenerateChallengeFailedFlow()
|
||||
|
||||
// Run generateChallengeAsCredentialActivityResult()
|
||||
val intent =
|
||||
Intent().putExtra(ChooseLockSettingsHelper.EXTRA_KEY_GK_PW_HANDLE, gkPwHandle)
|
||||
val ret = viewModel.generateChallengeAsCredentialActivityResult(
|
||||
false,
|
||||
ActivityResult(Activity.RESULT_OK, intent),
|
||||
backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(ret).isTrue()
|
||||
assertThat(generateFails.size).isEqualTo(0)
|
||||
assertThat(viewModel.token).isNotNull()
|
||||
assertThat(challengeGenerator.callbackRunCount).isEqualTo(1)
|
||||
assertThat(hasCalledRemoveGkPwHandle.get()).isTrue()
|
||||
}
|
||||
|
||||
private fun TestScope.listOfGenerateChallengeFailedFlow(): List<Boolean> =
|
||||
mutableListOf<Boolean>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.generateChallengeFailedFlow.toList(it)
|
||||
}
|
||||
}
|
||||
|
||||
class TestChallengeGenerator : ChallengeGenerator {
|
||||
var sensorId = -1
|
||||
var userId = UserHandle.myUserId()
|
||||
var challenge = CredentialModel.INVALID_CHALLENGE
|
||||
var callbackRunCount = 0
|
||||
|
||||
override var callback: AutoCredentialViewModel.GenerateChallengeCallback? = null
|
||||
|
||||
override fun generateChallenge(userId: Int) {
|
||||
callback?.let {
|
||||
it.onChallengeGenerated(sensorId, this.userId, challenge)
|
||||
++callbackRunCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun newGoodCredential(gkPwHandle: Long, hat: ByteArray): VerifyCredentialResponse {
|
||||
return VerifyCredentialResponse.Builder()
|
||||
.setGatekeeperPasswordHandle(gkPwHandle)
|
||||
.setGatekeeperHAT(hat)
|
||||
.build()
|
||||
}
|
||||
|
||||
private fun newBadCredential(timeout: Int): VerifyCredentialResponse {
|
||||
return if (timeout > 0) {
|
||||
VerifyCredentialResponse.fromTimeout(timeout)
|
||||
} else {
|
||||
VerifyCredentialResponse.fromError()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.biometrics2.ui.viewmodel;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.testutils.InstantTaskExecutorRule;
|
||||
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DeviceFoldedViewModelTest {
|
||||
|
||||
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
private DeviceFoldedViewModel mViewModel;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final Application application = ApplicationProvider.getApplicationContext();
|
||||
mViewModel = new DeviceFoldedViewModel(new ScreenSizeFoldProvider(application),
|
||||
application.getMainExecutor());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLiveData() {
|
||||
final Configuration config1 = new Configuration();
|
||||
config1.smallestScreenWidthDp = 601;
|
||||
mViewModel.onConfigurationChanged(config1);
|
||||
assertThat(mViewModel.getLiveData().getValue()).isFalse();
|
||||
|
||||
final Configuration config2 = new Configuration();
|
||||
config2.smallestScreenWidthDp = 599;
|
||||
mViewModel.onConfigurationChanged(config2);
|
||||
assertThat(mViewModel.getLiveData().getValue()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.biometrics2.ui.viewmodel;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.Application;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.testutils.InstantTaskExecutorRule;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class DeviceRotationViewModelTest {
|
||||
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
private TestDeviceRotationViewModel mViewModel;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
TestDeviceRotationViewModel.sTestRotation = 3;
|
||||
mViewModel = new TestDeviceRotationViewModel(ApplicationProvider.getApplicationContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultLiveDataNotNull() {
|
||||
assertThat(mViewModel.getLiveData().getValue()).isEqualTo(mViewModel.sTestRotation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDisplayChange() {
|
||||
mViewModel.sTestRotation = 3;
|
||||
mViewModel.triggerOnDisplayChanged();
|
||||
assertThat(mViewModel.getLiveData().getValue()).isEqualTo(mViewModel.sTestRotation);
|
||||
}
|
||||
|
||||
public static class TestDeviceRotationViewModel extends DeviceRotationViewModel {
|
||||
|
||||
@Surface.Rotation static int sTestRotation = 0;
|
||||
|
||||
public TestDeviceRotationViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
void triggerOnDisplayChanged() {
|
||||
mDisplayListener.onDisplayChanged(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getRotation() {
|
||||
return sTestRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel;
|
||||
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
|
||||
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
|
||||
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.app.Application;
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||
import com.android.settings.testutils.InstantTaskExecutorRule;
|
||||
|
||||
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 java.util.ArrayList;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintEnrollEnrollingViewModelTest {
|
||||
|
||||
private static final int TEST_USER_ID = 33;
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
@Rule
|
||||
public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
@Mock
|
||||
private FingerprintManager mFingerprintManager;
|
||||
|
||||
private Application mApplication;
|
||||
private FingerprintEnrollEnrollingViewModel mViewModel;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mApplication = ApplicationProvider.getApplicationContext();
|
||||
mViewModel = new FingerprintEnrollEnrollingViewModel(
|
||||
mApplication,
|
||||
TEST_USER_ID,
|
||||
newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, 5)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIconTouchDialog() {
|
||||
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(null);
|
||||
|
||||
mViewModel.showIconTouchDialog();
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tesBackPressedScenario() {
|
||||
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(null);
|
||||
assertThat(mViewModel.getOnBackPressed()).isEqualTo(false);
|
||||
|
||||
mViewModel.setOnBackPressed();
|
||||
assertThat(mViewModel.getOnBackPressed()).isEqualTo(true);
|
||||
|
||||
mViewModel.onCancelledDueToOnBackPressed();
|
||||
assertThat(mViewModel.getOnBackPressed()).isEqualTo(false);
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSkipPressedScenario() {
|
||||
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(null);
|
||||
assertThat(mViewModel.getOnSkipPressed()).isEqualTo(false);
|
||||
|
||||
mViewModel.setOnSkipPressed();
|
||||
assertThat(mViewModel.getOnSkipPressed()).isEqualTo(true);
|
||||
|
||||
mViewModel.onCancelledDueToOnSkipPressed();
|
||||
assertThat(mViewModel.getOnSkipPressed()).isEqualTo(false);
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstFingerprintSensorPropertiesInternal() {
|
||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
final FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
|
||||
0 /* sensorId */,
|
||||
SensorProperties.STRENGTH_STRONG,
|
||||
5,
|
||||
new ArrayList<>() /* componentInfo */,
|
||||
TYPE_UDFPS_OPTICAL,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */);
|
||||
props.add(prop);
|
||||
doAnswer(invocation -> {
|
||||
final IFingerprintAuthenticatorsRegisteredCallback callback =
|
||||
invocation.getArgument(0);
|
||||
callback.onAllAuthenticatorsRegistered(props);
|
||||
return null;
|
||||
}).when(mFingerprintManager).addAuthenticatorsRegisteredCallback(any());
|
||||
|
||||
mViewModel = new FingerprintEnrollEnrollingViewModel(
|
||||
mApplication,
|
||||
TEST_USER_ID,
|
||||
new FingerprintRepository(mFingerprintManager)
|
||||
);
|
||||
|
||||
assertThat(mViewModel.getFirstFingerprintSensorPropertiesInternal()).isEqualTo(prop);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnrollStageCount() {
|
||||
final int expectedValue = 24;
|
||||
doReturn(expectedValue).when(mFingerprintManager).getEnrollStageCount();
|
||||
|
||||
assertThat(mViewModel.getEnrollStageCount()).isEqualTo(expectedValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnrollStageThreshold() {
|
||||
final float expectedValue0 = 0.42f;
|
||||
final float expectedValue1 = 0.24f;
|
||||
final float expectedValue2 = 0.33f;
|
||||
final float expectedValue3 = 0.90f;
|
||||
|
||||
doReturn(expectedValue0).when(mFingerprintManager).getEnrollStageThreshold(0);
|
||||
doReturn(expectedValue1).when(mFingerprintManager).getEnrollStageThreshold(1);
|
||||
doReturn(expectedValue2).when(mFingerprintManager).getEnrollStageThreshold(2);
|
||||
doReturn(expectedValue3).when(mFingerprintManager).getEnrollStageThreshold(3);
|
||||
|
||||
assertThat(mViewModel.getEnrollStageThreshold(2)).isEqualTo(expectedValue2);
|
||||
assertThat(mViewModel.getEnrollStageThreshold(1)).isEqualTo(expectedValue1);
|
||||
assertThat(mViewModel.getEnrollStageThreshold(3)).isEqualTo(expectedValue3);
|
||||
assertThat(mViewModel.getEnrollStageThreshold(0)).isEqualTo(expectedValue0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintErrorDialogSetResultAction.FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FingerprintEnrollErrorDialogViewModelTest {
|
||||
|
||||
private val application = ApplicationProvider.getApplicationContext<Application>()
|
||||
private var viewModel: FingerprintEnrollErrorDialogViewModel =
|
||||
FingerprintEnrollErrorDialogViewModel(application, false)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// Make sure viewModel is new for each test
|
||||
viewModel = FingerprintEnrollErrorDialogViewModel(application, false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsDialogNotShownDefaultFalse() {
|
||||
assertThat(viewModel.isDialogShown).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsSuw() {
|
||||
assertThat(FingerprintEnrollErrorDialogViewModel(application, false).isSuw).isFalse()
|
||||
assertThat(FingerprintEnrollErrorDialogViewModel(application, true).isSuw).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewDialog() = runTest {
|
||||
val newDialogs: List<Int> = mutableListOf<Int>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.newDialogFlow.toList(it)
|
||||
}
|
||||
}
|
||||
|
||||
runCurrent()
|
||||
|
||||
// Default values
|
||||
assertThat(viewModel.isDialogShown).isFalse()
|
||||
assertThat(newDialogs.size).isEqualTo(0)
|
||||
|
||||
val testErrorMsgId = 3456
|
||||
viewModel.newDialog(testErrorMsgId)
|
||||
runCurrent()
|
||||
|
||||
// verify after emit
|
||||
assertThat(viewModel.isDialogShown).isTrue()
|
||||
assertThat(newDialogs.size).isEqualTo(1)
|
||||
assertThat(newDialogs[0]).isEqualTo(testErrorMsgId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testTriggerRetry() = runTest {
|
||||
val triggerRetries: List<Any> = mutableListOf<Any>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.triggerRetryFlow.toList(it)
|
||||
}
|
||||
}
|
||||
|
||||
runCurrent()
|
||||
|
||||
// Default values
|
||||
assertThat(triggerRetries.size).isEqualTo(0)
|
||||
|
||||
viewModel.triggerRetry()
|
||||
runCurrent()
|
||||
|
||||
// verify after emit
|
||||
assertThat(triggerRetries.size).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetResultFinish() = runTest {
|
||||
val setResults: List<FingerprintErrorDialogSetResultAction> =
|
||||
mutableListOf<FingerprintErrorDialogSetResultAction>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.setResultFlow.toList(it)
|
||||
}
|
||||
}
|
||||
|
||||
runCurrent()
|
||||
|
||||
// Default values
|
||||
assertThat(setResults.size).isEqualTo(0)
|
||||
|
||||
viewModel.setResultAndFinish(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
|
||||
runCurrent()
|
||||
|
||||
// verify after emit
|
||||
assertThat(setResults.size).isEqualTo(1)
|
||||
assertThat(setResults[0]).isEqualTo(FINGERPRINT_ERROR_DIALOG_ACTION_SET_RESULT_FINISH)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel;
|
||||
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG;
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP;
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFindSensorViewModel.FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.testutils.InstantTaskExecutorRule;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintEnrollFindSensorViewModelTest {
|
||||
|
||||
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
private Application mApplication;
|
||||
private FingerprintEnrollFindSensorViewModel mViewModel;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mApplication = ApplicationProvider.getApplicationContext();
|
||||
mViewModel = new FingerprintEnrollFindSensorViewModel(mApplication, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickSkipButtonNotInSuw() {
|
||||
mViewModel = new FingerprintEnrollFindSensorViewModel(mApplication, false);
|
||||
mViewModel.onSkipButtonClick();
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickSkipButtonInSuw() {
|
||||
mViewModel = new FingerprintEnrollFindSensorViewModel(mApplication, true);
|
||||
mViewModel.onSkipButtonClick();
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_DIALOG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickSkipDialogButton() {
|
||||
mViewModel.onSkipDialogButtonClick();
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_SKIP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickStartDialogButton() {
|
||||
mViewModel.onStartButtonClick();
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_FIND_SENSOR_ACTION_START);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearActionLiveData() {
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isNull();
|
||||
|
||||
mViewModel.onStartButtonClick();
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isNotNull();
|
||||
|
||||
mViewModel.clearActionLiveData();
|
||||
assertThat(mViewModel.getActionLiveData().getValue()).isNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel;
|
||||
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
|
||||
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK;
|
||||
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollFinishViewModel.FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK;
|
||||
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
|
||||
import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
|
||||
import com.android.settings.testutils.InstantTaskExecutorRule;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintEnrollFinishViewModelTest {
|
||||
|
||||
private static final int USER_ID = 334;
|
||||
private static final int MAX_ENROLLABLE = 5;
|
||||
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
@Mock private FingerprintManager mFingerprintManager;
|
||||
|
||||
private Application mApplication;
|
||||
private EnrollmentRequest mRequest;
|
||||
private FingerprintEnrollFinishViewModel mViewModel;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mApplication = ApplicationProvider.getApplicationContext();
|
||||
mRequest = new EnrollmentRequest(new Intent(), mApplication, true);
|
||||
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
|
||||
newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, MAX_ENROLLABLE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanAssumeSfps() {
|
||||
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
|
||||
newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL, MAX_ENROLLABLE));
|
||||
assertThat(mViewModel.canAssumeSfps()).isFalse();
|
||||
|
||||
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
|
||||
newFingerprintRepository(mFingerprintManager, TYPE_REAR, MAX_ENROLLABLE));
|
||||
assertThat(mViewModel.canAssumeSfps()).isFalse();
|
||||
|
||||
mViewModel = new FingerprintEnrollFinishViewModel(mApplication, USER_ID, mRequest,
|
||||
newFingerprintRepository(mFingerprintManager, TYPE_POWER_BUTTON, MAX_ENROLLABLE));
|
||||
assertThat(mViewModel.canAssumeSfps()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAnotherFingerprintEnrollable() {
|
||||
setupFingerprintEnrolledFingerprints(mFingerprintManager, USER_ID, MAX_ENROLLABLE);
|
||||
assertThat(mViewModel.isAnotherFingerprintEnrollable()).isFalse();
|
||||
|
||||
setupFingerprintEnrolledFingerprints(mFingerprintManager, USER_ID, MAX_ENROLLABLE - 1);
|
||||
assertThat(mViewModel.isAnotherFingerprintEnrollable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRequest() {
|
||||
assertThat(mViewModel.getRequest()).isEqualTo(mRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnAddButtonClick() {
|
||||
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
|
||||
|
||||
// Test init value
|
||||
assertThat(actionLiveData.getValue()).isNull();
|
||||
|
||||
// Test onAddButtonClick()
|
||||
mViewModel.onAddButtonClick();
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_FINISH_ACTION_ADD_BUTTON_CLICK);
|
||||
|
||||
// Clear
|
||||
mViewModel.clearActionLiveData();
|
||||
assertThat(actionLiveData.getValue()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnNextButtonClick() {
|
||||
final LiveData<Integer> actionLiveData = mViewModel.getActionLiveData();
|
||||
|
||||
// Test init value
|
||||
assertThat(actionLiveData.getValue()).isNull();
|
||||
|
||||
// Test onNextButtonClick()
|
||||
mViewModel.onNextButtonClick();
|
||||
assertThat(actionLiveData.getValue()).isEqualTo(
|
||||
FINGERPRINT_ENROLL_FINISH_ACTION_NEXT_BUTTON_CLICK);
|
||||
|
||||
// Clear
|
||||
mViewModel.clearActionLiveData();
|
||||
assertThat(actionLiveData.getValue()).isNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Resources
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentRequest
|
||||
import com.android.settings.biometrics2.ui.model.FingerprintEnrollIntroStatus
|
||||
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
|
||||
import com.android.settings.biometrics2.ui.model.FingerprintEnrollable.FINGERPRINT_ENROLLABLE_OK
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroAction.CONTINUE_ENROLL
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroAction.DONE_AND_FINISH
|
||||
import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollIntroAction.SKIP_OR_CANCEL
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwDeferredRequest
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwPortalRequest
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwRequest
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwSuggestedActionFlowRequest
|
||||
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository
|
||||
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints
|
||||
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupSuwMaxFingerprintsEnrollable
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
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
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FingerprintEnrollIntroViewModelTest {
|
||||
|
||||
@get:Rule val mockito = MockitoJUnit.rule()
|
||||
|
||||
@Mock private lateinit var resources: Resources
|
||||
@Mock private lateinit var fingerprintManager: FingerprintManager
|
||||
|
||||
private var application: Application = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private fun newFingerprintEnrollIntroViewModel(
|
||||
fingerprintRepository: FingerprintRepository,
|
||||
enrollmentRequest: EnrollmentRequest
|
||||
) = FingerprintEnrollIntroViewModel(
|
||||
application,
|
||||
fingerprintRepository,
|
||||
enrollmentRequest,
|
||||
TEST_USER_ID
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
application = ApplicationProvider.getApplicationContext()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPageStatusFlowDefaultAndUpdate() = runTest {
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 1),
|
||||
newAllFalseRequest(application)
|
||||
)
|
||||
|
||||
val statusList = listOfPageStatusFlow(viewModel)
|
||||
|
||||
runCurrent()
|
||||
|
||||
// assert default values
|
||||
assertThat(statusList.size).isEqualTo(1)
|
||||
assertThat(statusList[0].hasScrollToBottom()).isFalse()
|
||||
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_OK)
|
||||
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 1)
|
||||
viewModel.updateEnrollableStatus(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
// assert new updated value
|
||||
assertThat(statusList.size).isEqualTo(2)
|
||||
assertThat(statusList[1].hasScrollToBottom()).isFalse()
|
||||
assertThat(statusList[1].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
|
||||
}
|
||||
|
||||
fun testOnStartToUpdateEnrollableStatusOk_isSuw() = runTest {
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 0)
|
||||
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
newIsSuwRequest(application)
|
||||
)
|
||||
|
||||
val statusList = listOfPageStatusFlow(viewModel)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertThat(statusList.size).isEqualTo(1)
|
||||
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_OK)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusReachMax_isSuw() = runTest {
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 1)
|
||||
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
newIsSuwRequest(application)
|
||||
)
|
||||
|
||||
val statusList = listOfPageStatusFlow(viewModel)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertThat(statusList.size).isEqualTo(1)
|
||||
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusOk_isNotSuw() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusOk(newAllFalseRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusReachMax_isNotSuw() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(newAllFalseRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusOk_isSuwDeferred() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusOk(newIsSuwDeferredRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusReachMax_isSuwDeferred() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(newIsSuwDeferredRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusOk_isSuwPortal() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusOk(newIsSuwPortalRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusReachMax_isSuwPortal() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(newIsSuwPortalRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusOk_isSuwSuggestedActionFlow() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusOk(newIsSuwSuggestedActionFlowRequest(application))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnStartToUpdateEnrollableStatusReachMax_isSuwSuggestedActionFlow() = runTest {
|
||||
testOnStartToUpdateEnrollableStatusReachMax(
|
||||
newIsSuwSuggestedActionFlowRequest(application)
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestScope.testOnStartToUpdateEnrollableStatusOk(request: EnrollmentRequest) {
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 0)
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
request
|
||||
)
|
||||
|
||||
val statusList = listOfPageStatusFlow(viewModel)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertThat(statusList.size).isEqualTo(1)
|
||||
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_OK)
|
||||
}
|
||||
|
||||
private fun TestScope.testOnStartToUpdateEnrollableStatusReachMax(request: EnrollmentRequest) {
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 5)
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
request
|
||||
)
|
||||
|
||||
val statusList = listOfPageStatusFlow(viewModel)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertThat(statusList.size).isEqualTo(1)
|
||||
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsParentalConsentRequired() {
|
||||
// We shall not mock FingerprintRepository, but
|
||||
// FingerprintRepository.isParentalConsentRequired() calls static method inside, we can't
|
||||
// mock static method
|
||||
val fingerprintRepository = Mockito.mock(
|
||||
FingerprintRepository::class.java
|
||||
)
|
||||
val viewModel = FingerprintEnrollIntroViewModel(
|
||||
application,
|
||||
fingerprintRepository,
|
||||
newAllFalseRequest(application),
|
||||
TEST_USER_ID
|
||||
)
|
||||
Mockito.`when`(
|
||||
fingerprintRepository.isParentalConsentRequired(application)
|
||||
).thenReturn(true)
|
||||
assertThat(viewModel.isParentalConsentRequired).isEqualTo(true)
|
||||
Mockito.`when`(
|
||||
fingerprintRepository.isParentalConsentRequired(application)
|
||||
).thenReturn(false)
|
||||
assertThat(viewModel.isParentalConsentRequired).isEqualTo(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsBiometricUnlockDisabledByAdmin() {
|
||||
// We shall not mock FingerprintRepository, but
|
||||
// FingerprintRepository.isDisabledByAdmin() calls static method inside, we can't mock
|
||||
// static method
|
||||
val fingerprintRepository = Mockito.mock(FingerprintRepository::class.java)
|
||||
val viewModel = FingerprintEnrollIntroViewModel(
|
||||
application,
|
||||
fingerprintRepository,
|
||||
newAllFalseRequest(application),
|
||||
TEST_USER_ID
|
||||
)
|
||||
Mockito.`when`(
|
||||
fingerprintRepository.isDisabledByAdmin(application, TEST_USER_ID)
|
||||
).thenReturn(true)
|
||||
assertThat(viewModel.isBiometricUnlockDisabledByAdmin).isEqualTo(true)
|
||||
Mockito.`when`(
|
||||
fingerprintRepository.isDisabledByAdmin(application, TEST_USER_ID)
|
||||
).thenReturn(false)
|
||||
assertThat(viewModel.isBiometricUnlockDisabledByAdmin).isEqualTo(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetHasScrolledToBottom() = runTest {
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
newAllFalseRequest(application)
|
||||
)
|
||||
|
||||
val pageStatusList = listOfPageStatusFlow(viewModel)
|
||||
|
||||
viewModel.setHasScrolledToBottom(true, backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
assertThat(pageStatusList[pageStatusList.size-1].hasScrollToBottom()).isEqualTo(true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnNextButtonClick_enrollNext() = runTest {
|
||||
// Set latest status to FINGERPRINT_ENROLLABLE_OK
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 0)
|
||||
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
newIsSuwRequest(application)
|
||||
)
|
||||
|
||||
val actions = listOfActionFlow(viewModel)
|
||||
|
||||
// Perform click on `next`
|
||||
viewModel.onNextButtonClick(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
assertThat(actions.size).isEqualTo(1)
|
||||
assertThat(actions[0]).isEqualTo(CONTINUE_ENROLL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnNextButtonClick_doneAndFinish() = runTest {
|
||||
// Set latest status to FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, TEST_USER_ID, 1)
|
||||
setupSuwMaxFingerprintsEnrollable(application, resources, 1)
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
newIsSuwRequest(application)
|
||||
)
|
||||
|
||||
val statusList = listOfPageStatusFlow(viewModel)
|
||||
val actionList = listOfActionFlow(viewModel)
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertThat(statusList.size).isEqualTo(1)
|
||||
assertThat(statusList[0].enrollableStatus).isEqualTo(FINGERPRINT_ENROLLABLE_ERROR_REACH_MAX)
|
||||
|
||||
val actions = listOfActionFlow(viewModel)
|
||||
|
||||
// Perform click on `next`
|
||||
viewModel.onNextButtonClick(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
assertThat(actionList.size).isEqualTo(1)
|
||||
assertThat(actionList[0]).isEqualTo(DONE_AND_FINISH)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnSkipOrCancelButtonClick() = runTest {
|
||||
val viewModel = newFingerprintEnrollIntroViewModel(
|
||||
newFingerprintRepository(fingerprintManager, TYPE_UDFPS_OPTICAL, 5),
|
||||
newAllFalseRequest(application)
|
||||
)
|
||||
|
||||
val actions = listOfActionFlow(viewModel)
|
||||
|
||||
viewModel.onSkipOrCancelButtonClick(backgroundScope)
|
||||
runCurrent()
|
||||
|
||||
assertThat(actions.size).isEqualTo(1)
|
||||
assertThat(actions[0]).isEqualTo(SKIP_OR_CANCEL)
|
||||
}
|
||||
|
||||
private fun TestScope.listOfActionFlow(
|
||||
viewModel: FingerprintEnrollIntroViewModel
|
||||
): List<FingerprintEnrollIntroAction> =
|
||||
mutableListOf<FingerprintEnrollIntroAction>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.actionFlow.toList(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TestScope.listOfPageStatusFlow(
|
||||
viewModel: FingerprintEnrollIntroViewModel
|
||||
): List<FingerprintEnrollIntroStatus> =
|
||||
mutableListOf<FingerprintEnrollIntroStatus>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.pageStatusFlow.toList(it)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TEST_USER_ID = 33
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel;
|
||||
|
||||
import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL;
|
||||
import static android.hardware.fingerprint.FingerprintManager.ENROLL_FIND_SENSOR;
|
||||
import static android.hardware.fingerprint.FingerprintManager.EnrollReason;
|
||||
import static android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
|
||||
|
||||
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
||||
|
||||
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.doAnswer;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.only;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.res.Resources;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.biometrics.fingerprint.FingerprintUpdater;
|
||||
import com.android.settings.biometrics.fingerprint.MessageDisplayController;
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentProgress;
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentStatusMessage;
|
||||
import com.android.settings.testutils.InstantTaskExecutorRule;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FingerprintEnrollProgressViewModelTest {
|
||||
|
||||
private static final int TEST_USER_ID = 334;
|
||||
|
||||
@Rule public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
@Rule public final InstantTaskExecutorRule mTaskExecutorRule = new InstantTaskExecutorRule();
|
||||
|
||||
@Mock private Application mApplication;
|
||||
@Mock private Resources mResources;
|
||||
@Mock private FingerprintUpdater mFingerprintUpdater;
|
||||
|
||||
private FingerprintEnrollProgressViewModel mViewModel;
|
||||
private final TestWrapper<CancellationSignal> mCancellationSignalWrapper = new TestWrapper<>();
|
||||
private final TestWrapper<EnrollmentCallback> mCallbackWrapper = new TestWrapper<>();
|
||||
private int mEnrollmentMessageDisplayControllerFlagResId;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mEnrollmentMessageDisplayControllerFlagResId = ApplicationProvider.getApplicationContext()
|
||||
.getResources().getIdentifier("enrollment_message_display_controller_flag", "bool",
|
||||
SETTINGS_PACKAGE_NAME);
|
||||
|
||||
when(mApplication.getResources()).thenReturn(mResources);
|
||||
|
||||
// Not use MessageDisplayController by default
|
||||
when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(false);
|
||||
mViewModel = new FingerprintEnrollProgressViewModel(mApplication, mFingerprintUpdater,
|
||||
TEST_USER_ID);
|
||||
|
||||
mCancellationSignalWrapper.mValue = null;
|
||||
mCallbackWrapper.mValue = null;
|
||||
doAnswer(invocation -> {
|
||||
mCancellationSignalWrapper.mValue = invocation.getArgument(1);
|
||||
mCallbackWrapper.mValue = invocation.getArgument(3);
|
||||
return null;
|
||||
}).when(mFingerprintUpdater).enroll(any(byte[].class), any(CancellationSignal.class),
|
||||
eq(TEST_USER_ID), any(EnrollmentCallback.class), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartFindSensor() {
|
||||
@EnrollReason final int enrollReason = ENROLL_FIND_SENSOR;
|
||||
final byte[] token = new byte[] { 1, 2, 3 };
|
||||
mViewModel.setToken(token);
|
||||
|
||||
// Start enrollment
|
||||
final Object ret = mViewModel.startEnrollment(enrollReason);
|
||||
|
||||
assertThat(ret).isNotNull();
|
||||
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
|
||||
eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any());
|
||||
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEnrolling() {
|
||||
@EnrollReason final int enrollReason = ENROLL_ENROLL;
|
||||
final byte[] token = new byte[] { 1, 2, 3 };
|
||||
mViewModel.setToken(token);
|
||||
|
||||
// Start enrollment
|
||||
final Object ret = mViewModel.startEnrollment(enrollReason);
|
||||
|
||||
assertThat(ret).isNotNull();
|
||||
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
|
||||
eq(TEST_USER_ID), any(EnrollmentCallback.class), eq(enrollReason), any());
|
||||
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEnrollingWithMessageDisplayController() {
|
||||
// Enable MessageDisplayController and mock handler for it
|
||||
when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(true);
|
||||
when(mApplication.getMainThreadHandler()).thenReturn(new TestHandler());
|
||||
|
||||
@EnrollReason final int enrollReason = ENROLL_ENROLL;
|
||||
final byte[] token = new byte[] { 1, 2, 3 };
|
||||
mViewModel.setToken(token);
|
||||
|
||||
// Start enrollment
|
||||
final Object ret = mViewModel.startEnrollment(enrollReason);
|
||||
|
||||
assertThat(ret).isNotNull();
|
||||
verify(mFingerprintUpdater, only()).enroll(eq(token), any(CancellationSignal.class),
|
||||
eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any());
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
assertThat(mCallbackWrapper.mValue instanceof MessageDisplayController).isTrue();
|
||||
final EnrollmentCallback callback1 = mCallbackWrapper.mValue;
|
||||
|
||||
// Cancel and start again
|
||||
mViewModel.cancelEnrollment();
|
||||
mViewModel.startEnrollment(enrollReason);
|
||||
|
||||
// Shall not use the same MessageDisplayController
|
||||
verify(mFingerprintUpdater, times(2)).enroll(eq(token), any(CancellationSignal.class),
|
||||
eq(TEST_USER_ID), any(MessageDisplayController.class), eq(enrollReason), any());
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
assertThat(callback1).isNotEqualTo(mCallbackWrapper.mValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartEnrollmentFailBecauseOfNoToken() {
|
||||
// Start enrollment
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_FIND_SENSOR);
|
||||
|
||||
assertThat(ret).isNull();
|
||||
verify(mFingerprintUpdater, never()).enroll(any(byte[].class),
|
||||
any(CancellationSignal.class), anyInt(), any(EnrollmentCallback.class), anyInt(),
|
||||
any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelEnrollment() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCancellationSignalWrapper.mValue).isNotNull();
|
||||
|
||||
// Cancel enrollment
|
||||
mViewModel.cancelEnrollment();
|
||||
|
||||
assertThat(mCancellationSignalWrapper.mValue.isCanceled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgressUpdate() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Test default value
|
||||
final LiveData<EnrollmentProgress> progressLiveData = mViewModel.getProgressLiveData();
|
||||
EnrollmentProgress progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(-1);
|
||||
assertThat(progress.getRemaining()).isEqualTo(0);
|
||||
|
||||
// Update first progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(25);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(25);
|
||||
|
||||
// Update second progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(20);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(20);
|
||||
|
||||
// Update latest progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(0);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgressUpdateClearHelpMessage() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
final LiveData<EnrollmentProgress> progressLiveData = mViewModel.getProgressLiveData();
|
||||
final LiveData<EnrollmentStatusMessage> helpMsgLiveData =
|
||||
mViewModel.getHelpMessageLiveData();
|
||||
|
||||
// Update first progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(25);
|
||||
EnrollmentProgress progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(25);
|
||||
|
||||
// Update help message
|
||||
final int testHelpMsgId = 3;
|
||||
final String testHelpString = "Test Help String";
|
||||
mCallbackWrapper.mValue.onEnrollmentHelp(testHelpMsgId, testHelpString);
|
||||
final EnrollmentStatusMessage helpMsg = helpMsgLiveData.getValue();
|
||||
assertThat(helpMsg).isNotNull();
|
||||
assertThat(helpMsg.getMsgId()).isEqualTo(testHelpMsgId);
|
||||
assertThat(helpMsg.getStr().toString()).isEqualTo(testHelpString);
|
||||
|
||||
// Update second progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(20);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(20);
|
||||
|
||||
// Help message shall be set to null
|
||||
assertThat(helpMsgLiveData.getValue()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProgressUpdateWithMessageDisplayController() {
|
||||
// Enable MessageDisplayController and mock handler for it
|
||||
when(mResources.getBoolean(mEnrollmentMessageDisplayControllerFlagResId)).thenReturn(true);
|
||||
when(mApplication.getMainThreadHandler()).thenReturn(new TestHandler());
|
||||
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
|
||||
// Start enrollment
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Test default value
|
||||
final LiveData<EnrollmentProgress> progressLiveData = mViewModel.getProgressLiveData();
|
||||
EnrollmentProgress progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(-1);
|
||||
assertThat(progress.getRemaining()).isEqualTo(0);
|
||||
|
||||
// Update first progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(25);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(25);
|
||||
|
||||
// Update second progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(20);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(20);
|
||||
|
||||
// Update latest progress
|
||||
mCallbackWrapper.mValue.onEnrollmentProgress(0);
|
||||
progress = progressLiveData.getValue();
|
||||
assertThat(progress).isNotNull();
|
||||
assertThat(progress.getSteps()).isEqualTo(25);
|
||||
assertThat(progress.getRemaining()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetErrorMessageLiveData() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Check default value
|
||||
final LiveData<EnrollmentStatusMessage> liveData = mViewModel.getErrorMessageLiveData();
|
||||
assertThat(liveData.getValue()).isNull();
|
||||
|
||||
// Notify error message
|
||||
final int errMsgId = 3;
|
||||
final String errMsg = "test error message";
|
||||
mCallbackWrapper.mValue.onEnrollmentError(errMsgId, errMsg);
|
||||
final EnrollmentStatusMessage value = liveData.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(value.getMsgId()).isEqualTo(errMsgId);
|
||||
assertThat(value.getStr().toString()).isEqualTo(errMsg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHelpMessageLiveData() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Check default value
|
||||
final LiveData<EnrollmentStatusMessage> liveData = mViewModel.getHelpMessageLiveData();
|
||||
assertThat(liveData.getValue()).isNull();
|
||||
|
||||
// Notify help message
|
||||
final int errMsgId = 3;
|
||||
final String errMsg = "test error message";
|
||||
mCallbackWrapper.mValue.onEnrollmentHelp(errMsgId, errMsg);
|
||||
final EnrollmentStatusMessage value = liveData.getValue();
|
||||
assertThat(value).isNotNull();
|
||||
assertThat(value.getMsgId()).isEqualTo(errMsgId);
|
||||
assertThat(value.getStr().toString()).isEqualTo(errMsg);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAcquireLiveData() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Check default value
|
||||
final LiveData<Boolean> liveData = mViewModel.getAcquireLiveData();
|
||||
assertThat(liveData.getValue()).isNull();
|
||||
|
||||
// Notify acquire message
|
||||
mCallbackWrapper.mValue.onAcquired(true);
|
||||
assertThat(liveData.getValue()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPointerDownLiveData() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Check default value
|
||||
final LiveData<Integer> liveData = mViewModel.getPointerDownLiveData();
|
||||
assertThat(liveData.getValue()).isNull();
|
||||
|
||||
// Notify acquire message
|
||||
final int value = 33;
|
||||
mCallbackWrapper.mValue.onUdfpsPointerDown(value);
|
||||
assertThat(liveData.getValue()).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPointerUpLiveData() {
|
||||
// Start enrollment
|
||||
mViewModel.setToken(new byte[] { 1, 2, 3 });
|
||||
final Object ret = mViewModel.startEnrollment(ENROLL_ENROLL);
|
||||
assertThat(ret).isNotNull();
|
||||
assertThat(mCallbackWrapper.mValue).isNotNull();
|
||||
|
||||
// Check default value
|
||||
final LiveData<Integer> liveData = mViewModel.getPointerUpLiveData();
|
||||
assertThat(liveData.getValue()).isNull();
|
||||
|
||||
// Notify acquire message
|
||||
final int value = 44;
|
||||
mCallbackWrapper.mValue.onUdfpsPointerUp(value);
|
||||
assertThat(liveData.getValue()).isEqualTo(value);
|
||||
}
|
||||
|
||||
private static class TestWrapper<T> {
|
||||
T mValue;
|
||||
}
|
||||
|
||||
private static class TestHandler extends Handler {
|
||||
|
||||
TestHandler() {
|
||||
super(Looper.getMainLooper());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
|
||||
msg.getCallback().run();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.ui.viewmodel
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.FingerprintSensorProperties
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.biometrics.BiometricEnrollBase
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest
|
||||
import com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newIsSuwRequest
|
||||
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository
|
||||
import com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
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.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class FingerprintEnrollmentViewModelTest {
|
||||
|
||||
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
private val application: Application
|
||||
get() = ApplicationProvider.getApplicationContext()
|
||||
|
||||
@Mock
|
||||
private lateinit var fingerprintManager: FingerprintManager
|
||||
|
||||
private lateinit var fingerprintRepository: FingerprintRepository
|
||||
private lateinit var viewModel: FingerprintEnrollmentViewModel
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
fingerprintRepository = newFingerprintRepository(
|
||||
fingerprintManager,
|
||||
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
|
||||
5
|
||||
)
|
||||
viewModel = FingerprintEnrollmentViewModel(
|
||||
application,
|
||||
fingerprintRepository,
|
||||
newAllFalseRequest(application)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetRequest() {
|
||||
assertThat(viewModel.request).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsWaitingActivityResultDefaultFalse() {
|
||||
assertThat(viewModel.isWaitingActivityResult.value).isFalse()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testOverrideActivityResult_shallKeepNullIntent_woChallengeExtra() {
|
||||
val retResult = viewModel.getOverrideActivityResult(
|
||||
ActivityResult(22, null), null
|
||||
)
|
||||
assertThat(retResult).isNotNull()
|
||||
assertThat(retResult.data).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOverrideActivityResult_shallKeepNullIntent_noIntent_woChallengeExtra() {
|
||||
val intent = Intent()
|
||||
val retResult = viewModel.getOverrideActivityResult(
|
||||
ActivityResult(33, intent), null
|
||||
)
|
||||
assertThat(retResult).isNotNull()
|
||||
assertThat(retResult.data).isEqualTo(intent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOverrideActivityResult_shallKeepNull_woAdded_woIntent_withChallenge() {
|
||||
val extra = Bundle()
|
||||
extra.putString("test1", "test123")
|
||||
|
||||
val retResult = viewModel.getOverrideActivityResult(
|
||||
ActivityResult(33, null), extra
|
||||
)
|
||||
|
||||
assertThat(retResult).isNotNull()
|
||||
assertThat(retResult.data).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOverrideActivityResult_shallCreateNew_woIntent_withChallenge() {
|
||||
val key1 = "test1"
|
||||
val key2 = "test2"
|
||||
val extra = Bundle().apply {
|
||||
putString(key1, "test123")
|
||||
putInt(key2, 9999)
|
||||
}
|
||||
|
||||
viewModel.isNewFingerprintAdded = true
|
||||
|
||||
val retResult = viewModel.getOverrideActivityResult(
|
||||
ActivityResult(33, null), extra
|
||||
)
|
||||
assertThat(retResult).isNotNull()
|
||||
|
||||
val retIntent = retResult.data
|
||||
assertThat(retIntent).isNotNull()
|
||||
|
||||
val retExtra = retIntent!!.extras
|
||||
assertThat(retExtra).isNotNull()
|
||||
assertThat(retExtra!!.size).isEqualTo(extra.size)
|
||||
assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
|
||||
assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOverrideActivityResult_shallNotMerge_nonAdded_woIntent_withChallenge() {
|
||||
val extra = Bundle().apply {
|
||||
putString("test2", "test123")
|
||||
}
|
||||
|
||||
val key2 = "test2"
|
||||
val intent = Intent().apply {
|
||||
putExtra(key2, 3456L)
|
||||
}
|
||||
|
||||
val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra)
|
||||
|
||||
assertThat(retResult).isNotNull()
|
||||
|
||||
val retIntent = retResult.data
|
||||
assertThat(retIntent).isNotNull()
|
||||
|
||||
val retExtra = retIntent!!.extras
|
||||
assertThat(retExtra).isNotNull()
|
||||
assertThat(retExtra!!.size).isEqualTo(intent.extras!!.size)
|
||||
assertThat(retExtra.getString(key2)).isEqualTo(intent.extras!!.getString(key2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOverrideActivityResult_shallMerge_added_woIntent_withChallenge() {
|
||||
val key1 = "test1"
|
||||
val key2 = "test2"
|
||||
val extra = Bundle().apply {
|
||||
putString(key1, "test123")
|
||||
putInt(key2, 9999)
|
||||
}
|
||||
|
||||
val key3 = "test3"
|
||||
val intent = Intent().apply {
|
||||
putExtra(key3, 3456L)
|
||||
}
|
||||
|
||||
viewModel.isNewFingerprintAdded = true
|
||||
|
||||
val retResult = viewModel.getOverrideActivityResult(ActivityResult(33, intent), extra)
|
||||
assertThat(retResult).isNotNull()
|
||||
|
||||
val retIntent = retResult.data
|
||||
assertThat(retIntent).isNotNull()
|
||||
|
||||
val retExtra = retIntent!!.extras
|
||||
assertThat(retExtra).isNotNull()
|
||||
assertThat(retExtra!!.size).isEqualTo(extra.size + intent.extras!!.size)
|
||||
assertThat(retExtra.getString(key1)).isEqualTo(extra.getString(key1))
|
||||
assertThat(retExtra.getInt(key2)).isEqualTo(extra.getInt(key2))
|
||||
assertThat(retExtra.getLong(key3)).isEqualTo(intent.extras!!.getLong(key3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIsMaxEnrolledReached() {
|
||||
val uid = 100
|
||||
fingerprintRepository = newFingerprintRepository(
|
||||
fingerprintManager,
|
||||
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
|
||||
3
|
||||
)
|
||||
viewModel = FingerprintEnrollmentViewModel(
|
||||
application,
|
||||
fingerprintRepository,
|
||||
newAllFalseRequest(application)
|
||||
)
|
||||
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 0)
|
||||
assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
|
||||
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 1)
|
||||
assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
|
||||
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 2)
|
||||
assertThat(viewModel.isMaxEnrolledReached(uid)).isFalse()
|
||||
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 3)
|
||||
assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
|
||||
|
||||
setupFingerprintEnrolledFingerprints(fingerprintManager, uid, 4)
|
||||
assertThat(viewModel.isMaxEnrolledReached(uid)).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetResultFlow_defaultEmpty() = runTest {
|
||||
val activityResults = listOfSetResultFlow()
|
||||
|
||||
runCurrent()
|
||||
|
||||
assertThat(activityResults.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckFinishActivityDuringOnPause_doNothingIfIsSuw() = runTest {
|
||||
viewModel = FingerprintEnrollmentViewModel(
|
||||
application,
|
||||
fingerprintRepository,
|
||||
newIsSuwRequest(application)
|
||||
)
|
||||
|
||||
val activityResults = listOfSetResultFlow()
|
||||
|
||||
viewModel.checkFinishActivityDuringOnPause(
|
||||
isActivityFinishing = false,
|
||||
isChangingConfigurations = false,
|
||||
scope = this
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(activityResults.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckFinishActivityDuringOnPause_doNothingIfIsWaitingActivity() = runTest {
|
||||
val activityResults = listOfSetResultFlow()
|
||||
|
||||
viewModel.isWaitingActivityResult.value = true
|
||||
viewModel.checkFinishActivityDuringOnPause(
|
||||
isActivityFinishing = false,
|
||||
isChangingConfigurations = false,
|
||||
scope = this
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(activityResults.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckFinishActivityDuringOnPause_doNothingIfIsActivityFinishing() = runTest {
|
||||
val activityResults = listOfSetResultFlow()
|
||||
|
||||
viewModel.checkFinishActivityDuringOnPause(
|
||||
isActivityFinishing = true,
|
||||
isChangingConfigurations = false,
|
||||
scope = this
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(activityResults.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckFinishActivityDuringOnPause_doNothingIfIsChangingConfigurations() = runTest {
|
||||
val activityResults = listOfSetResultFlow()
|
||||
|
||||
viewModel.checkFinishActivityDuringOnPause(
|
||||
isActivityFinishing = false,
|
||||
isChangingConfigurations = true,
|
||||
scope = this
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(activityResults.size).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCheckFinishActivityDuringOnPause_defaultFinishSelf() = runTest {
|
||||
val activityResults = listOfSetResultFlow()
|
||||
|
||||
viewModel.checkFinishActivityDuringOnPause(
|
||||
isActivityFinishing = false,
|
||||
isChangingConfigurations = false,
|
||||
scope = backgroundScope
|
||||
)
|
||||
runCurrent()
|
||||
|
||||
assertThat(activityResults.size).isEqualTo(1)
|
||||
assertThat(activityResults[0].resultCode).isEqualTo(BiometricEnrollBase.RESULT_TIMEOUT)
|
||||
assertThat(activityResults[0].data).isEqualTo(null)
|
||||
}
|
||||
|
||||
private fun TestScope.listOfSetResultFlow(): List<ActivityResult> =
|
||||
mutableListOf<ActivityResult>().also {
|
||||
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||
viewModel.setResultFlow.toList(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.TextUtils
|
||||
import com.android.settings.biometrics.BiometricEnrollBase
|
||||
import com.android.settings.biometrics2.ui.model.EnrollmentRequest
|
||||
import com.google.android.setupcompat.util.WizardManagerHelper
|
||||
|
||||
object EnrollmentRequestUtils {
|
||||
@JvmStatic
|
||||
fun newAllFalseRequest(context: Context): EnrollmentRequest {
|
||||
return newRequest(
|
||||
context = context,
|
||||
isSuw = false,
|
||||
isSuwDeferred = false,
|
||||
isSuwPortal = false,
|
||||
isSuwSuggestedActionFlow = false,
|
||||
isSuwFirstRun = false,
|
||||
isFromSettingsSummery = false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newIsSuwRequest(context: Context): EnrollmentRequest {
|
||||
return newRequest(
|
||||
context = context,
|
||||
isSuw = true,
|
||||
isSuwDeferred = false,
|
||||
isSuwPortal = false,
|
||||
isSuwSuggestedActionFlow = false,
|
||||
isSuwFirstRun = false,
|
||||
isFromSettingsSummery = false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newIsSuwDeferredRequest(context: Context): EnrollmentRequest {
|
||||
return newRequest(
|
||||
context = context,
|
||||
isSuw = true,
|
||||
isSuwDeferred = true,
|
||||
isSuwPortal = false,
|
||||
isSuwSuggestedActionFlow = false,
|
||||
isSuwFirstRun = false,
|
||||
isFromSettingsSummery = false, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newIsSuwPortalRequest(context: Context): EnrollmentRequest {
|
||||
return newRequest(
|
||||
context = context,
|
||||
isSuw = true,
|
||||
isSuwDeferred = false,
|
||||
isSuwPortal = true,
|
||||
isSuwSuggestedActionFlow = false,
|
||||
isSuwFirstRun = false,
|
||||
isFromSettingsSummery = false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun newIsSuwSuggestedActionFlowRequest(
|
||||
context: Context
|
||||
): EnrollmentRequest {
|
||||
return newRequest(
|
||||
context = context,
|
||||
isSuw = true,
|
||||
isSuwDeferred = false,
|
||||
isSuwPortal = false,
|
||||
isSuwSuggestedActionFlow = true,
|
||||
isSuwFirstRun = false,
|
||||
isFromSettingsSummery = false)
|
||||
}
|
||||
|
||||
fun newRequest(
|
||||
context: Context,
|
||||
isSuw: Boolean,
|
||||
isSuwDeferred: Boolean,
|
||||
isSuwPortal: Boolean,
|
||||
isSuwSuggestedActionFlow: Boolean,
|
||||
isSuwFirstRun: Boolean,
|
||||
isFromSettingsSummery: Boolean,
|
||||
theme: String? = null
|
||||
): EnrollmentRequest {
|
||||
val i = Intent()
|
||||
i.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, isSuw)
|
||||
i.putExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, isSuwDeferred)
|
||||
i.putExtra(WizardManagerHelper.EXTRA_IS_PORTAL_SETUP, isSuwPortal)
|
||||
i.putExtra(WizardManagerHelper.EXTRA_IS_SUW_SUGGESTED_ACTION_FLOW, isSuwSuggestedActionFlow)
|
||||
i.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, isSuwFirstRun)
|
||||
i.putExtra(BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY, isFromSettingsSummery)
|
||||
if (!TextUtils.isEmpty(theme)) {
|
||||
i.putExtra(WizardManagerHelper.EXTRA_THEME, theme)
|
||||
}
|
||||
return EnrollmentRequest(i, context, true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.biometrics2.utils;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
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.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.settings.biometrics2.data.repository.FingerprintRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class FingerprintRepositoryUtils {
|
||||
|
||||
public static void setupSuwMaxFingerprintsEnrollable(
|
||||
@NonNull Context context,
|
||||
@NonNull Resources mockedResources,
|
||||
int numOfFp) {
|
||||
final int resId = context.getResources().getIdentifier("suw_max_fingerprints_enrollable",
|
||||
"integer", context.getPackageName());
|
||||
when(mockedResources.getInteger(resId)).thenReturn(numOfFp);
|
||||
}
|
||||
|
||||
public static FingerprintRepository newFingerprintRepository(
|
||||
@NonNull FingerprintManager mockedFingerprintManager,
|
||||
@FingerprintSensorProperties.SensorType int sensorType,
|
||||
int maxEnrollmentsPerUser) {
|
||||
|
||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
props.add(new FingerprintSensorPropertiesInternal(
|
||||
0 /* sensorId */,
|
||||
SensorProperties.STRENGTH_STRONG,
|
||||
maxEnrollmentsPerUser,
|
||||
new ArrayList<>() /* componentInfo */,
|
||||
sensorType,
|
||||
true /* resetLockoutRequiresHardwareAuthToken */));
|
||||
doAnswer(invocation -> {
|
||||
final IFingerprintAuthenticatorsRegisteredCallback callback =
|
||||
invocation.getArgument(0);
|
||||
callback.onAllAuthenticatorsRegistered(props);
|
||||
return null;
|
||||
}).when(mockedFingerprintManager).addAuthenticatorsRegisteredCallback(any());
|
||||
return new FingerprintRepository(mockedFingerprintManager);
|
||||
}
|
||||
|
||||
public static void setupFingerprintEnrolledFingerprints(
|
||||
@NonNull FingerprintManager mockedFingerprintManager,
|
||||
int userId,
|
||||
int enrolledFingerprints) {
|
||||
final ArrayList<Fingerprint> ret = new ArrayList<>();
|
||||
for (int i = 0; i < enrolledFingerprints; ++i) {
|
||||
ret.add(new Fingerprint("name", 0, 0, 0L));
|
||||
}
|
||||
when(mockedFingerprintManager.getEnrolledFingerprints(userId)).thenReturn(ret);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 androidx.slice.builders.ListBuilder.ICON_IMAGE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceViewManager;
|
||||
import androidx.slice.builders.ListBuilder;
|
||||
import androidx.slice.builders.ListBuilder.RowBuilder;
|
||||
import androidx.slice.builders.SliceAction;
|
||||
import androidx.test.annotation.UiThreadTest;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BlockingPrefWithSliceControllerTest {
|
||||
private static final String KEY = "bt_device_slice_category";
|
||||
private static final String TEST_URI_AUTHORITY = "com.android.settings";
|
||||
private static final String TEST_EXTRA_INTENT = "EXTRA_INTENT";
|
||||
private static final String TEST_EXTRA_PENDING_INTENT = "EXTRA_PENDING_INTENT";
|
||||
private static final String TEST_INTENT_ACTION = "test";
|
||||
private static final String TEST_PENDING_INTENT_ACTION = "test";
|
||||
private static final String TEST_SLICE_TITLE = "Test Title";
|
||||
private static final String TEST_SLICE_SUBTITLE = "Test Subtitle";
|
||||
private static final String FAKE_ACTION = "fake_action";
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
private LiveData<Slice> mLiveData;
|
||||
@Mock
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
@Captor
|
||||
ArgumentCaptor<Preference> mPreferenceArgumentCaptor;
|
||||
|
||||
private Context mContext;
|
||||
private BlockingPrefWithSliceController mController;
|
||||
private Uri mUri;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mController = spy(new BlockingPrefWithSliceController(mContext, KEY));
|
||||
mController.mLiveData = mLiveData;
|
||||
mController.mExtraIntent = TEST_EXTRA_INTENT;
|
||||
mController.mExtraPendingIntent = TEST_EXTRA_PENDING_INTENT;
|
||||
mController.mSliceIntentAction = TEST_INTENT_ACTION;
|
||||
mController.mSlicePendingIntentAction = TEST_PENDING_INTENT_ACTION;
|
||||
mController.mPreferenceCategory = mPreferenceCategory;
|
||||
mUri = Uri.EMPTY;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_uriNull_returnFalse() {
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@UiThreadTest
|
||||
public void isAvailable_uriNotNull_returnTrue() {
|
||||
mController.setSliceUri(mUri);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_registerObserver() {
|
||||
mController.onStart();
|
||||
|
||||
verify(mLiveData).observeForever(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_unregisterObserver() {
|
||||
mController.onStop();
|
||||
|
||||
verify(mLiveData).removeObserver(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_nullSlice_updateSlice() {
|
||||
mController.onChanged(null);
|
||||
|
||||
verify(mController).updatePreferenceFromSlice(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_testSlice_updateSlice() {
|
||||
mController.onChanged(buildTestSlice());
|
||||
|
||||
verify(mController.mPreferenceCategory).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChanged_sliceWithoutValidIntent_makePreferenceUnselectable() {
|
||||
mController.onChanged(buildTestSlice());
|
||||
|
||||
verify(mController.mPreferenceCategory).addPreference(mPreferenceArgumentCaptor.capture());
|
||||
assertThat(mPreferenceArgumentCaptor.getValue().isSelectable()).isFalse();
|
||||
}
|
||||
|
||||
private Slice buildTestSlice() {
|
||||
Uri uri =
|
||||
new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(TEST_URI_AUTHORITY)
|
||||
.build();
|
||||
SliceViewManager.getInstance(mContext).pinSlice(uri);
|
||||
ListBuilder listBuilder = new ListBuilder(mContext, uri, ListBuilder.INFINITY);
|
||||
IconCompat icon = mock(IconCompat.class);
|
||||
listBuilder.addRow(
|
||||
new RowBuilder()
|
||||
.setTitleItem(icon, ICON_IMAGE)
|
||||
.setTitle(TEST_SLICE_TITLE)
|
||||
.setSubtitle(TEST_SLICE_SUBTITLE)
|
||||
.setPrimaryAction(
|
||||
SliceAction.create(
|
||||
PendingIntent.getActivity(
|
||||
mContext,
|
||||
/*requestCode= */ 0,
|
||||
new Intent(FAKE_ACTION),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT
|
||||
| PendingIntent.FLAG_IMMUTABLE),
|
||||
icon,
|
||||
ICON_IMAGE,
|
||||
"")));
|
||||
return listBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (C) 2022 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 org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
|
||||
import com.android.settingslib.bluetooth.BluetoothBroadcastUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class QrCodeScanModeActivityTest {
|
||||
|
||||
@Mock
|
||||
private Intent mIntent;
|
||||
private QrCodeScanModeActivity mActivity;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mIntent = new Intent(BluetoothBroadcastUtils.ACTION_BLUETOOTH_LE_AUDIO_QR_CODE_SCANNER);
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
try {
|
||||
mActivity =
|
||||
spy((QrCodeScanModeActivity) InstrumentationRegistry
|
||||
.getInstrumentation().newActivity(
|
||||
getClass().getClassLoader(),
|
||||
QrCodeScanModeActivity.class.getName(), mIntent));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e); // nothing to do
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntent_noIntentAction_shouldFinish() {
|
||||
mIntent = new Intent();
|
||||
mActivity.handleIntent(mIntent);
|
||||
verify(mActivity).finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleIntent_hasIntentExtra_shouldShowFragment() {
|
||||
doNothing().when(mActivity).showQrCodeScannerFragment(mIntent);
|
||||
mActivity.handleIntent(mIntent);
|
||||
verify(mActivity).showQrCodeScannerFragment(mIntent);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.communal;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserManager;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.testutils.ResourcesUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CommunalPreferenceControllerTest {
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
private CommunalPreferenceController mController;
|
||||
|
||||
private static final String PREF_KEY = "test_key";
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mResources = spy(mContext.getResources());
|
||||
|
||||
mController = new CommunalPreferenceController(mContext, PREF_KEY);
|
||||
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_communalEnabled_shouldBeTrueForDockUser() {
|
||||
setCommunalEnabled(true);
|
||||
when(Utils.canCurrentUserDream(mContext)).thenReturn(true);
|
||||
assertTrue(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_communalEnabled_shouldBeFalseForNonDockUser() {
|
||||
setCommunalEnabled(true);
|
||||
when(Utils.canCurrentUserDream(mContext)).thenReturn(false);
|
||||
assertFalse(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_communalDisabled_shouldBeFalseForDockUser() {
|
||||
setCommunalEnabled(false);
|
||||
when(Utils.canCurrentUserDream(mContext)).thenReturn(true);
|
||||
assertFalse(mController.isAvailable());
|
||||
}
|
||||
|
||||
private void setCommunalEnabled(boolean enabled) {
|
||||
final int boolId = ResourcesUtils.getResourcesId(
|
||||
ApplicationProvider.getApplicationContext(), "bool",
|
||||
"config_show_communal_settings");
|
||||
when(mResources.getBoolean(boolId)).thenReturn(enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include platform/packages/modules/Connectivity:/thread/OWNERS
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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.threadnetwork
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.thread.ThreadNetworkController.STATE_DISABLED
|
||||
import android.net.thread.ThreadNetworkController.STATE_DISABLING
|
||||
import android.net.thread.ThreadNetworkController.STATE_ENABLED
|
||||
import android.net.thread.ThreadNetworkController.StateCallback
|
||||
import android.net.thread.ThreadNetworkException
|
||||
import android.os.OutcomeReceiver
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import android.provider.Settings
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.SwitchPreference
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.net.thread.platform.flags.Flags
|
||||
import com.android.settings.R
|
||||
import com.android.settings.core.BasePreferenceController.AVAILABLE
|
||||
import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE
|
||||
import com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING
|
||||
import com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE
|
||||
import com.android.settings.connecteddevice.threadnetwork.ThreadNetworkPreferenceController.BaseThreadNetworkController
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import java.util.concurrent.Executor
|
||||
|
||||
/** Unit tests for [ThreadNetworkPreferenceController]. */
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ThreadNetworkPreferenceControllerTest {
|
||||
@get:Rule
|
||||
val mSetFlagsRule = SetFlagsRule()
|
||||
private lateinit var context: Context
|
||||
private lateinit var executor: Executor
|
||||
private lateinit var controller: ThreadNetworkPreferenceController
|
||||
private lateinit var fakeThreadNetworkController: FakeThreadNetworkController
|
||||
private lateinit var preference: SwitchPreference
|
||||
private val broadcastReceiverArgumentCaptor = ArgumentCaptor.forClass(
|
||||
BroadcastReceiver::class.java
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_THREAD_ENABLED_PLATFORM)
|
||||
context = spy(ApplicationProvider.getApplicationContext<Context>())
|
||||
executor = ContextCompat.getMainExecutor(context)
|
||||
fakeThreadNetworkController = FakeThreadNetworkController(executor)
|
||||
controller = newControllerWithThreadFeatureSupported(true)
|
||||
val preferenceManager = PreferenceManager(context)
|
||||
val preferenceScreen = preferenceManager.createPreferenceScreen(context)
|
||||
preference = SwitchPreference(context)
|
||||
preference.key = "thread_network_settings"
|
||||
preferenceScreen.addPreference(preference)
|
||||
controller.displayPreference(preferenceScreen)
|
||||
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
}
|
||||
|
||||
private fun newControllerWithThreadFeatureSupported(
|
||||
present: Boolean
|
||||
): ThreadNetworkPreferenceController {
|
||||
return ThreadNetworkPreferenceController(
|
||||
context,
|
||||
"thread_network_settings" /* key */,
|
||||
executor,
|
||||
if (present) fakeThreadNetworkController else null
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_flagDisabled_returnsConditionallyUnavailable() {
|
||||
mSetFlagsRule.disableFlags(Flags.FLAG_THREAD_ENABLED_PLATFORM)
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_airPlaneModeOn_returnsDisabledDependentSetting() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_airPlaneModeOff_returnsAvailable() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun availabilityStatus_threadFeatureNotSupported_returnsUnsupported() {
|
||||
controller = newControllerWithThreadFeatureSupported(false)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(fakeThreadNetworkController.registeredStateCallback).isNull()
|
||||
assertThat(controller.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChecked_threadSetEnabled_returnsTrue() {
|
||||
fakeThreadNetworkController.setEnabled(true, executor) { }
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.isChecked).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isChecked_threadSetDisabled_returnsFalse() {
|
||||
fakeThreadNetworkController.setEnabled(false, executor) { }
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(controller.isChecked).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setChecked_setChecked_threadIsEnabled() {
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
controller.setChecked(true)
|
||||
|
||||
assertThat(fakeThreadNetworkController.isEnabled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setChecked_setUnchecked_threadIsDisabled() {
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
controller.setChecked(false)
|
||||
|
||||
assertThat(fakeThreadNetworkController.isEnabled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePreference_airPlaneModeOff_preferenceEnabled() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(preference.isEnabled).isTrue()
|
||||
assertThat(preference.summary).isEqualTo(
|
||||
context.resources.getString(R.string.thread_network_settings_summary)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePreference_airPlaneModeOn_preferenceDisabled() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
|
||||
assertThat(preference.isEnabled).isFalse()
|
||||
assertThat(preference.summary).isEqualTo(
|
||||
context.resources.getString(R.string.thread_network_settings_summary_airplane_mode)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updatePreference_airPlaneModeTurnedOn_preferenceDisabled() {
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0)
|
||||
startControllerAndCaptureCallbacks()
|
||||
|
||||
Settings.Global.putInt(context.contentResolver, Settings.Global.AIRPLANE_MODE_ON, 1)
|
||||
broadcastReceiverArgumentCaptor.value.onReceive(context, Intent())
|
||||
|
||||
assertThat(preference.isEnabled).isFalse()
|
||||
assertThat(preference.summary).isEqualTo(
|
||||
context.resources.getString(R.string.thread_network_settings_summary_airplane_mode)
|
||||
)
|
||||
}
|
||||
|
||||
private fun startControllerAndCaptureCallbacks() {
|
||||
controller.onStateChanged(mock(LifecycleOwner::class.java), Lifecycle.Event.ON_START)
|
||||
verify(context)!!.registerReceiver(broadcastReceiverArgumentCaptor.capture(), any())
|
||||
}
|
||||
|
||||
private class FakeThreadNetworkController(private val executor: Executor) :
|
||||
BaseThreadNetworkController {
|
||||
var isEnabled = true
|
||||
private set
|
||||
var registeredStateCallback: StateCallback? = null
|
||||
private set
|
||||
|
||||
override fun setEnabled(
|
||||
enabled: Boolean,
|
||||
executor: Executor,
|
||||
receiver: OutcomeReceiver<Void?, ThreadNetworkException>
|
||||
) {
|
||||
isEnabled = enabled
|
||||
if (registeredStateCallback != null) {
|
||||
if (!isEnabled) {
|
||||
executor.execute {
|
||||
registeredStateCallback!!.onThreadEnableStateChanged(
|
||||
STATE_DISABLING
|
||||
)
|
||||
}
|
||||
executor.execute {
|
||||
registeredStateCallback!!.onThreadEnableStateChanged(
|
||||
STATE_DISABLED
|
||||
)
|
||||
}
|
||||
} else {
|
||||
executor.execute {
|
||||
registeredStateCallback!!.onThreadEnableStateChanged(
|
||||
STATE_ENABLED
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
executor.execute { receiver.onResult(null) }
|
||||
}
|
||||
|
||||
override fun registerStateCallback(
|
||||
executor: Executor,
|
||||
callback: StateCallback
|
||||
) {
|
||||
require(callback !== registeredStateCallback) { "callback is already registered" }
|
||||
registeredStateCallback = callback
|
||||
val enabledState =
|
||||
if (isEnabled) STATE_ENABLED else STATE_DISABLED
|
||||
executor.execute { registeredStateCallback!!.onThreadEnableStateChanged(enabledState) }
|
||||
}
|
||||
|
||||
override fun unregisterStateCallback(callback: StateCallback) {
|
||||
requireNotNull(registeredStateCallback) { "callback is already unregistered" }
|
||||
registeredStateCallback = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class BadPreferenceController extends BasePreferenceController {
|
||||
|
||||
public BadPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
throw new IllegalArgumentException("error");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useDynamicSliceSummary() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core;
|
||||
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.core.codeinspection.CodeInspector;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BasePreferenceControllerSignatureInspector extends CodeInspector {
|
||||
|
||||
private final List<String> mExemptList;
|
||||
|
||||
public BasePreferenceControllerSignatureInspector(List<Class<?>> classes) {
|
||||
super(classes);
|
||||
mExemptList = new ArrayList<>();
|
||||
initializeExemptList(mExemptList,
|
||||
"exempt_invalid_base_preference_controller_constructor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
StringBuilder badClasses = new StringBuilder();
|
||||
|
||||
for (Class c : mClasses) {
|
||||
if (!isConcreteSettingsClass(c)) {
|
||||
// Not a Settings class, or is abstract, don't care.
|
||||
continue;
|
||||
}
|
||||
if (!BasePreferenceController.class.isAssignableFrom(c)) {
|
||||
// Not a BasePreferenceController, don't care.
|
||||
continue;
|
||||
}
|
||||
final String className = c.getName();
|
||||
if (mExemptList.remove(className)) {
|
||||
continue;
|
||||
}
|
||||
final Constructor[] constructors = c.getDeclaredConstructors();
|
||||
if (constructors == null || constructors.length == 0) {
|
||||
badClasses.append(c.getName()).append(",");
|
||||
}
|
||||
|
||||
boolean hasValidConstructor = false;
|
||||
for (Constructor constructor : constructors) {
|
||||
if (hasValidConstructorSignature(constructor)) {
|
||||
hasValidConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasValidConstructor) {
|
||||
badClasses.append(className).append(",");
|
||||
}
|
||||
}
|
||||
|
||||
assertWithMessage("All BasePreferenceController (and subclasses) constructor must either"
|
||||
+ " only take Context, or (Context, String). No other types are allowed")
|
||||
.that(badClasses.toString())
|
||||
.isEmpty();
|
||||
|
||||
assertWithMessage("Something in the exempt list is no longer relevant. Please remove"
|
||||
+ "it from packages/apps/Settings/tests/robotests/assets/"
|
||||
+ "exempt_invalid_base_preference_controller_constructor")
|
||||
.that(mExemptList)
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
private static boolean hasValidConstructorSignature(Constructor constructor) {
|
||||
final Class[] parameterTypes = constructor.getParameterTypes();
|
||||
if (parameterTypes.length == 1) {
|
||||
return Context.class.isAssignableFrom(parameterTypes[0]);
|
||||
} else if (parameterTypes.length == 2) {
|
||||
return Context.class.isAssignableFrom(parameterTypes[0])
|
||||
&& String.class.isAssignableFrom(parameterTypes[1]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.slices.SliceData;
|
||||
import com.android.settings.widget.SeekBarPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SettingsSliderPreferenceControllerTest {
|
||||
|
||||
private FakeSliderPreferenceController mSliderController;
|
||||
|
||||
private SeekBarPreference mPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mPreference = new SeekBarPreference(ApplicationProvider.getApplicationContext());
|
||||
mSliderController = new FakeSliderPreferenceController(
|
||||
ApplicationProvider.getApplicationContext(), "key");
|
||||
|
||||
mPreference.setContinuousUpdates(true);
|
||||
mPreference.setMin(mSliderController.getMin());
|
||||
mPreference.setMax(mSliderController.getMax());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_updatesPosition() {
|
||||
final int newValue = 28;
|
||||
|
||||
mSliderController.onPreferenceChange(mPreference, newValue);
|
||||
|
||||
assertThat(mSliderController.getSliderPosition()).isEqualTo(newValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_setsPreferenceToCurrentValue() {
|
||||
final int newValue = 28;
|
||||
mSliderController.setSliderPosition(newValue);
|
||||
|
||||
mSliderController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.getProgress()).isEqualTo(newValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSliceType_returnsSliceType() {
|
||||
assertThat(mSliderController.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER);
|
||||
}
|
||||
|
||||
private class FakeSliderPreferenceController extends SliderPreferenceController {
|
||||
|
||||
private static final int MAX_STEPS = 2112;
|
||||
private int mPosition;
|
||||
|
||||
private FakeSliderPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliderPosition() {
|
||||
return mPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setSliderPosition(int position) {
|
||||
mPosition = position;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMax() {
|
||||
return MAX_STEPS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMin() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import com.android.settings.slices.SliceData;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TogglePreferenceControllerTest {
|
||||
|
||||
private FakeToggle mToggleController;
|
||||
|
||||
private Context mContext;
|
||||
private SwitchPreference mPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = ApplicationProvider.getApplicationContext();
|
||||
mPreference = new SwitchPreference(mContext);
|
||||
mToggleController = new FakeToggle(mContext, "key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetsPreferenceValue_setsChecked() {
|
||||
mToggleController.setChecked(true);
|
||||
mPreference.setChecked(false);
|
||||
|
||||
mToggleController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetsPreferenceValue_setsNotChecked() {
|
||||
mToggleController.setChecked(false);
|
||||
mPreference.setChecked(true);
|
||||
|
||||
mToggleController.updateState(mPreference);
|
||||
|
||||
assertThat(mPreference.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesPreferenceOnChange_turnsOn() {
|
||||
boolean newValue = true;
|
||||
mToggleController.setChecked(!newValue);
|
||||
|
||||
mToggleController.onPreferenceChange(mPreference, newValue);
|
||||
|
||||
assertThat(mToggleController.isChecked()).isEqualTo(newValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatesPreferenceOnChange_turnsOff() {
|
||||
boolean newValue = false;
|
||||
mToggleController.setChecked(!newValue);
|
||||
|
||||
mToggleController.onPreferenceChange(mPreference, newValue);
|
||||
|
||||
assertThat(mToggleController.isChecked()).isEqualTo(newValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSliceType_returnsSliceType() {
|
||||
assertThat(mToggleController.getSliceType()).isEqualTo(
|
||||
SliceData.SliceType.SWITCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnTrue() {
|
||||
assertThat(mToggleController.isSliceable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isPublicSlice_returnFalse() {
|
||||
assertThat(mToggleController.isPublicSlice()).isFalse();
|
||||
}
|
||||
|
||||
private static class FakeToggle extends TogglePreferenceController {
|
||||
|
||||
private boolean mCheckedFlag;
|
||||
|
||||
private FakeToggle(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return mCheckedFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
mCheckedFlag = isChecked;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSliceHighlightMenuRes() {
|
||||
return 5678;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core.codeinspection;
|
||||
|
||||
import com.google.common.reflect.ClassPath;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Scans and builds all classes in current classloader.
|
||||
*/
|
||||
public class ClassScanner {
|
||||
|
||||
public List<Class<?>> getClassesForPackage(String packageName) throws ClassNotFoundException {
|
||||
final List<Class<?>> classes = new ArrayList<>();
|
||||
|
||||
try {
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
ClassPath classPath = ClassPath.from(classLoader);
|
||||
|
||||
// Some anonymous classes don't return true when calling isAnonymousClass(), but they
|
||||
// always seem to be nested anonymous classes like com.android.settings.Foo$1$2. In
|
||||
// general we don't want any anonymous classes so we just filter these out by searching
|
||||
// for $[0-9] in the name.
|
||||
Pattern anonymousClassPattern = Pattern.compile(".*\\$\\d+.*");
|
||||
Matcher anonymousClassMatcher = anonymousClassPattern.matcher("");
|
||||
|
||||
for (ClassPath.ClassInfo info : classPath.getAllClasses()) {
|
||||
if (info.getPackageName().startsWith(packageName)) {
|
||||
try {
|
||||
Class clazz = classLoader.loadClass(info.getName());
|
||||
if (clazz.isAnonymousClass() || anonymousClassMatcher.reset(
|
||||
clazz.getName()).matches()) {
|
||||
continue;
|
||||
}
|
||||
classes.add(clazz);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// do nothing. this class hasn't been found by the
|
||||
// loader, and we don't care.
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new ClassNotFoundException("Error when parsing " + packageName, e);
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core.codeinspection;
|
||||
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Inspector takes a list of class objects and perform static code analysis in its {@link #run()}
|
||||
* method.
|
||||
*/
|
||||
public abstract class CodeInspector {
|
||||
|
||||
protected static final String PACKAGE_NAME = "com.android.settings";
|
||||
|
||||
private static final String TEST_CLASS_SUFFIX = "Test";
|
||||
private static final String TEST_INNER_CLASS_SIGNATURE = "Test$";
|
||||
|
||||
protected final List<Class<?>> mClasses;
|
||||
|
||||
public CodeInspector(List<Class<?>> classes) {
|
||||
mClasses = classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Code inspection runner method.
|
||||
*/
|
||||
public abstract void run();
|
||||
|
||||
protected void assertNoObsoleteInExemptList(String listName, List<String> list) {
|
||||
final StringBuilder obsoleteExemptItems = new StringBuilder(listName).append(
|
||||
" contains item that should not be exempted.\n");
|
||||
for (String c : list) {
|
||||
obsoleteExemptItems.append(c).append("\n");
|
||||
}
|
||||
assertWithMessage(obsoleteExemptItems.toString()).that(list).isEmpty();
|
||||
}
|
||||
|
||||
protected boolean isConcreteSettingsClass(Class clazz) {
|
||||
// Abstract classes
|
||||
if (Modifier.isAbstract(clazz.getModifiers())) {
|
||||
return false;
|
||||
}
|
||||
final String packageName = clazz.getPackage().getName();
|
||||
// Classes that are not in Settings
|
||||
if (!packageName.contains(PACKAGE_NAME + ".") && !packageName.endsWith(PACKAGE_NAME)) {
|
||||
return false;
|
||||
}
|
||||
final String className = clazz.getName();
|
||||
// Classes from tests
|
||||
if (className.endsWith(TEST_CLASS_SUFFIX)) {
|
||||
return false;
|
||||
}
|
||||
if (className.contains(TEST_INNER_CLASS_SIGNATURE)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void initializeExemptList(List<String> exemptList, String filename) {
|
||||
try {
|
||||
final InputStream in = ApplicationProvider.getApplicationContext()
|
||||
.getAssets().open(filename);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
exemptList.add(line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Error initializing exempt list " + filename, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.core.codeinspection;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||
import com.android.settingslib.core.lifecycle.events.OnAttach;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
||||
import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
|
||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
||||
import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
||||
import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
|
||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||
import com.android.settingslib.core.lifecycle.events.SetPreferenceScreen;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class LifecycleObserverCodeInspector extends CodeInspector {
|
||||
|
||||
private static final List<Class> LIFECYCLE_EVENTS = Arrays.asList(
|
||||
OnAttach.class,
|
||||
OnCreate.class,
|
||||
OnCreateOptionsMenu.class,
|
||||
OnDestroy.class,
|
||||
OnOptionsItemSelected.class,
|
||||
OnPause.class,
|
||||
OnPrepareOptionsMenu.class,
|
||||
OnResume.class,
|
||||
OnSaveInstanceState.class,
|
||||
OnStart.class,
|
||||
OnStop.class,
|
||||
SetPreferenceScreen.class
|
||||
);
|
||||
|
||||
public LifecycleObserverCodeInspector(List<Class<?>> classes) {
|
||||
super(classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Set<String> notImplementingLifecycleObserver = new ArraySet<>();
|
||||
for (Class clazz : mClasses) {
|
||||
if (!isConcreteSettingsClass(clazz)) {
|
||||
continue;
|
||||
}
|
||||
boolean classObservesLifecycleEvent = false;
|
||||
for (Class event : LIFECYCLE_EVENTS) {
|
||||
if (event.isAssignableFrom(clazz)) {
|
||||
classObservesLifecycleEvent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (classObservesLifecycleEvent && !LifecycleObserver.class.isAssignableFrom(clazz)) {
|
||||
// Observes LifecycleEvent but not implementing LifecycleObserver. Something is
|
||||
// wrong.
|
||||
notImplementingLifecycleObserver.add(clazz.getName());
|
||||
}
|
||||
}
|
||||
if (!notImplementingLifecycleObserver.isEmpty()) {
|
||||
final String errorTemplate =
|
||||
"The following class(es) implements lifecycle.events.*, but don't "
|
||||
+ "implement LifecycleObserver. Something is wrong:\n";
|
||||
final StringBuilder error = new StringBuilder(errorTemplate);
|
||||
for (String name : notImplementingLifecycleObserver) {
|
||||
error.append(name).append('\n');
|
||||
}
|
||||
fail(error.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user