fix: 引入Settings的Module
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package com.android.settings.location;
|
||||
|
||||
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.location.LocationManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
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 org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AppLocationPermissionPreferenceControllerTest {
|
||||
|
||||
private AppLocationPermissionPreferenceController mController;
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
private LocationManager mLocationManager;
|
||||
private LocationSettings mLocationSettings;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
mLocationSettings = spy(new LocationSettings());
|
||||
when(mLocationSettings.getSettingsLifecycle()).thenReturn(mLifecycle);
|
||||
mController = new AppLocationPermissionPreferenceController(mContext, "key");
|
||||
mController.init(mLocationSettings);
|
||||
mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_noLocationLinkPermission_shouldReturnFalse() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, 0);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_hasLocationLinkPermission_shouldReturnTrue() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED, 1);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationIsOff_shouldReturnStringForOff() {
|
||||
mLocationManager.setLocationEnabledForUser(false, android.os.Process.myUserHandle());
|
||||
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.location_app_permission_summary_location_off));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationIsOn_shouldReturnLoadingString() {
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.location_settings_loading_app_permission_stats));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationAppCountIsOne_shouldShowSingularString() {
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
mController.mNumHasLocation = 1;
|
||||
mController.mNumTotal = 1;
|
||||
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("count", 1);
|
||||
arguments.put("total", 1);
|
||||
assertThat(mController.getSummary()).isEqualTo(StringUtil.getIcuPluralsString(mContext,
|
||||
arguments, R.string.location_app_permission_summary_location_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationAppCountIsGreaterThanOne_shouldShowPluralString() {
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
mController.mNumHasLocation = 5;
|
||||
mController.mNumTotal = 10;
|
||||
|
||||
Map<String, Object> arguments = new HashMap<>();
|
||||
arguments.put("count", 5);
|
||||
arguments.put("total", 10);
|
||||
assertThat(mController.getSummary()).isEqualTo(StringUtil.getIcuPluralsString(mContext,
|
||||
arguments, R.string.location_app_permission_summary_location_on));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.Global;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothScanningPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
private SwitchPreference mPreference;
|
||||
|
||||
private ContentResolver mContentResolver;
|
||||
private BluetoothScanningPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContentResolver = RuntimeEnvironment.application.getContentResolver();
|
||||
mController = new BluetoothScanningPreferenceController(RuntimeEnvironment.application);
|
||||
when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_bluetoothScanningEnabled_shouldCheckedPreference() {
|
||||
Settings.Global.putInt(mContentResolver, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_bluetoothScanningDisabled_shouldUncheckedPreference() {
|
||||
Settings.Global.putInt(mContentResolver, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_checked_shouldEnableBluetoothScanning() {
|
||||
when(mPreference.isChecked()).thenReturn(true);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
final int btScanning = Global.getInt(mContentResolver, Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
assertThat(btScanning).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_unchecked_shouldDisableBluetoothScanning() {
|
||||
when(mPreference.isChecked()).thenReturn(false);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
final int btScanning = Global.getInt(mContentResolver, Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
assertThat(btScanning).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowSecureSettings;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowSecureSettings.class)
|
||||
public class LocationEnablerTest {
|
||||
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private LocationEnabler.LocationModeChangeListener mListener;
|
||||
|
||||
private Context mContext;
|
||||
private LocationEnabler mEnabler;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
mEnabler = spy(new LocationEnabler(mContext, mListener, mLifecycle));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_shouldSetActiveAndRegisterListener() {
|
||||
mEnabler.onStart();
|
||||
|
||||
verify(mContext).registerReceiver(eq(mEnabler.mReceiver),
|
||||
eq(LocationEnabler.INTENT_FILTER_LOCATION_MODE_CHANGED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_shouldRefreshLocationMode() {
|
||||
mEnabler.onStart();
|
||||
|
||||
verify(mEnabler).refreshLocationMode();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_shouldUnregisterListener() {
|
||||
mEnabler.onStart();
|
||||
mEnabler.onStop();
|
||||
|
||||
verify(mContext).unregisterReceiver(mEnabler.mReceiver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onReceive_shouldRefreshLocationMode() {
|
||||
mEnabler.onStart();
|
||||
reset(mListener);
|
||||
mEnabler.mReceiver.onReceive(mContext, new Intent());
|
||||
|
||||
verify(mListener).onLocationModeChanged(anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEnabled_locationOff_shouldReturnFalse() {
|
||||
assertThat(mEnabler.isEnabled(Settings.Secure.LOCATION_MODE_OFF)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEnabled_restricted_shouldReturnFalse() {
|
||||
when(mUserManager.hasUserRestriction(anyString())).thenReturn(true);
|
||||
|
||||
assertThat(mEnabler.isEnabled(Settings.Secure.LOCATION_MODE_OFF)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEnabled_locationNotRestricted_shouldReturnTrue() {
|
||||
when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
|
||||
|
||||
assertThat(mEnabler.isEnabled(Settings.Secure.LOCATION_MODE_BATTERY_SAVING)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refreshLocationMode_shouldCallOnLocationModeChanged() {
|
||||
mEnabler.refreshLocationMode();
|
||||
|
||||
verify(mListener).onLocationModeChanged(anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLocationEnabled_notRestricted_shouldRefreshLocation() {
|
||||
when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
|
||||
mEnabler.setLocationEnabled(true);
|
||||
|
||||
verify(mEnabler).refreshLocationMode();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setLocationEnabled_notRestricted_shouldBroadcastUpdateAndSetChanger() {
|
||||
when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
|
||||
Settings.Secure.putInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
|
||||
mEnabler.setLocationEnabled(true);
|
||||
|
||||
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.LOCATION_CHANGER, Settings.Secure.LOCATION_CHANGER_UNKNOWN))
|
||||
.isEqualTo(Settings.Secure.LOCATION_CHANGER_SYSTEM_SETTINGS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isManagedProfileRestrictedByBase_notManagedProfile_shouldReturnFalse() {
|
||||
assertThat(mEnabler.isManagedProfileRestrictedByBase()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isManagedProfileRestrictedByBase_notRestricted_shouldReturnFalse() {
|
||||
mockManagedProfile();
|
||||
doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
|
||||
assertThat(mEnabler.isManagedProfileRestrictedByBase()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isManagedProfileRestrictedByBase_hasManagedProfile_shouldReturnFalse() {
|
||||
mockManagedProfile();
|
||||
doReturn(true).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
|
||||
assertThat(mEnabler.isManagedProfileRestrictedByBase()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRestriction_getShareLocationEnforcedAdmin_shouldReturnEnforcedAdmin() {
|
||||
int userId = UserHandle.myUserId();
|
||||
List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
|
||||
// Add two enforcing users so that RestrictedLockUtils.checkIfRestrictionEnforced returns
|
||||
// non-null.
|
||||
enforcingUsers.add(new UserManager.EnforcingUser(userId,
|
||||
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
|
||||
enforcingUsers.add(new UserManager.EnforcingUser(userId,
|
||||
UserManager.RESTRICTION_SOURCE_PROFILE_OWNER));
|
||||
when(mUserManager.getUserRestrictionSources(
|
||||
UserManager.DISALLOW_CONFIG_LOCATION, UserHandle.of(userId)))
|
||||
.thenReturn(enforcingUsers);
|
||||
|
||||
assertThat(mEnabler.getShareLocationEnforcedAdmin(userId) != null).isTrue();
|
||||
}
|
||||
|
||||
private void mockManagedProfile() {
|
||||
final List<UserHandle> userProfiles = new ArrayList<>();
|
||||
final UserHandle userHandle = mock(UserHandle.class);
|
||||
when(userHandle.getIdentifier()).thenReturn(5);
|
||||
userProfiles.add(userHandle);
|
||||
when(mUserManager.getUserProfiles()).thenReturn(userProfiles);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(1);
|
||||
when(mUserManager.getUserInfo(5))
|
||||
.thenReturn(new UserInfo(5, "user 5", UserInfo.FLAG_MANAGED_PROFILE));
|
||||
}
|
||||
|
||||
private static ArgumentMatcher<Intent> actionMatches(String expected) {
|
||||
return intent -> TextUtils.equals(expected, intent.getAction());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
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.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LocationForWorkPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
private RestrictedSwitchPreference mPreference;
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private LocationEnabler mEnabler;
|
||||
@Mock
|
||||
private UserHandle mUserHandle;
|
||||
|
||||
private Context mContext;
|
||||
private LocationForWorkPreferenceController mController;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
private LocationSettings mLocationSettings;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
mLocationSettings = spy(new LocationSettings());
|
||||
when(mLocationSettings.getSettingsLifecycle()).thenReturn(mLifecycle);
|
||||
mController = spy(new LocationForWorkPreferenceController(mContext, "key"));
|
||||
mController.init(mLocationSettings);
|
||||
mockManagedProfile();
|
||||
ReflectionHelpers.setField(mController, "mLocationEnabler", mEnabler);
|
||||
when(mScreen.findPreference(any())).thenReturn(mPreference);
|
||||
final String key = mController.getPreferenceKey();
|
||||
when(mPreference.getKey()).thenReturn(key);
|
||||
when(mPreference.isVisible()).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_preferenceChecked_shouldSetRestrictionAndOnSummary() {
|
||||
mController.displayPreference(mScreen);
|
||||
when(mPreference.isChecked()).thenReturn(true);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mUserManager)
|
||||
.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, false, mUserHandle);
|
||||
verify(mPreference).setSummary(R.string.switch_on_text);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_preferenceUnchecked_shouldSetRestritionAndOffSummary() {
|
||||
mController.displayPreference(mScreen);
|
||||
when(mPreference.isChecked()).thenReturn(false);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mUserManager)
|
||||
.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, mUserHandle);
|
||||
verify(mPreference).setSummary(R.string.switch_off_text);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_disabledByAdmin_shouldDisablePreference() {
|
||||
mController.displayPreference(mScreen);
|
||||
final EnforcedAdmin admin = mock(EnforcedAdmin.class);
|
||||
doReturn(admin).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).isManagedProfileRestrictedByBase();
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mPreference).setDisabledByAdmin(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_locationOff_shouldDisablePreference() {
|
||||
mController.displayPreference(mScreen);
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).isManagedProfileRestrictedByBase();
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_OFF, false);
|
||||
|
||||
verify(mPreference).setEnabled(false);
|
||||
verify(mPreference).setChecked(false);
|
||||
verify(mPreference).setSummary(R.string.location_app_permission_summary_location_off);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_locationOn_shouldEnablePreference() {
|
||||
mController.displayPreference(mScreen);
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).isManagedProfileRestrictedByBase();
|
||||
doReturn(true).when(mEnabler).isEnabled(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mPreference).setEnabled(true);
|
||||
verify(mPreference).setSummary(R.string.switch_on_text);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_noRestriction_shouldCheckedPreference() {
|
||||
mController.displayPreference(mScreen);
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).isManagedProfileRestrictedByBase();
|
||||
doReturn(true).when(mEnabler).isEnabled(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mPreference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_hasRestriction_shouldCheckedPreference() {
|
||||
mController.displayPreference(mScreen);
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(true).when(mEnabler).isManagedProfileRestrictedByBase();
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
private void mockManagedProfile() {
|
||||
final List<UserHandle> userProfiles = new ArrayList<>();
|
||||
when(mUserHandle.getIdentifier()).thenReturn(5);
|
||||
userProfiles.add(mUserHandle);
|
||||
when(mUserManager.getUserProfiles()).thenReturn(userProfiles);
|
||||
when(mUserManager.getProcessUserId()).thenReturn(1);
|
||||
when(mUserManager.getUserInfo(5))
|
||||
.thenReturn(new UserInfo(5, "user 5", UserInfo.FLAG_MANAGED_PROFILE));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.location;
|
||||
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.provider.DeviceConfig;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LocationIndicatorsPreferenceController}.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowDeviceConfig.class})
|
||||
public class LocationIndicatorsPreferenceControllerTest {
|
||||
@Mock
|
||||
PackageManager mPackageManager;
|
||||
private Context mContext;
|
||||
private LocationIndicatorsPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mController = new LocationIndicatorsPreferenceController(mContext, "key");
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowDeviceConfig.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the location indicator settings are visible when location feature is supported
|
||||
* on the device.
|
||||
*/
|
||||
@Test
|
||||
public void getAvailabilityStatus_locationSupported_shouldReturnAVAILABLE() {
|
||||
// Enable the settings flags.
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
Utils.PROPERTY_LOCATION_INDICATOR_SETTINGS_ENABLED, Boolean.toString(true),
|
||||
true);
|
||||
when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_LOCATION))).thenReturn(
|
||||
true);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the location indicator settings are not visible when location feature is not supported
|
||||
* on the device.
|
||||
*/
|
||||
@Test
|
||||
public void getAvailabilityStatus_locationNotSupported_shouldReturnUNSUPPORTED() {
|
||||
// Enable the settings flags.
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
Utils.PROPERTY_LOCATION_INDICATOR_SETTINGS_ENABLED, Boolean.toString(true),
|
||||
true);
|
||||
when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_LOCATION))).thenReturn(
|
||||
false);
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the location indicator settings are not visible when location indicator settings
|
||||
* are disabled on the device.
|
||||
*/
|
||||
@Test
|
||||
public void getAvailabilityStatus_flagDisabled_shouldReturnUNSUPPORTED() {
|
||||
// Disable the settings flags.
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
Utils.PROPERTY_LOCATION_INDICATOR_SETTINGS_ENABLED, Boolean.toString(false),
|
||||
false);
|
||||
when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_LOCATION))).thenReturn(
|
||||
true);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the location indicator preference is checked when the feature is enabled.
|
||||
*/
|
||||
@Test
|
||||
public void isChecked_featureEnabled_shouldReturnTrue() {
|
||||
final boolean enabled = true;
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
Utils.PROPERTY_LOCATION_INDICATORS_ENABLED, Boolean.toString(enabled), true);
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the location indicator preference is unchecked when the feature is not enabled.
|
||||
*/
|
||||
@Test
|
||||
public void isChecked_featureNotEnabled_shouldReturnFalse() {
|
||||
final boolean enabled = false;
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
Utils.PROPERTY_LOCATION_INDICATORS_ENABLED, Boolean.toString(enabled), true);
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
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.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.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settings.widget.RestrictedAppPreference;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowUserManager.class, ShadowDevicePolicyManager.class})
|
||||
public class LocationInjectedServicesPreferenceControllerTest {
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
|
||||
private static final String KEY_LOCATION_SERVICES = "location_service";
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private LocationSettings mFragment;
|
||||
@Mock
|
||||
private PreferenceCategory mCategoryPrimary;
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private AppSettingsInjector mSettingsInjector;
|
||||
@Mock
|
||||
private DevicePolicyManager mDevicePolicyManager;
|
||||
|
||||
private Context mContext;
|
||||
private LocationInjectedServicesPreferenceController mController;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
mController = spy(
|
||||
new LocationInjectedServicesPreferenceController(mContext, KEY_LOCATION_SERVICES));
|
||||
when(mFragment.getSettingsLifecycle()).thenReturn(mLifecycle);
|
||||
mController.init(mFragment);
|
||||
mController.mInjector = mSettingsInjector;
|
||||
final String key = mController.getPreferenceKey();
|
||||
when(mScreen.findPreference(key)).thenReturn(mCategoryPrimary);
|
||||
when(mCategoryPrimary.getKey()).thenReturn(key);
|
||||
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
|
||||
.thenReturn(mDevicePolicyManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onResume_shouldRegisterListener() {
|
||||
mController.onResume();
|
||||
|
||||
verify(mContext).registerReceiver(eq(mController.mInjectedSettingsReceiver),
|
||||
eq(mController.INTENT_FILTER_INJECTED_SETTING_CHANGED),
|
||||
anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPause_shouldUnregisterListener() {
|
||||
mController.onResume();
|
||||
mController.onPause();
|
||||
|
||||
verify(mContext).unregisterReceiver(mController.mInjectedSettingsReceiver);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workProfileDisallowShareLocationOn_getParentUserLocationServicesOnly() {
|
||||
final int fakeWorkProfileId = 123;
|
||||
ShadowUserManager.getShadow().setProfileIdsWithDisabled(
|
||||
new int[]{UserHandle.myUserId(), fakeWorkProfileId});
|
||||
ShadowUserManager.getShadow().addProfile(new UserInfo(UserHandle.myUserId(), "", 0));
|
||||
ShadowUserManager.getShadow().addProfile(new UserInfo(fakeWorkProfileId, "",
|
||||
UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_PROFILE));
|
||||
|
||||
// Mock RestrictedLockUtils.checkIfRestrictionEnforced and let it return non-null.
|
||||
final List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
|
||||
enforcingUsers.add(new UserManager.EnforcingUser(fakeWorkProfileId,
|
||||
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
|
||||
final ComponentName componentName = new ComponentName("test", "test");
|
||||
// Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null.
|
||||
ShadowUserManager.getShadow().setUserRestrictionSources(
|
||||
UserManager.DISALLOW_SHARE_LOCATION,
|
||||
UserHandle.of(fakeWorkProfileId),
|
||||
enforcingUsers);
|
||||
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(componentName);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
verify(mSettingsInjector).getInjectedSettings(
|
||||
any(Context.class), eq(UserHandle.myUserId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void workProfileDisallowShareLocationOff_getAllUserLocationServices() {
|
||||
final int fakeWorkProfileId = 123;
|
||||
ShadowUserManager.getShadow().setProfileIdsWithDisabled(
|
||||
new int[]{UserHandle.myUserId(), fakeWorkProfileId});
|
||||
|
||||
// Mock RestrictedLockUtils.checkIfRestrictionEnforced and let it return null.
|
||||
// Empty enforcing users.
|
||||
final List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
|
||||
ShadowUserManager.getShadow().setUserRestrictionSources(
|
||||
UserManager.DISALLOW_SHARE_LOCATION,
|
||||
UserHandle.of(fakeWorkProfileId),
|
||||
enforcingUsers);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
verify(mSettingsInjector).getInjectedSettings(
|
||||
any(Context.class), eq(UserHandle.USER_CURRENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_shouldRequestReloadInjectedSettigns() {
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mSettingsInjector).reloadStatusMessages();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withUserRestriction_shouldDisableLocationAccuracy() {
|
||||
final List<Preference> preferences = new ArrayList<>();
|
||||
final RestrictedAppPreference pref = new RestrictedAppPreference(mContext,
|
||||
UserManager.DISALLOW_CONFIG_LOCATION);
|
||||
pref.setTitle("Location Accuracy");
|
||||
preferences.add(pref);
|
||||
final Map<Integer, List<Preference>> map = new ArrayMap<>();
|
||||
map.put(UserHandle.myUserId(), preferences);
|
||||
doReturn(map).when(mSettingsInjector)
|
||||
.getInjectedSettings(any(Context.class), anyInt());
|
||||
ShadowUserManager.getShadow().setProfileIdsWithDisabled(new int[]{UserHandle.myUserId()});
|
||||
|
||||
final int userId = UserHandle.myUserId();
|
||||
List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
|
||||
enforcingUsers.add(new UserManager.EnforcingUser(userId,
|
||||
UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
|
||||
ComponentName componentName = new ComponentName("test", "test");
|
||||
// Ensure that RestrictedLockUtils.checkIfRestrictionEnforced doesn't return null.
|
||||
ShadowUserManager.getShadow().setUserRestrictionSources(
|
||||
UserManager.DISALLOW_CONFIG_LOCATION,
|
||||
UserHandle.of(userId),
|
||||
enforcingUsers);
|
||||
when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(componentName);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
assertThat(pref.isDisabledByAdmin()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LocationServicesPreferenceControllerTest {
|
||||
@Mock
|
||||
private Context mContext;
|
||||
private LocationServicesPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mController = new LocationServicesPreferenceController(mContext, "key");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocationScanning_byDefault_shouldBeShown() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(qualifiers = "mcc999")
|
||||
public void testLocationScanning_ifDisabled_shouldNotBeShown() {
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.SearchIndexableResource;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LocationServicesTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void searchProvider_shouldIndexDefaultXml() {
|
||||
final List<SearchIndexableResource> sir = LocationServices.SEARCH_INDEX_DATA_PROVIDER
|
||||
.getXmlResourcesToIndex(mContext, true /* enabled */);
|
||||
|
||||
assertThat(sir).hasSize(1);
|
||||
assertThat(sir.get(0).xmlResId).isEqualTo(R.xml.location_services);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyChar;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
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 org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LocationSettingsFooterPreferenceControllerTest {
|
||||
|
||||
private static final int TEST_RES_ID = 1234;
|
||||
private static final String TEST_TEXT = "text";
|
||||
private static final String PREFERENCE_KEY = "location_footer";
|
||||
|
||||
private Context mContext;
|
||||
private LocationSettingsFooterPreferenceController mController;
|
||||
private Lifecycle mLifecycle;
|
||||
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
@Mock
|
||||
private FooterPreference mFooterPreference;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
|
||||
@Before
|
||||
public void setUp() throws NameNotFoundException {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
|
||||
LifecycleOwner lifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(lifecycleOwner);
|
||||
LocationSettings locationSettings = spy(new LocationSettings());
|
||||
when(locationSettings.getSettingsLifecycle()).thenReturn(mLifecycle);
|
||||
|
||||
mController = spy(new LocationSettingsFooterPreferenceController(mContext, PREFERENCE_KEY));
|
||||
mController.init(locationSettings);
|
||||
|
||||
when(mPreferenceScreen.findPreference(PREFERENCE_KEY)).thenReturn(mFooterPreference);
|
||||
when(mPackageManager.getResourcesForApplication(any(ApplicationInfo.class)))
|
||||
.thenReturn(mResources);
|
||||
when(mResources.getString(TEST_RES_ID)).thenReturn(TEST_TEXT);
|
||||
mController.displayPreference(mPreferenceScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_hasValidFooter_returnsTrue() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the footer even without the injected string.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_noSystemApp_returnsTrue() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the footer even without the injected string.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_noRequiredMetadata_returnsTrue() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ false));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_setTitle() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
mController.updateState(mFooterPreference);
|
||||
ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mFooterPreference).setTitle(title.capture());
|
||||
assertThat(title.getValue()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_off_setTitle() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
mController.updateState(mFooterPreference);
|
||||
verify(mFooterPreference).setTitle(any());
|
||||
mController.onLocationModeChanged(/* mode= */ 0, /* restricted= */ false);
|
||||
ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mFooterPreference, times(2)).setTitle(title.capture());
|
||||
|
||||
assertThat(title.getValue().toString()).contains(
|
||||
Html.fromHtml(mContext.getString(
|
||||
R.string.location_settings_footer_location_off)).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_on_setTitle() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
mController.updateState(mFooterPreference);
|
||||
verify(mFooterPreference).setTitle(any());
|
||||
mController.onLocationModeChanged(/* mode= */ 1, /* restricted= */ false);
|
||||
ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mFooterPreference, times(2)).setTitle(title.capture());
|
||||
assertThat(title.getValue().toString()).doesNotContain(
|
||||
Html.fromHtml(mContext.getString(
|
||||
R.string.location_settings_footer_location_off)).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_notSystemApp_ignore() {
|
||||
final List<ResolveInfo> testResolveInfos = new ArrayList<>();
|
||||
testResolveInfos.add(
|
||||
getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
|
||||
when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
|
||||
.thenReturn(testResolveInfos);
|
||||
mController.updateState(mFooterPreference);
|
||||
verify(mFooterPreference, never()).setTitle(anyChar());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ResolveInfo object for testing
|
||||
* @param isSystemApp If true, the application is a system app.
|
||||
* @param hasRequiredMetaData If true, the broadcast receiver has a valid value for
|
||||
* {@link LocationManager#METADATA_SETTINGS_FOOTER_STRING}
|
||||
*/
|
||||
private ResolveInfo getTestResolveInfo(boolean isSystemApp, boolean hasRequiredMetaData) {
|
||||
ResolveInfo testResolveInfo = new ResolveInfo();
|
||||
ApplicationInfo testAppInfo = new ApplicationInfo();
|
||||
if (isSystemApp) {
|
||||
testAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
}
|
||||
ActivityInfo testActivityInfo = new ActivityInfo();
|
||||
testActivityInfo.name = "TestActivityName";
|
||||
testActivityInfo.packageName = "TestPackageName";
|
||||
testActivityInfo.applicationInfo = testAppInfo;
|
||||
if (hasRequiredMetaData) {
|
||||
testActivityInfo.metaData = new Bundle();
|
||||
testActivityInfo.metaData.putInt(
|
||||
LocationManager.METADATA_SETTINGS_FOOTER_STRING, TEST_RES_ID);
|
||||
}
|
||||
testResolveInfo.activityInfo = testActivityInfo;
|
||||
return testResolveInfo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.settings.location;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
public class LocationSettingsTest {
|
||||
|
||||
@Mock
|
||||
private SettingsActivity mActivity;
|
||||
@Mock
|
||||
private SettingsMainSwitchBar mSwitchBar;
|
||||
@Mock
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
private Context mContext;
|
||||
private LocationSettings mLocationSettings;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mLocationSettings = spy(new LocationSettings());
|
||||
doReturn(mActivity).when(mLocationSettings).getActivity();
|
||||
doReturn(mContext).when(mLocationSettings).getContext();
|
||||
doReturn(mContentResolver).when(mActivity).getContentResolver();
|
||||
when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onActivityCreated_shouldShowSwitchBar() {
|
||||
mLocationSettings.onActivityCreated(new Bundle());
|
||||
|
||||
verify(mSwitchBar).show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.android.settings.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceMetadata;
|
||||
import androidx.slice.SliceProvider;
|
||||
import androidx.slice.core.SliceAction;
|
||||
import androidx.slice.widget.SliceLiveData;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LocationSliceTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
// Set-up specs for SliceMetadata.
|
||||
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLocationSlice_correctSliceContent() {
|
||||
final Slice LocationSlice = new LocationSlice(mContext).getSlice();
|
||||
|
||||
final SliceMetadata metadata = SliceMetadata.from(mContext, LocationSlice);
|
||||
assertThat(metadata.getTitle()).isEqualTo(
|
||||
mContext.getString(R.string.location_settings_title));
|
||||
|
||||
final List<SliceAction> toggles = metadata.getToggles();
|
||||
assertThat(toggles).isEmpty();
|
||||
|
||||
final SliceAction primaryAction = metadata.getPrimaryAction();
|
||||
final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
|
||||
com.android.internal.R.drawable.ic_signal_location);
|
||||
assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
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.content.Context;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.widget.Switch;
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
|
||||
import com.android.settings.widget.SettingsMainSwitchBar;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class LocationSwitchBarControllerTest {
|
||||
|
||||
@Mock
|
||||
private SettingsMainSwitchBar mSwitchBar;
|
||||
@Mock
|
||||
private Switch mSwitch;
|
||||
@Mock
|
||||
private LocationEnabler mEnabler;
|
||||
|
||||
private Context mContext;
|
||||
private LocationSwitchBarController mController;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
private Lifecycle mLifecycle;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
ReflectionHelpers.setField(mSwitchBar, "mSwitch", mSwitch);
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = new Lifecycle(mLifecycleOwner);
|
||||
mController = spy(new LocationSwitchBarController(mContext, mSwitchBar, mLifecycle));
|
||||
ReflectionHelpers.setField(mController, "mLocationEnabler", mEnabler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_shouldAddOnSwitchChangeListener() {
|
||||
mController.onStart();
|
||||
|
||||
verify(mSwitchBar).addOnSwitchChangeListener(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_shouldRemoveOnSwitchChangeListener() {
|
||||
mController.onStart();
|
||||
mController.onStop();
|
||||
|
||||
verify(mSwitchBar).removeOnSwitchChangeListener(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSwitchChanged_switchChecked_shouldSetLocationEnabled() {
|
||||
mController.onCheckedChanged(mSwitch, true);
|
||||
|
||||
verify(mEnabler).setLocationEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSwitchChanged_switchUnchecked_shouldSetLocationDisabled() {
|
||||
mController.onCheckedChanged(mSwitch, false);
|
||||
|
||||
verify(mEnabler).setLocationEnabled(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_hasEnforcedAdmin_shouldDisableSwitchByAdmin() {
|
||||
final RestrictedLockUtils.EnforcedAdmin admin =
|
||||
mock(RestrictedLockUtils.EnforcedAdmin.class);
|
||||
doReturn(admin).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mSwitchBar).setDisabledByAdmin(admin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_Restricted_shouldDisableSwitchByAdmin() {
|
||||
final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtils.EnforcedAdmin
|
||||
.createDefaultEnforcedAdminWithRestriction(UserManager.DISALLOW_SHARE_LOCATION);
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, true);
|
||||
|
||||
verify(mSwitchBar).setDisabledByAdmin(admin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_Restricted_shouldDisableSwitch() {
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(true).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mSwitchBar).setEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_notRestricted_shouldEnableSwitch() {
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mSwitchBar).setEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_locationOn_shouldCheckSwitch() {
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
when(mSwitchBar.isChecked()).thenReturn(false);
|
||||
doReturn(true).when(mEnabler).isEnabled(anyInt());
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_BATTERY_SAVING, false);
|
||||
|
||||
verify(mSwitchBar).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onLocationModeChanged_locationOff_shouldUncheckSwitch() {
|
||||
doReturn(null).when(mEnabler).getShareLocationEnforcedAdmin(anyInt());
|
||||
doReturn(false).when(mEnabler).hasShareLocationRestriction(anyInt());
|
||||
when(mSwitchBar.isChecked()).thenReturn(true);
|
||||
|
||||
mController.onLocationModeChanged(Settings.Secure.LOCATION_MODE_OFF, false);
|
||||
|
||||
verify(mSwitchBar).setChecked(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
import com.android.settingslib.applications.RecentAppOpsAccess;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowDeviceConfig.class})
|
||||
public class RecentLocationAccessPreferenceControllerTest {
|
||||
private static final String PREFERENCE_KEY = "test_preference_key";
|
||||
@Mock
|
||||
private PreferenceCategory mLayoutPreference;
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private DashboardFragment mDashboardFragment;
|
||||
@Mock
|
||||
private RecentAppOpsAccess mRecentLocationApps;
|
||||
|
||||
private Context mContext;
|
||||
private RecentLocationAccessPreferenceController mController;
|
||||
private View mAppEntitiesHeaderView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mController = spy(
|
||||
new RecentLocationAccessPreferenceController(mContext, PREFERENCE_KEY,
|
||||
mRecentLocationApps));
|
||||
mController.init(mDashboardFragment);
|
||||
final String key = mController.getPreferenceKey();
|
||||
mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate(
|
||||
com.android.settingslib.widget.entityheader.R.layout.app_entities_header, null /* root */);
|
||||
when(mScreen.findPreference(key)).thenReturn(mLayoutPreference);
|
||||
when(mLayoutPreference.getKey()).thenReturn(key);
|
||||
when(mLayoutPreference.getContext()).thenReturn(mContext);
|
||||
when(mDashboardFragment.getContext()).thenReturn(mContext);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowDeviceConfig.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_shouldReturnTrue() {
|
||||
assertThat(mController.isAvailable()).isEqualTo(true);
|
||||
}
|
||||
|
||||
/** Verifies the title text, details text are correct, and the click listener is set. */
|
||||
@Test
|
||||
@Ignore
|
||||
public void updateState_whenAppListIsEmpty_shouldDisplayTitleTextAndDetailsText() {
|
||||
doReturn(new ArrayList<>()).when(mRecentLocationApps).getAppListSorted(false);
|
||||
mController.displayPreference(mScreen);
|
||||
mController.updateState(mLayoutPreference);
|
||||
|
||||
final TextView title = mAppEntitiesHeaderView.findViewById(R.id.header_title);
|
||||
assertThat(title.getText()).isEqualTo(
|
||||
mContext.getText(R.string.location_category_recent_location_access));
|
||||
final TextView details = mAppEntitiesHeaderView
|
||||
.findViewById(com.android.settingslib.widget.entityheader.R.id.header_details);
|
||||
assertThat(details.getText()).isEqualTo(
|
||||
mContext.getText(R.string.location_recent_location_access_view_details));
|
||||
assertThat(details.hasOnClickListeners()).isTrue();
|
||||
}
|
||||
|
||||
/** Verifies the title text, details text are correct, and the click listener is set. */
|
||||
@Test
|
||||
public void updateState_showSystemAccess() {
|
||||
doReturn(ImmutableList.of(
|
||||
new RecentAppOpsAccess.Access("app", UserHandle.CURRENT, null, "app", "", 0)))
|
||||
.when(mRecentLocationApps).getAppListSorted(false);
|
||||
doReturn(new ArrayList<>()).when(mRecentLocationApps).getAppListSorted(true);
|
||||
mController.displayPreference(mScreen);
|
||||
mController.updateState(mLayoutPreference);
|
||||
verify(mLayoutPreference).addPreference(Mockito.any());
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContext.getContentResolver(), Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, 1);
|
||||
verify(mLayoutPreference, Mockito.times(1)).addPreference(Mockito.any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.location;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
|
||||
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
import com.android.settings.testutils.shadow.ShadowUserManager;
|
||||
import com.android.settingslib.location.RecentLocationApps;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowDeviceConfig.class, ShadowUserManager.class})
|
||||
public class RecentLocationRequestPreferenceControllerTest {
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
@Mock
|
||||
private PreferenceCategory mCategory;
|
||||
private Context mContext;
|
||||
private RecentLocationRequestPreferenceController mController;
|
||||
private ShadowUserManager mUserManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = spy(
|
||||
new RecentLocationRequestPreferenceController(mContext, "key"));
|
||||
when(mCategory.getContext()).thenReturn(mContext);
|
||||
when(mScreen.findPreference("key")).thenReturn(mCategory);
|
||||
mUserManager = ShadowUserManager.getShadow();
|
||||
mController.mRecentLocationApps = spy(new RecentLocationApps(mContext));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowDeviceConfig.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_whenAppListMoreThanThree_shouldDisplayTopThreeApps() {
|
||||
final List<RecentLocationApps.Request> requests = createMockRequest(6);
|
||||
when(mController.mRecentLocationApps.getAppListSorted(false)).thenReturn(requests);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mCategory, times(3)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_whenAppListMoreThanThree_showSystem() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
|
||||
SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_SMALL_ENABLED,
|
||||
Boolean.toString(true),
|
||||
true);
|
||||
when(mController.mRecentLocationApps.getAppListSorted(false))
|
||||
.thenReturn(createMockRequest(2));
|
||||
when(mController.mRecentLocationApps.getAppListSorted(true))
|
||||
.thenReturn(createMockRequest(3));
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
verify(mCategory, times(2)).addPreference(any());
|
||||
|
||||
Settings.Secure.putInt(
|
||||
mContext.getContentResolver(),
|
||||
Settings.Secure.LOCATION_SHOW_SYSTEM_OPS,
|
||||
1);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
verify(mCategory, times(5)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_workProfile_shouldShowOnlyWorkProfileApps() {
|
||||
final List<RecentLocationApps.Request> requests = createMockRequest(6);
|
||||
when(mController.mRecentLocationApps.getAppListSorted(false)).thenReturn(requests);
|
||||
mController.setProfileType(ProfileSelectFragment.ProfileType.WORK);
|
||||
final Set<Integer> profileIds = new HashSet<>();
|
||||
profileIds.add(4);
|
||||
profileIds.add(5);
|
||||
mUserManager.setManagedProfiles(profileIds);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
// contains userId 4 and userId 5
|
||||
verify(mCategory, times(2)).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_Personal_shouldShowOnlyPersonalApps() {
|
||||
final List<RecentLocationApps.Request> requests = createMockRequest(6);
|
||||
when(mController.mRecentLocationApps.getAppListSorted(false)).thenReturn(requests);
|
||||
mController.setProfileType(ProfileSelectFragment.ProfileType.PERSONAL);
|
||||
final Set<Integer> profileIds = new HashSet<>();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
profileIds.add(i);
|
||||
}
|
||||
mUserManager.setManagedProfiles(profileIds);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
// contains userId 4 and userId 5
|
||||
verify(mCategory, times(2)).addPreference(any());
|
||||
}
|
||||
|
||||
private List<RecentLocationApps.Request> createMockRequest(int count) {
|
||||
final List<RecentLocationApps.Request> requests = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final Drawable icon = mock(Drawable.class);
|
||||
// Add mock accesses
|
||||
final RecentLocationApps.Request request = new RecentLocationApps.Request(
|
||||
"packageName", UserHandle.of(i), icon,
|
||||
"appTitle" + i, false, "appSummary" + i, 1000 - i);
|
||||
requests.add(request);
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.location;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.LocationManager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.utils.StringUtil;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class TopLevelLocationPreferenceControllerTest {
|
||||
private static final String PREFERENCE_KEY = "top_level_location";
|
||||
private Context mContext;
|
||||
private TopLevelLocationPreferenceController mController;
|
||||
private LocationManager mLocationManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new TopLevelLocationPreferenceController(mContext, PREFERENCE_KEY);
|
||||
mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_byDefault_shouldReturnTrue() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationIsOff_shouldReturnStringForOff() {
|
||||
mLocationManager.setLocationEnabledForUser(false, android.os.Process.myUserHandle());
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
mContext.getString(R.string.location_settings_summary_location_off));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationIsOn_shouldPreservePreviousText() {
|
||||
final int locationAppCount = 5;
|
||||
// Retrieve summary text once.
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
mController.setLocationAppCount(locationAppCount);
|
||||
mController.getSummary();
|
||||
// Turn off location.
|
||||
mLocationManager.setLocationEnabledForUser(false, android.os.Process.myUserHandle());
|
||||
// Turn on location again and check if the previous summary text is still cached.
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
StringUtil.getIcuPluralsString(mContext, locationAppCount,
|
||||
R.string.location_settings_summary_location_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationAppCountIsOne_shouldShowSingularString() {
|
||||
final int locationAppCount = 1;
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
mController.setLocationAppCount(locationAppCount);
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
StringUtil.getIcuPluralsString(mContext, locationAppCount,
|
||||
R.string.location_settings_summary_location_on));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_whenLocationAppCountIsGreaterThanOne_shouldShowPluralString() {
|
||||
final int locationAppCount = 5;
|
||||
mLocationManager.setLocationEnabledForUser(true, android.os.Process.myUserHandle());
|
||||
mController.setLocationAppCount(locationAppCount);
|
||||
assertThat(mController.getSummary()).isEqualTo(
|
||||
StringUtil.getIcuPluralsString(mContext, locationAppCount,
|
||||
R.string.location_settings_summary_location_on));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.location;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.net.wifi.WifiManager;
|
||||
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class WifiScanningPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
private SwitchPreference mPreference;
|
||||
@Mock
|
||||
private WifiManager mWifiManager;
|
||||
|
||||
private Context mContext;
|
||||
private ContentResolver mContentResolver;
|
||||
private WifiScanningPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContentResolver = RuntimeEnvironment.application.getContentResolver();
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
|
||||
|
||||
mController = new WifiScanningPreferenceController(mContext);
|
||||
when(mPreference.getKey()).thenReturn(mController.getPreferenceKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_wifiScanningEnabled_shouldCheckedPreference() {
|
||||
when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_wifiScanningDisabled_shouldUncheckedPreference() {
|
||||
when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
|
||||
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_checked_shouldEnableWifiScanning() {
|
||||
when(mPreference.isChecked()).thenReturn(true);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mWifiManager).setScanAlwaysAvailable(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlePreferenceTreeClick_unchecked_shouldDisableWifiScanning() {
|
||||
when(mPreference.isChecked()).thenReturn(false);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mWifiManager).setScanAlwaysAvailable(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user