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