1 /* 2 * Copyright (C) 2018 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 17 package com.android.settings.slices; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.anyInt; 22 import static org.mockito.ArgumentMatchers.eq; 23 import static org.mockito.Mockito.doReturn; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.verify; 26 27 import android.app.PendingIntent; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.res.Resources; 32 import android.net.Uri; 33 import android.provider.Settings; 34 import android.provider.SettingsSlicesContract; 35 import android.util.Pair; 36 37 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 38 import com.android.settings.R; 39 import com.android.settings.core.BasePreferenceController; 40 import com.android.settings.testutils.FakeFeatureFactory; 41 import com.android.settings.testutils.FakeSliderController; 42 import com.android.settings.testutils.FakeToggleController; 43 import com.android.settings.testutils.FakeUnavailablePreferenceController; 44 import com.android.settings.testutils.SettingsRobolectricTestRunner; 45 import com.android.settings.testutils.SliceTester; 46 47 import org.junit.Before; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 import org.mockito.ArgumentCaptor; 51 import org.robolectric.RuntimeEnvironment; 52 53 import androidx.slice.Slice; 54 import androidx.slice.SliceProvider; 55 import androidx.slice.widget.SliceLiveData; 56 57 @RunWith(SettingsRobolectricTestRunner.class) 58 public class SliceBuilderUtilsTest { 59 60 private final String KEY = "KEY"; 61 private final String TITLE = "title"; 62 private final String SUMMARY = "summary"; 63 private final String SCREEN_TITLE = "screen title"; 64 private final String KEYWORDS = "a, b, c"; 65 private final String FRAGMENT_NAME = "fragment name"; 66 private final int ICON = 1234; // I declare a thumb war 67 private final Uri URI = Uri.parse("content://com.android.settings.slices/test"); 68 private final Class TOGGLE_CONTROLLER = FakeToggleController.class; 69 private final Class SLIDER_CONTROLLER = FakeSliderController.class; 70 private final Class CONTEXT_CONTROLLER = FakeContextOnlyPreferenceController.class; 71 72 private final String INTENT_PATH = SettingsSlicesContract.PATH_SETTING_INTENT + "/" + KEY; 73 private final String ACTION_PATH = SettingsSlicesContract.PATH_SETTING_ACTION + "/" + KEY; 74 75 private Context mContext; 76 private FakeFeatureFactory mFeatureFactory; 77 private ArgumentCaptor<Pair<Integer, Object>> mLoggingArgumentCatpor; 78 79 @Before setUp()80 public void setUp() { 81 mContext = spy(RuntimeEnvironment.application); 82 mFeatureFactory = FakeFeatureFactory.setupForTest(); 83 mLoggingArgumentCatpor = ArgumentCaptor.forClass(Pair.class); 84 85 // Prevent crash in SliceMetadata. 86 Resources resources = spy(mContext.getResources()); 87 doReturn(60).when(resources).getDimensionPixelSize(anyInt()); 88 doReturn(resources).when(mContext).getResources(); 89 90 // Set-up specs for SliceMetadata. 91 SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); 92 } 93 94 @Test buildIntentSlice_returnsMatchingSlice()95 public void buildIntentSlice_returnsMatchingSlice() { 96 final SliceData sliceData = getDummyData(CONTEXT_CONTROLLER, SliceData.SliceType.INTENT); 97 final Slice slice = SliceBuilderUtils.buildSlice(mContext, sliceData); 98 99 SliceTester.testSettingsIntentSlice(mContext, slice, sliceData); 100 } 101 102 @Test buildToggleSlice_returnsMatchingSlice()103 public void buildToggleSlice_returnsMatchingSlice() { 104 final SliceData dummyData = getDummyData(TOGGLE_CONTROLLER, SliceData.SliceType.SWITCH); 105 106 final Slice slice = SliceBuilderUtils.buildSlice(mContext, dummyData); 107 verify(mFeatureFactory.metricsFeatureProvider) 108 .action(eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_SLICE_REQUESTED), 109 mLoggingArgumentCatpor.capture()); 110 final Pair<Integer, Object> capturedLoggingPair = mLoggingArgumentCatpor.getValue(); 111 112 assertThat(capturedLoggingPair.first) 113 .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); 114 assertThat(capturedLoggingPair.second) 115 .isEqualTo(dummyData.getKey()); 116 SliceTester.testSettingsToggleSlice(mContext, slice, dummyData); 117 } 118 119 @Test buildSliderSlice_returnsMatchingSlice()120 public void buildSliderSlice_returnsMatchingSlice() { 121 final SliceData data = getDummyData(SLIDER_CONTROLLER, SliceData.SliceType.SLIDER); 122 123 124 final Slice slice = SliceBuilderUtils.buildSlice(mContext, data); 125 verify(mFeatureFactory.metricsFeatureProvider) 126 .action(eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_SLICE_REQUESTED), 127 mLoggingArgumentCatpor.capture()); 128 final Pair<Integer, Object> capturedLoggingPair = mLoggingArgumentCatpor.getValue(); 129 130 assertThat(capturedLoggingPair.first) 131 .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); 132 assertThat(capturedLoggingPair.second) 133 .isEqualTo(data.getKey()); 134 SliceTester.testSettingsSliderSlice(mContext, slice, data); 135 } 136 137 @Test testUriBuilder_oemAuthority_intentPath_returnsValidSliceUri()138 public void testUriBuilder_oemAuthority_intentPath_returnsValidSliceUri() { 139 final Uri expectedUri = new Uri.Builder() 140 .scheme(ContentResolver.SCHEME_CONTENT) 141 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 142 .appendPath(INTENT_PATH) 143 .build(); 144 145 final Uri actualUri = SliceBuilderUtils.getUri(INTENT_PATH, false); 146 147 assertThat(actualUri).isEqualTo(expectedUri); 148 } 149 150 @Test testUriBuilder_oemAuthority_actionPath_returnsValidSliceUri()151 public void testUriBuilder_oemAuthority_actionPath_returnsValidSliceUri() { 152 final Uri expectedUri = new Uri.Builder() 153 .scheme(ContentResolver.SCHEME_CONTENT) 154 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 155 .appendPath(ACTION_PATH) 156 .build(); 157 158 final Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, false); 159 160 assertThat(actualUri).isEqualTo(expectedUri); 161 } 162 163 @Test testUriBuilder_platformAuthority_intentPath_returnsValidSliceUri()164 public void testUriBuilder_platformAuthority_intentPath_returnsValidSliceUri() { 165 final Uri expectedUri = new Uri.Builder() 166 .scheme(ContentResolver.SCHEME_CONTENT) 167 .authority(SettingsSlicesContract.AUTHORITY) 168 .appendPath(ACTION_PATH) 169 .build(); 170 171 final Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, true); 172 173 assertThat(actualUri).isEqualTo(expectedUri); 174 } 175 176 @Test testUriBuilder_platformAuthority_actionPath_returnsValidSliceUri()177 public void testUriBuilder_platformAuthority_actionPath_returnsValidSliceUri() { 178 final Uri expectedUri = new Uri.Builder() 179 .scheme(ContentResolver.SCHEME_CONTENT) 180 .authority(SettingsSlicesContract.AUTHORITY) 181 .appendPath(ACTION_PATH) 182 .build(); 183 184 final Uri actualUri = SliceBuilderUtils.getUri(ACTION_PATH, true); 185 186 assertThat(actualUri).isEqualTo(expectedUri); 187 } 188 189 @Test testGetPreferenceController_buildsMatchingController()190 public void testGetPreferenceController_buildsMatchingController() { 191 final BasePreferenceController controller = 192 SliceBuilderUtils.getPreferenceController(mContext, getDummyData()); 193 194 assertThat(controller).isInstanceOf(FakeToggleController.class); 195 } 196 197 @Test testGetPreferenceController_contextOnly_buildsMatchingController()198 public void testGetPreferenceController_contextOnly_buildsMatchingController() { 199 final BasePreferenceController controller = 200 SliceBuilderUtils.getPreferenceController(mContext, 201 getDummyData(CONTEXT_CONTROLLER, 0)); 202 203 assertThat(controller).isInstanceOf(FakeContextOnlyPreferenceController.class); 204 } 205 206 @Test getDynamicSummary_returnsScreenTitle()207 public void getDynamicSummary_returnsScreenTitle() { 208 final SliceData data = getDummyData(); 209 final FakePreferenceController controller = new FakePreferenceController(mContext, KEY); 210 211 final CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data); 212 213 assertThat(summary).isEqualTo(data.getScreenTitle()); 214 } 215 216 @Test getDynamicSummary_noScreenTitle_returnsPrefControllerSummary()217 public void getDynamicSummary_noScreenTitle_returnsPrefControllerSummary() { 218 final SliceData data = getDummyData("", ""); 219 final FakePreferenceController controller = spy( 220 new FakePreferenceController(mContext, KEY)); 221 final String controllerSummary = "new_Summary"; 222 doReturn(controllerSummary).when(controller).getSummary(); 223 224 final CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data); 225 226 assertThat(summary).isEqualTo(controllerSummary); 227 } 228 229 @Test getDynamicSummary_screenTitleMatchesTitle_returnsPrefControllerSummary()230 public void getDynamicSummary_screenTitleMatchesTitle_returnsPrefControllerSummary() { 231 final SliceData data = getDummyData("", TITLE); 232 final FakePreferenceController controller = spy( 233 new FakePreferenceController(mContext, KEY)); 234 final String controllerSummary = "new_Summary"; 235 doReturn(controllerSummary).when(controller).getSummary(); 236 237 final CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data); 238 239 assertThat(summary).isEqualTo(controllerSummary); 240 } 241 242 @Test getDynamicSummary_emptyScreenTitle_emptyControllerSummary_returnsEmptyString()243 public void getDynamicSummary_emptyScreenTitle_emptyControllerSummary_returnsEmptyString() { 244 final SliceData data = getDummyData(null, null); 245 final FakePreferenceController controller = new FakePreferenceController(mContext, KEY); 246 final CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data); 247 248 assertThat(summary).isEqualTo(""); 249 } 250 251 @Test 252 public void getDynamicSummary_emptyScreenTitle_placeHolderControllerSummary_returnsEmptyString()253 getDynamicSummary_emptyScreenTitle_placeHolderControllerSummary_returnsEmptyString() { 254 final SliceData data = getDummyData(mContext.getString(R.string.summary_placeholder), null); 255 final FakePreferenceController controller = new FakePreferenceController(mContext, KEY); 256 final CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data); 257 258 assertThat(summary).isEqualTo(""); 259 } 260 261 @Test getDynamicSummary_screenTitleAndControllerPlaceholder_returnsSliceEmptyString()262 public void getDynamicSummary_screenTitleAndControllerPlaceholder_returnsSliceEmptyString() { 263 final String summaryPlaceholder = mContext.getString(R.string.summary_placeholder); 264 final SliceData data = getDummyData(summaryPlaceholder, summaryPlaceholder); 265 final FakePreferenceController controller = spy( 266 new FakePreferenceController(mContext, KEY)); 267 doReturn(summaryPlaceholder).when(controller).getSummary(); 268 269 CharSequence summary = SliceBuilderUtils.getSubtitleText(mContext, controller, data); 270 271 assertThat(summary).isEqualTo(""); 272 } 273 274 @Test getPathData_splitsIntentUri()275 public void getPathData_splitsIntentUri() { 276 final Uri uri = new Uri.Builder() 277 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 278 .appendPath(SettingsSlicesContract.PATH_SETTING_INTENT) 279 .appendPath(KEY) 280 .build(); 281 282 final Pair<Boolean, String> pathPair = SliceBuilderUtils.getPathData(uri); 283 284 assertThat(pathPair.first).isTrue(); 285 assertThat(pathPair.second).isEqualTo(KEY); 286 } 287 288 @Test getPathData_splitsActionUri()289 public void getPathData_splitsActionUri() { 290 final Uri uri = new Uri.Builder() 291 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 292 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) 293 .appendPath(KEY) 294 .build(); 295 296 final Pair<Boolean, String> pathPair = SliceBuilderUtils.getPathData(uri); 297 298 assertThat(pathPair.first).isFalse(); 299 assertThat(pathPair.second).isEqualTo(KEY); 300 } 301 302 @Test getPathData_noKey_returnsNull()303 public void getPathData_noKey_returnsNull() { 304 final Uri uri = new Uri.Builder() 305 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 306 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) 307 .build(); 308 309 final Pair<Boolean, String> pathPair = SliceBuilderUtils.getPathData(uri); 310 311 assertThat(pathPair).isNull(); 312 } 313 314 @Test getPathData_extraArg_returnsNull()315 public void getPathData_extraArg_returnsNull() { 316 final Uri uri = new Uri.Builder() 317 .authority(SettingsSliceProvider.SLICE_AUTHORITY) 318 .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION) 319 .appendPath(KEY) 320 .appendPath(KEY) 321 .build(); 322 323 final Pair<Boolean, String> pathPair = SliceBuilderUtils.getPathData(uri); 324 325 assertThat(pathPair.first).isFalse(); 326 assertThat(pathPair.second).isEqualTo(KEY + "/" + KEY); 327 } 328 329 @Test testUnsupportedSlice_validTitleSummary()330 public void testUnsupportedSlice_validTitleSummary() { 331 final SliceData data = getDummyData(FakeUnavailablePreferenceController.class, 332 SliceData.SliceType.SWITCH); 333 Settings.Global.putInt(mContext.getContentResolver(), 334 FakeUnavailablePreferenceController.AVAILABILITY_KEY, 335 BasePreferenceController.UNSUPPORTED_ON_DEVICE); 336 337 final Slice slice = SliceBuilderUtils.buildSlice(mContext, data); 338 339 assertThat(slice).isNull(); 340 } 341 342 @Test testDisabledForUserSlice_validTitleSummary()343 public void testDisabledForUserSlice_validTitleSummary() { 344 final SliceData data = getDummyData(FakeUnavailablePreferenceController.class, 345 SliceData.SliceType.SWITCH); 346 Settings.Global.putInt(mContext.getContentResolver(), 347 FakeUnavailablePreferenceController.AVAILABILITY_KEY, 348 BasePreferenceController.DISABLED_FOR_USER); 349 350 final Slice slice = SliceBuilderUtils.buildSlice(mContext, data); 351 352 assertThat(slice).isNull(); 353 } 354 355 @Test testDisabledDependentSettingSlice_validTitleSummary()356 public void testDisabledDependentSettingSlice_validTitleSummary() { 357 final SliceData data = getDummyData(FakeUnavailablePreferenceController.class, 358 SliceData.SliceType.INTENT); 359 Settings.Global.putInt(mContext.getContentResolver(), 360 FakeUnavailablePreferenceController.AVAILABILITY_KEY, 361 BasePreferenceController.DISABLED_DEPENDENT_SETTING); 362 363 final Slice slice = SliceBuilderUtils.buildSlice(mContext, data); 364 365 verify(mFeatureFactory.metricsFeatureProvider) 366 .action(eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_SLICE_REQUESTED), 367 mLoggingArgumentCatpor.capture()); 368 final Pair<Integer, Object> capturedLoggingPair = mLoggingArgumentCatpor.getValue(); 369 370 assertThat(capturedLoggingPair.first) 371 .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); 372 assertThat(capturedLoggingPair.second) 373 .isEqualTo(data.getKey()); 374 SliceTester.testSettingsUnavailableSlice(mContext, slice, data); 375 } 376 377 @Test testConditionallyUnavailableSlice_validTitleSummary()378 public void testConditionallyUnavailableSlice_validTitleSummary() { 379 final SliceData data = getDummyData(FakeUnavailablePreferenceController.class, 380 SliceData.SliceType.SWITCH); 381 Settings.Global.putInt(mContext.getContentResolver(), 382 FakeUnavailablePreferenceController.AVAILABILITY_KEY, 383 BasePreferenceController.CONDITIONALLY_UNAVAILABLE); 384 385 final Slice slice = SliceBuilderUtils.buildSlice(mContext, data); 386 387 verify(mFeatureFactory.metricsFeatureProvider) 388 .action(eq(mContext), eq(MetricsEvent.ACTION_SETTINGS_SLICE_REQUESTED), 389 mLoggingArgumentCatpor.capture()); 390 391 final Pair<Integer, Object> capturedLoggingPair = mLoggingArgumentCatpor.getValue(); 392 393 assertThat(capturedLoggingPair.first) 394 .isEqualTo(MetricsEvent.FIELD_SETTINGS_PREFERENCE_CHANGE_NAME); 395 assertThat(capturedLoggingPair.second) 396 .isEqualTo(data.getKey()); 397 assertThat(slice).isNull(); 398 } 399 400 @Test testContentIntent_includesUniqueData()401 public void testContentIntent_includesUniqueData() { 402 final SliceData sliceData = getDummyData(); 403 final Uri expectedUri = new Uri.Builder().appendPath(sliceData.getKey()).build(); 404 405 final Intent intent = SliceBuilderUtils.getContentIntent(mContext, sliceData); 406 final Uri intentData = intent.getData(); 407 408 assertThat(intentData).isEqualTo(expectedUri); 409 } 410 getDummyData()411 private SliceData getDummyData() { 412 return getDummyData(TOGGLE_CONTROLLER, SUMMARY, SliceData.SliceType.SWITCH, SCREEN_TITLE); 413 } 414 getDummyData(String summary, String screenTitle)415 private SliceData getDummyData(String summary, String screenTitle) { 416 return getDummyData(TOGGLE_CONTROLLER, summary, SliceData.SliceType.SWITCH, screenTitle); 417 } 418 getDummyData(Class prefController, int sliceType)419 private SliceData getDummyData(Class prefController, int sliceType) { 420 return getDummyData(prefController, SUMMARY, sliceType, SCREEN_TITLE); 421 } 422 getDummyData(Class prefController, String summary, int sliceType, String screenTitle)423 private SliceData getDummyData(Class prefController, String summary, int sliceType, 424 String screenTitle) { 425 return new SliceData.Builder() 426 .setKey(KEY) 427 .setTitle(TITLE) 428 .setSummary(summary) 429 .setScreenTitle(screenTitle) 430 .setKeywords(KEYWORDS) 431 .setIcon(ICON) 432 .setFragmentName(FRAGMENT_NAME) 433 .setUri(URI) 434 .setPreferenceControllerClassName(prefController.getName()) 435 .setSliceType(sliceType) 436 .build(); 437 } 438 } 439