1 /*
2  * Copyright (C) 2020 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.testutils;
17 
18 import static android.app.slice.Slice.HINT_TITLE;
19 import static android.app.slice.Slice.SUBTYPE_COLOR;
20 import static android.app.slice.SliceItem.FORMAT_IMAGE;
21 import static android.app.slice.SliceItem.FORMAT_INT;
22 import static android.app.slice.SliceItem.FORMAT_TEXT;
23 
24 import static com.google.common.truth.Truth.assertThat;
25 
26 import android.app.PendingIntent;
27 import android.content.Context;
28 import android.text.TextUtils;
29 
30 import androidx.core.graphics.drawable.IconCompat;
31 import androidx.slice.Slice;
32 import androidx.slice.SliceItem;
33 import androidx.slice.SliceMetadata;
34 import androidx.slice.builders.ListBuilder;
35 import androidx.slice.core.SliceAction;
36 import androidx.slice.core.SliceQuery;
37 import androidx.slice.widget.EventInfo;
38 
39 import com.android.settings.Utils;
40 import com.android.settings.slices.SettingsSliceProvider;
41 import com.android.settings.slices.SliceBuilderUtils;
42 import com.android.settings.slices.SliceData;
43 
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.Set;
47 import java.util.stream.Collectors;
48 
49 /**
50  * Testing utility class to verify the contents of the different Settings Slices.
51  *
52  * TODO (77712944) check Summary, range (metadata.getRange()), toggle icons.
53  */
54 public class SliceTester {
55 
56     /**
57      * Test the contents of an intent based slice, including:
58      * - No toggles
59      * - Correct intent
60      * - Correct title
61      * - Correct keywords
62      * - TTL
63      * - Color
64      */
testSettingsIntentSlice(Context context, Slice slice, SliceData sliceData)65     public static void testSettingsIntentSlice(Context context, Slice slice, SliceData sliceData) {
66         final SliceMetadata metadata = SliceMetadata.from(context, slice);
67 
68         final long sliceTTL = metadata.getExpiry();
69         assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);
70 
71         final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
72         final int color = colorItem.getInt();
73         assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context));
74 
75         final List<SliceAction> toggles = metadata.getToggles();
76         assertThat(toggles).isEmpty();
77 
78         final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction();
79         assertThat(primaryPendingIntent).isEqualTo(
80                 SliceBuilderUtils.getContentPendingIntent(context, sliceData));
81 
82         assertThat(metadata.getTitle()).isEqualTo(sliceData.getTitle());
83 
84         assertKeywords(metadata, sliceData);
85     }
86 
87     /**
88      * Test the contents of an toggle based slice, including:
89      * - Contains one toggle
90      * - Correct toggle intent
91      * - Correct content intent
92      * - Correct title
93      * - Correct keywords
94      * - TTL
95      * - Color
96      */
testSettingsToggleSlice(Context context, Slice slice, SliceData sliceData)97     public static void testSettingsToggleSlice(Context context, Slice slice, SliceData sliceData) {
98         final SliceMetadata metadata = SliceMetadata.from(context, slice);
99 
100         final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
101         final int color = colorItem.getInt();
102         assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context));
103 
104         final List<SliceAction> toggles = metadata.getToggles();
105         assertThat(toggles).hasSize(1);
106 
107         final long sliceTTL = metadata.getExpiry();
108         assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);
109 
110         final SliceAction mainToggleAction = toggles.get(0);
111 
112         assertThat(mainToggleAction.getIcon()).isNull();
113 
114         // Check intent in Toggle Action
115         final PendingIntent togglePendingIntent = mainToggleAction.getAction();
116         assertThat(togglePendingIntent).isEqualTo(SliceBuilderUtils.getActionIntent(context,
117                 SettingsSliceProvider.ACTION_TOGGLE_CHANGED, sliceData));
118 
119         // Check primary intent
120         final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction();
121         assertThat(primaryPendingIntent).isEqualTo(
122                 SliceBuilderUtils.getContentPendingIntent(context, sliceData));
123 
124         assertThat(metadata.getTitle()).isEqualTo(sliceData.getTitle());
125 
126         assertKeywords(metadata, sliceData);
127     }
128 
129     /**
130      * Test the contents of an slider based slice, including:
131      * - No intent
132      * - Correct title
133      * - Correct keywords
134      * - TTL
135      * - Color
136      */
testSettingsSliderSlice(Context context, Slice slice, SliceData sliceData)137     public static void testSettingsSliderSlice(Context context, Slice slice, SliceData sliceData) {
138         final SliceMetadata metadata = SliceMetadata.from(context, slice);
139         final SliceAction primaryAction = metadata.getPrimaryAction();
140 
141         final IconCompat icon = primaryAction.getIcon();
142         if (icon == null) {
143             final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
144             final int color = colorItem.getInt();
145             assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context));
146 
147         } else {
148             final IconCompat expectedIcon = IconCompat.createWithResource(context,
149                     sliceData.getIconResource());
150             assertThat(expectedIcon.toString()).isEqualTo(icon.toString());
151         }
152 
153         final long sliceTTL = metadata.getExpiry();
154         assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);
155 
156         final int headerType = metadata.getHeaderType();
157         assertThat(headerType).isEqualTo(EventInfo.ROW_TYPE_SLIDER);
158 
159         // Check primary intent
160         final PendingIntent primaryPendingIntent = primaryAction.getAction();
161         assertThat(primaryPendingIntent).isEqualTo(
162                 SliceBuilderUtils.getContentPendingIntent(context, sliceData));
163 
164         assertThat(metadata.getTitle()).isEqualTo(sliceData.getTitle());
165 
166         assertKeywords(metadata, sliceData);
167     }
168 
169     /**
170      * Test the copyable slice, including:
171      * - No intent
172      * - Correct title
173      * - Correct intent
174      * - Correct keywords
175      * - TTL
176      * - Color
177      */
testSettingsCopyableSlice(Context context, Slice slice, SliceData sliceData)178     public static void testSettingsCopyableSlice(Context context, Slice slice,
179             SliceData sliceData) {
180         final SliceMetadata metadata = SliceMetadata.from(context, slice);
181 
182         final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
183         final int color = colorItem.getInt();
184         assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context));
185 
186         final SliceAction primaryAction = metadata.getPrimaryAction();
187 
188         final IconCompat expectedIcon = IconCompat.createWithResource(context,
189                 sliceData.getIconResource());
190         assertThat(expectedIcon.toString()).isEqualTo(primaryAction.getIcon().toString());
191 
192         final long sliceTTL = metadata.getExpiry();
193         assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);
194 
195         // Check primary intent
196         final PendingIntent primaryPendingIntent = primaryAction.getAction();
197         assertThat(primaryPendingIntent).isEqualTo(
198                 SliceBuilderUtils.getContentPendingIntent(context, sliceData));
199 
200         assertThat(metadata.getTitle()).isEqualTo(sliceData.getTitle());
201 
202         assertKeywords(metadata, sliceData);
203     }
204 
205     /**
206      * Test the contents of an unavailable slice, including:
207      * - No toggles
208      * - Correct title
209      * - Correct intent
210      * - Correct keywords
211      * - Color
212      * - TTL
213      */
testSettingsUnavailableSlice(Context context, Slice slice, SliceData sliceData)214     public static void testSettingsUnavailableSlice(Context context, Slice slice,
215             SliceData sliceData) {
216         final SliceMetadata metadata = SliceMetadata.from(context, slice);
217 
218         final long sliceTTL = metadata.getExpiry();
219         assertThat(sliceTTL).isEqualTo(ListBuilder.INFINITY);
220 
221         final SliceItem colorItem = SliceQuery.findSubtype(slice, FORMAT_INT, SUBTYPE_COLOR);
222         final int color = colorItem.getInt();
223         assertThat(color).isEqualTo(Utils.getColorAccentDefaultColor(context));
224 
225         final List<SliceAction> toggles = metadata.getToggles();
226         assertThat(toggles).isEmpty();
227 
228         final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction();
229         assertThat(primaryPendingIntent).isEqualTo(SliceBuilderUtils.getContentPendingIntent(
230                 context, sliceData));
231 
232         assertThat(metadata.getTitle()).isEqualTo(sliceData.getTitle());
233 
234         assertKeywords(metadata, sliceData);
235     }
236 
237     /**
238      * Assert any slice item contains title.
239      *
240      * @param sliceItems All slice items of a Slice.
241      * @param title Title for asserting.
242      */
assertAnySliceItemContainsTitle(List<SliceItem> sliceItems, String title)243     public static void assertAnySliceItemContainsTitle(List<SliceItem> sliceItems, String title) {
244         assertThat(hasText(sliceItems, title, HINT_TITLE)).isTrue();
245     }
246 
247     /**
248      * Assert any slice item contains subtitle.
249      *
250      * @param sliceItems All slice items of a Slice.
251      * @param subtitle Subtitle for asserting.
252      */
assertAnySliceItemContainsSubtitle(List<SliceItem> sliceItems, String subtitle)253     public static void assertAnySliceItemContainsSubtitle(List<SliceItem> sliceItems,
254             String subtitle) {
255         // Subtitle has no hints
256         assertThat(hasText(sliceItems, subtitle, null /* hints */)).isTrue();
257     }
258 
259     /**
260      * Assert no slice item contains subtitle.
261      *
262      * @param sliceItems All slice items of a Slice.
263      * @param subtitle Subtitle for asserting.
264      */
assertNoSliceItemContainsSubtitle(List<SliceItem> sliceItems, String subtitle)265     public static void assertNoSliceItemContainsSubtitle(List<SliceItem> sliceItems,
266             String subtitle) {
267         // Subtitle has no hints
268         assertThat(hasText(sliceItems, subtitle, null /* hints */)).isFalse();
269     }
270 
hasText(List<SliceItem> sliceItems, String text, String hints)271     private static boolean hasText(List<SliceItem> sliceItems, String text, String hints) {
272         boolean hasText = false;
273         for (SliceItem item : sliceItems) {
274             List<SliceItem> textItems = SliceQuery.findAll(item, FORMAT_TEXT, hints,
275                     null /* non-hints */);
276             if (textItems == null) {
277                 continue;
278             }
279 
280             for (SliceItem textItem : textItems) {
281                 if (TextUtils.equals(textItem.getText(), text)) {
282                     hasText = true;
283                     break;
284                 }
285             }
286         }
287         return hasText;
288     }
289 
290     /**
291      * Assert any slice item contains icon.
292      *
293      * @param sliceItems All slice items of a Slice.
294      * @param icon Icon for asserting.
295      */
assertAnySliceItemContainsIcon(List<SliceItem> sliceItems, IconCompat icon)296     public static void assertAnySliceItemContainsIcon(List<SliceItem> sliceItems, IconCompat icon) {
297         boolean hasIcon = false;
298         for (SliceItem item : sliceItems) {
299             List<SliceItem> iconItems = SliceQuery.findAll(item, FORMAT_IMAGE,
300                     (String) null /* hints */, null /* non-hints */);
301             if (iconItems == null) {
302                 continue;
303             }
304 
305             for (SliceItem iconItem : iconItems) {
306                 if (icon.toString().equals(iconItem.getIcon().toString())) {
307                     hasIcon = true;
308                     break;
309                 }
310             }
311         }
312         assertThat(hasIcon).isTrue();
313     }
314 
assertKeywords(SliceMetadata metadata, SliceData data)315     private static void assertKeywords(SliceMetadata metadata, SliceData data) {
316         final List<String> keywords = metadata.getSliceKeywords();
317         final Set<String> expectedKeywords = Arrays.stream(data.getKeywords().split(","))
318                 .map(s -> s = s.trim())
319                 .collect(Collectors.toSet());
320         expectedKeywords.add(data.getTitle());
321         expectedKeywords.add(data.getScreenTitle().toString());
322         assertThat(keywords).containsExactlyElementsIn(expectedKeywords);
323     }
324 }
325