1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.location; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import static org.mockito.ArgumentMatchers.any; 21 import static org.mockito.ArgumentMatchers.anyChar; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.Mockito.never; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.ActivityInfo; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.PackageManager.NameNotFoundException; 35 import android.content.pm.ResolveInfo; 36 import android.content.res.Resources; 37 import android.location.LocationManager; 38 import android.os.Bundle; 39 import android.text.Html; 40 41 import androidx.lifecycle.LifecycleOwner; 42 import androidx.preference.PreferenceScreen; 43 44 import com.android.settings.R; 45 import com.android.settingslib.core.lifecycle.Lifecycle; 46 import com.android.settingslib.widget.FooterPreference; 47 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.ArgumentCaptor; 52 import org.mockito.Mock; 53 import org.mockito.MockitoAnnotations; 54 import org.robolectric.RobolectricTestRunner; 55 import org.robolectric.RuntimeEnvironment; 56 57 import java.util.ArrayList; 58 import java.util.List; 59 60 @RunWith(RobolectricTestRunner.class) 61 public class LocationSettingsFooterPreferenceControllerTest { 62 63 private static final int TEST_RES_ID = 1234; 64 private static final String TEST_TEXT = "text"; 65 private static final String PREFERENCE_KEY = "location_footer"; 66 67 private Context mContext; 68 private LocationSettingsFooterPreferenceController mController; 69 private Lifecycle mLifecycle; 70 71 @Mock 72 private PreferenceScreen mPreferenceScreen; 73 @Mock 74 private FooterPreference mFooterPreference; 75 @Mock 76 private PackageManager mPackageManager; 77 @Mock 78 private Resources mResources; 79 80 @Before setUp()81 public void setUp() throws NameNotFoundException { 82 MockitoAnnotations.initMocks(this); 83 mContext = spy(RuntimeEnvironment.application); 84 when(mContext.getPackageManager()).thenReturn(mPackageManager); 85 86 LifecycleOwner lifecycleOwner = () -> mLifecycle; 87 mLifecycle = new Lifecycle(lifecycleOwner); 88 LocationSettings locationSettings = spy(new LocationSettings()); 89 when(locationSettings.getSettingsLifecycle()).thenReturn(mLifecycle); 90 91 mController = spy(new LocationSettingsFooterPreferenceController(mContext, PREFERENCE_KEY)); 92 mController.init(locationSettings); 93 94 when(mPreferenceScreen.findPreference(PREFERENCE_KEY)).thenReturn(mFooterPreference); 95 when(mPackageManager.getResourcesForApplication(any(ApplicationInfo.class))) 96 .thenReturn(mResources); 97 when(mResources.getString(TEST_RES_ID)).thenReturn(TEST_TEXT); 98 mController.displayPreference(mPreferenceScreen); 99 } 100 101 @Test isAvailable_hasValidFooter_returnsTrue()102 public void isAvailable_hasValidFooter_returnsTrue() { 103 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 104 testResolveInfos.add( 105 getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true)); 106 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 107 .thenReturn(testResolveInfos); 108 109 assertThat(mController.isAvailable()).isTrue(); 110 } 111 112 /** 113 * Display the footer even without the injected string. 114 */ 115 @Test isAvailable_noSystemApp_returnsTrue()116 public void isAvailable_noSystemApp_returnsTrue() { 117 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 118 testResolveInfos.add( 119 getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true)); 120 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 121 .thenReturn(testResolveInfos); 122 assertThat(mController.isAvailable()).isTrue(); 123 } 124 125 /** 126 * Display the footer even without the injected string. 127 */ 128 @Test isAvailable_noRequiredMetadata_returnsTrue()129 public void isAvailable_noRequiredMetadata_returnsTrue() { 130 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 131 testResolveInfos.add( 132 getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ false)); 133 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 134 .thenReturn(testResolveInfos); 135 assertThat(mController.isAvailable()).isTrue(); 136 } 137 138 @Test updateState_setTitle()139 public void updateState_setTitle() { 140 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 141 testResolveInfos.add( 142 getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true)); 143 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 144 .thenReturn(testResolveInfos); 145 mController.updateState(mFooterPreference); 146 ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class); 147 verify(mFooterPreference).setTitle(title.capture()); 148 assertThat(title.getValue()).isNotNull(); 149 } 150 151 @Test onLocationModeChanged_off_setTitle()152 public void onLocationModeChanged_off_setTitle() { 153 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 154 testResolveInfos.add( 155 getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true)); 156 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 157 .thenReturn(testResolveInfos); 158 mController.updateState(mFooterPreference); 159 verify(mFooterPreference).setTitle(any()); 160 mController.onLocationModeChanged(/* mode= */ 0, /* restricted= */ false); 161 ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class); 162 verify(mFooterPreference, times(2)).setTitle(title.capture()); 163 164 assertThat(title.getValue().toString()).contains( 165 Html.fromHtml(mContext.getString( 166 R.string.location_settings_footer_location_off)).toString()); 167 } 168 169 @Test onLocationModeChanged_on_setTitle()170 public void onLocationModeChanged_on_setTitle() { 171 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 172 testResolveInfos.add( 173 getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true)); 174 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 175 .thenReturn(testResolveInfos); 176 mController.updateState(mFooterPreference); 177 verify(mFooterPreference).setTitle(any()); 178 mController.onLocationModeChanged(/* mode= */ 1, /* restricted= */ false); 179 ArgumentCaptor<CharSequence> title = ArgumentCaptor.forClass(CharSequence.class); 180 verify(mFooterPreference, times(2)).setTitle(title.capture()); 181 assertThat(title.getValue().toString()).doesNotContain( 182 Html.fromHtml(mContext.getString( 183 R.string.location_settings_footer_location_off)).toString()); 184 } 185 186 @Test updateState_notSystemApp_ignore()187 public void updateState_notSystemApp_ignore() { 188 final List<ResolveInfo> testResolveInfos = new ArrayList<>(); 189 testResolveInfos.add( 190 getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true)); 191 when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt())) 192 .thenReturn(testResolveInfos); 193 mController.updateState(mFooterPreference); 194 verify(mFooterPreference, never()).setTitle(anyChar()); 195 } 196 197 /** 198 * Returns a ResolveInfo object for testing 199 * @param isSystemApp If true, the application is a system app. 200 * @param hasRequiredMetaData If true, the broadcast receiver has a valid value for 201 * {@link LocationManager#METADATA_SETTINGS_FOOTER_STRING} 202 */ getTestResolveInfo(boolean isSystemApp, boolean hasRequiredMetaData)203 private ResolveInfo getTestResolveInfo(boolean isSystemApp, boolean hasRequiredMetaData) { 204 ResolveInfo testResolveInfo = new ResolveInfo(); 205 ApplicationInfo testAppInfo = new ApplicationInfo(); 206 if (isSystemApp) { 207 testAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM; 208 } 209 ActivityInfo testActivityInfo = new ActivityInfo(); 210 testActivityInfo.name = "TestActivityName"; 211 testActivityInfo.packageName = "TestPackageName"; 212 testActivityInfo.applicationInfo = testAppInfo; 213 if (hasRequiredMetaData) { 214 testActivityInfo.metaData = new Bundle(); 215 testActivityInfo.metaData.putInt( 216 LocationManager.METADATA_SETTINGS_FOOTER_STRING, TEST_RES_ID); 217 } 218 testResolveInfo.activityInfo = testActivityInfo; 219 return testResolveInfo; 220 } 221 } 222