1 /*
2  * Copyright (C) 2019 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.wallpaper.picker.individual;
17 
18 import static androidx.test.espresso.Espresso.onView;
19 import static androidx.test.espresso.action.ViewActions.click;
20 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
21 import static androidx.test.espresso.assertion.ViewAssertions.matches;
22 import static androidx.test.espresso.intent.Intents.intending;
23 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
24 import static androidx.test.espresso.matcher.RootMatchers.isDialog;
25 import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
26 import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
27 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
28 import static androidx.test.espresso.matcher.ViewMatchers.withId;
29 import static androidx.test.espresso.matcher.ViewMatchers.withText;
30 
31 import static junit.framework.TestCase.assertFalse;
32 
33 import static org.junit.Assert.assertEquals;
34 import static org.junit.Assert.assertNotNull;
35 import static org.junit.Assert.assertNull;
36 import static org.junit.Assert.assertTrue;
37 
38 import android.app.Activity;
39 import android.app.Instrumentation.ActivityResult;
40 import android.app.WallpaperManager;
41 import android.content.Context;
42 import android.content.Intent;
43 
44 import androidx.recyclerview.widget.RecyclerView;
45 import androidx.test.espresso.intent.Intents;
46 import androidx.test.filters.MediumTest;
47 import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
48 import androidx.test.platform.app.InstrumentationRegistry;
49 import androidx.test.rule.ActivityTestRule;
50 
51 import com.android.wallpaper.R;
52 import com.android.wallpaper.model.Category;
53 import com.android.wallpaper.model.PickerIntentFactory;
54 import com.android.wallpaper.model.WallpaperInfo;
55 import com.android.wallpaper.model.WallpaperRotationInitializer;
56 import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState;
57 import com.android.wallpaper.module.Injector;
58 import com.android.wallpaper.module.InjectorProvider;
59 import com.android.wallpaper.testing.TestCategoryProvider;
60 import com.android.wallpaper.testing.TestFormFactorChecker;
61 import com.android.wallpaper.testing.TestInjector;
62 import com.android.wallpaper.testing.TestWallpaperCategory;
63 import com.android.wallpaper.testing.TestWallpaperInfo;
64 import com.android.wallpaper.testing.TestWallpaperPreferences;
65 import com.android.wallpaper.testing.TestWallpaperRotationInitializer;
66 
67 import org.hamcrest.Matcher;
68 import org.junit.After;
69 import org.junit.Before;
70 import org.junit.Rule;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.List;
77 
78 /**
79  * Tests for {@link IndividualPickerActivity}.
80  */
81 @RunWith(AndroidJUnit4ClassRunner.class)
82 @MediumTest
83 public class IndividualPickerActivityTest {
84 
85     private static final String EXTRA_WALLPAPER_INFO =
86             "com.android.wallpaper.picker.wallpaper_info";
87     private static final TestWallpaperInfo sWallpaperInfo1 = new TestWallpaperInfo(
88             TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-1");
89     private static final TestWallpaperInfo sWallpaperInfo2 = new TestWallpaperInfo(
90             TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-2");
91     private static final TestWallpaperInfo sWallpaperInfo3 = new TestWallpaperInfo(
92             TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-3");
93 
94     private TestCategoryProvider mTestCategoryProvider;
95 
96     private TestFormFactorChecker mTestFormFactorChecker;
97     private Injector mInjector;
98 
99     private TestWallpaperCategory mTestCategory;
100 
101     private TestWallpaperPreferences mPreferences;
102     private ArrayList<WallpaperInfo> mWallpapers;
103 
104     @Rule
105     public ActivityTestRule<IndividualPickerActivity> mActivityRule =
106             new ActivityTestRule<>(IndividualPickerActivity.class, false, false);
107 
108     @Before
setUp()109     public void setUp() {
110         Intents.init();
111 
112         mInjector = new TestInjector();
113         InjectorProvider.setInjector(mInjector);
114 
115         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
116         mTestFormFactorChecker = (TestFormFactorChecker) mInjector.getFormFactorChecker(context);
117         mTestCategoryProvider = (TestCategoryProvider) mInjector.getCategoryProvider(context);
118 
119         sWallpaperInfo1.setAttributions(Arrays.asList(
120                 "Attribution 0", "Attribution 1", "Attribution 2"));
121 
122         sWallpaperInfo1.setCollectionId("collection");
123 
124         mPreferences = (TestWallpaperPreferences) mInjector.getPreferences(context);
125 
126         mWallpapers = new ArrayList<>();
127         mWallpapers.add(sWallpaperInfo1);
128         mWallpapers.add(sWallpaperInfo2);
129         mWallpapers.add(sWallpaperInfo3);
130     }
131 
132     @After
tearDown()133     public void tearDown() {
134         Intents.release();
135         mActivityRule.finishActivity();
136     }
137 
getActivity()138     private IndividualPickerActivity getActivity() {
139         return mActivityRule.getActivity();
140     }
141 
setUpFragmentForTesting()142     private void setUpFragmentForTesting() {
143         IndividualPickerFragment fragment = (IndividualPickerFragment)
144                 getActivity().getSupportFragmentManager().findFragmentById(R.id.fragment_container);
145         fragment.setTestingMode(true);
146     }
147 
setActivityWithMockWallpapers(boolean isRotationEnabled, @RotationInitializationState int rotationState)148     private void setActivityWithMockWallpapers(boolean isRotationEnabled,
149             @RotationInitializationState int rotationState) {
150         mTestCategory = new TestWallpaperCategory(
151                 "Test category", "collection", mWallpapers, 0 /* priority */);
152         mTestCategory.setIsRotationEnabled(isRotationEnabled);
153         mTestCategory.setRotationInitializationState(rotationState);
154 
155         List<Category> testCategories = mTestCategoryProvider.getTestCategories();
156         testCategories.set(0, mTestCategory);
157 
158         PickerIntentFactory intentFactory =
159                 new IndividualPickerActivity.IndividualPickerActivityIntentFactory();
160         Intent intent = intentFactory.newIntent(
161                 InstrumentationRegistry.getInstrumentation().getTargetContext(),
162                 mTestCategory.getCollectionId());
163         mActivityRule.launchActivity(intent);
164     }
165 
166     @Test
testDrawsTilesForProvidedWallpapers()167     public void testDrawsTilesForProvidedWallpapers() {
168         setActivityWithMockWallpapers(false /* isRotationEnabled */,
169                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
170         IndividualPickerActivity activity = getActivity();
171 
172         RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid);
173 
174         // There are only three wallpapers in the category, so the grid should only have three
175         // items.
176         assertNotNull(recyclerView.findViewHolderForAdapterPosition(0));
177         assertNotNull(recyclerView.findViewHolderForAdapterPosition(1));
178         assertNotNull(recyclerView.findViewHolderForAdapterPosition(2));
179         assertNull(recyclerView.findViewHolderForAdapterPosition(3));
180     }
181 
182     @Test
testClickDailyRefreshAction_ShowsStartRotationDialog()183     public void testClickDailyRefreshAction_ShowsStartRotationDialog() {
184         setActivityWithMockWallpapers(true /* isRotationEnabled */,
185                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
186         getActivity();
187 
188         onView(withId(R.id.action_rotation)).perform(click());
189 
190         onView(withId(R.id.start_rotation_wifi_only_checkbox))
191                 .check(matches(isDisplayed()));
192         // WiFi-only option should be checked by default.
193         onView(withId(R.id.start_rotation_wifi_only_checkbox))
194                 .check(matches(isChecked()));
195     }
196 
197     @Test
testStartRotationDialogUiComponents()198     public void testStartRotationDialogUiComponents() {
199         setActivityWithMockWallpapers(true /* isRotationEnabled */,
200                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
201         getActivity();
202 
203         onView(withId(R.id.action_rotation)).perform(click());
204 
205         onView(withText(R.string.start_rotation_dialog_body)).check(matches(isDisplayed()));
206         onView(withText(android.R.string.cancel)).check(matches(isDisplayed()));
207         onView(withText(android.R.string.cancel)).check(matches(isClickable()));
208         onView(withText(android.R.string.ok)).check(matches(isDisplayed()));
209         onView(withText(android.R.string.ok)).check(matches(isClickable()));
210         onView(withId(R.id.start_rotation_wifi_only_checkbox))
211                 .check(matches(isDisplayed()));
212         onView(withId(R.id.start_rotation_wifi_only_checkbox))
213                 .check(matches(isClickable()));
214         onView(withText(R.string.start_rotation_dialog_wifi_only_option_message))
215                 .check(matches(isDisplayed()));
216     }
217 
218     @Test
testShowStartRotationDialog_WifiOnly_ClickOK_StartsRotation()219     public void testShowStartRotationDialog_WifiOnly_ClickOK_StartsRotation() throws Throwable {
220         setActivityWithMockWallpapers(true /* isRotationEnabled */,
221                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
222         getActivity();
223 
224         setUpFragmentForTesting();
225         TestWallpaperRotationInitializer
226                 testWPRotationInitializer = (TestWallpaperRotationInitializer)
227                 mTestCategory.getWallpaperRotationInitializer();
228         assertFalse(testWPRotationInitializer.isRotationInitialized());
229 
230         // Mock out the intent and response for the live wallpaper preview.
231         Matcher<Intent> expectedIntent = hasAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
232         intending(expectedIntent).respondWith(new ActivityResult(Activity.RESULT_OK, null));
233 
234         onView(withId(R.id.action_rotation)).perform(click());
235 
236         onView(withText(android.R.string.ok)).perform(click());
237         mActivityRule.runOnUiThread(() -> {
238             testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
239             assertTrue(testWPRotationInitializer.isRotationInitialized());
240             assertTrue(testWPRotationInitializer.isWifiOnly());
241 
242             // The activity should finish if starting a rotation was successful.
243             assertTrue(getActivity().isFinishing());
244         });
245     }
246 
247     @Test
testShowStartRotationDialog_WifiOnly_ClickOK_Fails_ShowsErrorDialog()248     public void testShowStartRotationDialog_WifiOnly_ClickOK_Fails_ShowsErrorDialog() {
249         setActivityWithMockWallpapers(true /* isRotationEnabled */,
250                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
251         getActivity();
252 
253         setUpFragmentForTesting();
254         TestWallpaperRotationInitializer
255                 testWPRotationInitializer = (TestWallpaperRotationInitializer)
256                 mTestCategory.getWallpaperRotationInitializer();
257         assertFalse(testWPRotationInitializer.isRotationInitialized());
258 
259         onView(withId(R.id.action_rotation)).perform(click());
260         onView(withText(android.R.string.ok)).perform(click());
261 
262         testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */);
263         assertFalse(testWPRotationInitializer.isRotationInitialized());
264 
265         // Error dialog should be shown with retry option.
266         onView(withText(R.string.start_rotation_error_message))
267                 .inRoot(isDialog()).check(matches(isDisplayed()));
268         onView(withText(R.string.try_again))
269                 .inRoot(isDialog()).check(matches(isDisplayed()));
270     }
271 
272     @Test
testStartRotation_WifiOnly_FailOnce_Retry()273     public void testStartRotation_WifiOnly_FailOnce_Retry() throws Throwable {
274         setActivityWithMockWallpapers(true /* isRotationEnabled */,
275                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
276         getActivity();
277 
278         setUpFragmentForTesting();
279         TestWallpaperRotationInitializer
280                 testWPRotationInitializer = (TestWallpaperRotationInitializer)
281                 mTestCategory.getWallpaperRotationInitializer();
282         assertFalse(testWPRotationInitializer.isRotationInitialized());
283 
284         onView(withId(R.id.action_rotation)).perform(click());
285         onView(withText(android.R.string.ok)).perform(click());
286 
287         testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */);
288         assertFalse(testWPRotationInitializer.isRotationInitialized());
289 
290         // Click try again to retry.
291         onView(withText(R.string.try_again)).inRoot(isDialog()).perform(click());
292 
293         mActivityRule.runOnUiThread(() -> {
294             testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
295             assertTrue(testWPRotationInitializer.isRotationInitialized());
296             assertTrue(testWPRotationInitializer.isWifiOnly());
297         });
298     }
299 
300     @Test
testShowStartRotationDialog_TurnOffWifiOnly_ClickOK_StartsRotation()301     public void testShowStartRotationDialog_TurnOffWifiOnly_ClickOK_StartsRotation()
302             throws Throwable {
303         setActivityWithMockWallpapers(true /* isRotationEnabled */,
304                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
305         getActivity();
306 
307         setUpFragmentForTesting();
308         TestWallpaperRotationInitializer
309                 testWPRotationInitializer = (TestWallpaperRotationInitializer)
310                 mTestCategory.getWallpaperRotationInitializer();
311         assertFalse(testWPRotationInitializer.isRotationInitialized());
312 
313         onView(withId(R.id.action_rotation)).perform(click());
314         // Click on WiFi-only option to toggle it off.
315         onView(withId(R.id.start_rotation_wifi_only_checkbox)).perform(click());
316         onView(withText(android.R.string.ok)).perform(click());
317 
318         mActivityRule.runOnUiThread(() -> {
319             testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
320             assertTrue(testWPRotationInitializer.isRotationInitialized());
321             assertFalse(testWPRotationInitializer.isWifiOnly());
322         });
323     }
324 
325     @Test
testStartRotation_WifiOnly_FailOnce_Retry_ShouldStillHaveWifiTurnedOff()326     public void testStartRotation_WifiOnly_FailOnce_Retry_ShouldStillHaveWifiTurnedOff()
327             throws Throwable {
328         setActivityWithMockWallpapers(true /* isRotationEnabled */,
329                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
330         getActivity();
331 
332         setUpFragmentForTesting();
333         TestWallpaperRotationInitializer
334                 testWPRotationInitializer = (TestWallpaperRotationInitializer)
335                 mTestCategory.getWallpaperRotationInitializer();
336         assertFalse(testWPRotationInitializer.isRotationInitialized());
337 
338         onView(withId(R.id.action_rotation)).perform(click());
339         // Click on WiFi-only option to toggle it off.
340         onView(withId(R.id.start_rotation_wifi_only_checkbox)).perform(click());
341         onView(withText(android.R.string.ok)).perform(click());
342 
343         testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */);
344         assertFalse(testWPRotationInitializer.isRotationInitialized());
345 
346         // Click try again to retry.
347         onView(withText(R.string.try_again)).inRoot(isDialog()).perform(click());
348 
349         mActivityRule.runOnUiThread(() -> {
350             testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
351             assertTrue(testWPRotationInitializer.isRotationInitialized());
352             assertFalse(testWPRotationInitializer.isWifiOnly());
353         });
354     }
355 
356     @Test
testShowStartRotationDialog_ClickCancel_DismissesDialog()357     public void testShowStartRotationDialog_ClickCancel_DismissesDialog() {
358         setActivityWithMockWallpapers(true /* isRotationEnabled */,
359                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
360         getActivity();
361 
362         setUpFragmentForTesting();
363         TestWallpaperRotationInitializer
364                 testWPRotationInitializer = (TestWallpaperRotationInitializer)
365                 mTestCategory.getWallpaperRotationInitializer();
366         assertFalse(testWPRotationInitializer.isRotationInitialized());
367 
368         onView(withId(R.id.action_rotation)).perform(click());
369         onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(matches(isDisplayed()));
370         // WiFi-only option should be checked by default.
371         onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(matches(isChecked()));
372 
373         // Click "Cancel" to dismiss the dialog.
374         onView(withText(android.R.string.cancel)).perform(click());
375 
376         // Rotation was not initialized and dialog is no longer visible.
377         assertFalse(testWPRotationInitializer.isRotationInitialized());
378         onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(doesNotExist());
379     }
380 
381     /**
382      * Tests that when no title is present, a wallpaper tile's content description attribute is
383      * set to the first attribution.
384      */
385     @Test
testWallpaperHasContentDescriptionFromAttribution()386     public void testWallpaperHasContentDescriptionFromAttribution() {
387         setActivityWithMockWallpapers(false /* isRotationEnabled */,
388                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
389         IndividualPickerActivity activity = getActivity();
390 
391         RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid);
392 
393         RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(0);
394         assertEquals("Attribution 0", holder.itemView.findViewById(R.id.tile)
395                 .getContentDescription());
396     }
397 
398     /**
399      * Tests whether the selected wallpaper has a clipped thumbnail: first wallpaper.
400      */
401     @Test
testSelectFirstWallpaper_ShowsClippedThumbnail()402     public void testSelectFirstWallpaper_ShowsClippedThumbnail() {
403         runSelectWallpaperTest(0);
404     }
405 
406     /**
407      * Tests whether the selected wallpaper has a clipped thumbnail: second wallpaper.
408      */
409     @Test
testSelectSecondWallpaper_ShowsClippedThumbnail()410     public void testSelectSecondWallpaper_ShowsClippedThumbnail() {
411         runSelectWallpaperTest(1);
412     }
413 
414     /**
415      * Tests whether the selected wallpaper has a clipped thumbnail: third wallpaper.
416      */
417     @Test
testSelectThirdWallpaper_ShowsClippedThumbnail()418     public void testSelectThirdWallpaper_ShowsClippedThumbnail() {
419         runSelectWallpaperTest(2);
420     }
421 
runSelectWallpaperTest(int selectedWallpaperIndex)422     private void runSelectWallpaperTest(int selectedWallpaperIndex) {
423         mPreferences.setHomeWallpaperRemoteId(
424                 mWallpapers.get(selectedWallpaperIndex).getWallpaperId());
425 
426         setActivityWithMockWallpapers(false /* isRotationEnabled */,
427                 WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
428         IndividualPickerActivity activity = getActivity();
429 
430         RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid);
431 
432         for (int index = 0; index < 3; index++) {
433             assertNotNull(recyclerView.findViewHolderForAdapterPosition(index));
434 
435             CustomShapeImageView thumbnail =
436                     recyclerView.findViewHolderForAdapterPosition(index)
437                             .itemView.findViewById(R.id.thumbnail);
438 
439             // Assert that only the selected wallpaper has a clipped thumbnail.
440             assertEquals(thumbnail.getClipped(), index == selectedWallpaperIndex);
441         }
442     }
443 }
444