• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.documentsui.queries;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Mockito.spy;
24 
25 import static java.util.Objects.requireNonNull;
26 
27 import android.content.Context;
28 import android.graphics.Bitmap;
29 import android.graphics.Canvas;
30 import android.graphics.drawable.Drawable;
31 import android.os.Bundle;
32 import android.platform.test.annotations.RequiresFlagsDisabled;
33 import android.platform.test.annotations.RequiresFlagsEnabled;
34 import android.platform.test.flag.junit.CheckFlagsRule;
35 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
36 import android.provider.DocumentsContract;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.ViewParent;
40 import android.widget.FrameLayout;
41 import android.widget.HorizontalScrollView;
42 import android.widget.LinearLayout;
43 
44 import androidx.test.ext.junit.runners.AndroidJUnit4;
45 import androidx.test.filters.SmallTest;
46 import androidx.test.platform.app.InstrumentationRegistry;
47 
48 import com.android.documentsui.IconUtils;
49 import com.android.documentsui.R;
50 import com.android.documentsui.base.MimeTypes;
51 import com.android.documentsui.base.Shared;
52 import com.android.documentsui.flags.Flags;
53 import com.android.documentsui.util.VersionUtils;
54 
55 import com.google.android.material.chip.Chip;
56 
57 import org.junit.Before;
58 import org.junit.Rule;
59 import org.junit.Test;
60 import org.junit.runner.RunWith;
61 
62 import java.util.HashSet;
63 import java.util.Iterator;
64 import java.util.Set;
65 
66 @RunWith(AndroidJUnit4.class)
67 @SmallTest
68 public final class SearchChipViewManagerTest {
69 
70     private static final String LARGE_FILES_CHIP_MIME_TYPE = "";
71     private static final String FROM_THIS_WEEK_CHIP_MIME_TYPE = "";
72     private static final String[] TEST_MIME_TYPES_INCLUDING_DOCUMENT =
73             new String[]{"image/*", "video/*", "text/*"};
74     private static final String[] TEST_MIME_TYPES =
75             new String[]{"image/*", "video/*"};
76     private static final String[] TEST_OTHER_TYPES =
77             new String[]{LARGE_FILES_CHIP_MIME_TYPE, FROM_THIS_WEEK_CHIP_MIME_TYPE};
78     private static int CHIP_TYPE = 1000;
79 
80     private Context mContext;
81     private SearchChipViewManager mSearchChipViewManager;
82     private LinearLayout mChipGroup;
83 
84     @Rule
85     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
86 
87     @Before
setUp()88     public void setUp() {
89         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
90         mContext.setTheme(com.android.documentsui.R.style.DocumentsTheme);
91         mContext.getTheme().applyStyle(R.style.DocumentsDefaultTheme, false);
92         mChipGroup = spy(new LinearLayout(mContext));
93 
94         mSearchChipViewManager = new SearchChipViewManager(mChipGroup);
95         mSearchChipViewManager.initChipSets(new String[] {"*/*"});
96     }
97 
98     @Test
testInitChipSets_hasCorrectChipCount()99     public void testInitChipSets_hasCorrectChipCount() throws Exception {
100         mSearchChipViewManager.initChipSets(TEST_MIME_TYPES);
101         mSearchChipViewManager.updateChips(new String[] {"*/*"});
102 
103         int totalChipLength = TEST_MIME_TYPES.length + TEST_OTHER_TYPES.length;
104         assertThat(mChipGroup.getChildCount()).isEqualTo(totalChipLength);
105     }
106 
107     @Test
108     @RequiresFlagsEnabled({Flags.FLAG_USE_MATERIAL3})
testChipIcon()109     public void testChipIcon() {
110         mSearchChipViewManager.initChipSets(
111                 new String[] {"image/*", "audio/*", "video/*", "text/*"});
112         mSearchChipViewManager.updateChips(new String[] {"*/*"});
113 
114         Chip imageChip = (Chip) mChipGroup.getChildAt(0);
115         assertDrawablesEqual(
116                 requireNonNull(imageChip.getChipIcon()),
117                 requireNonNull(mContext.getDrawable(R.drawable.ic_chip_image)));
118         Chip audioChip = (Chip) mChipGroup.getChildAt(1);
119         assertDrawablesEqual(
120                 requireNonNull(audioChip.getChipIcon()),
121                 requireNonNull(mContext.getDrawable(R.drawable.ic_chip_audio)));
122         Chip videoChip = (Chip) mChipGroup.getChildAt(2);
123         assertDrawablesEqual(
124                 requireNonNull(videoChip.getChipIcon()),
125                 requireNonNull(mContext.getDrawable(R.drawable.ic_chip_video)));
126         Chip documentChip = (Chip) mChipGroup.getChildAt(3);
127         assertDrawablesEqual(
128                 requireNonNull(documentChip.getChipIcon()),
129                 requireNonNull(mContext.getDrawable(R.drawable.ic_chip_document)));
130     }
131 
132     @Test
133     @RequiresFlagsDisabled({Flags.FLAG_USE_MATERIAL3})
testChipIcon_M3Disabled()134     public void testChipIcon_M3Disabled() {
135         mSearchChipViewManager.initChipSets(
136                 new String[] {"image/*", "audio/*", "video/*", "text/*"});
137         mSearchChipViewManager.updateChips(new String[] {"*/*"});
138 
139         Chip imageChip = (Chip) mChipGroup.getChildAt(0);
140         assertDrawablesEqual(
141                 requireNonNull(imageChip.getChipIcon()),
142                 requireNonNull(IconUtils.loadMimeIcon(mContext, "image/*")));
143         Chip audioChip = (Chip) mChipGroup.getChildAt(1);
144         assertDrawablesEqual(
145                 requireNonNull(audioChip.getChipIcon()),
146                 requireNonNull(IconUtils.loadMimeIcon(mContext, "audio/*")));
147         Chip videoChip = (Chip) mChipGroup.getChildAt(2);
148         assertDrawablesEqual(
149                 requireNonNull(videoChip.getChipIcon()),
150                 requireNonNull(IconUtils.loadMimeIcon(mContext, "video/*")));
151         Chip documentChip = (Chip) mChipGroup.getChildAt(3);
152         assertDrawablesEqual(
153                 requireNonNull(documentChip.getChipIcon()),
154                 requireNonNull(IconUtils.loadMimeIcon(mContext, MimeTypes.GENERIC_TYPE)));
155     }
156 
157     @Test
testUpdateChips_hasCorrectChipCount()158     public void testUpdateChips_hasCorrectChipCount() throws Exception {
159         mSearchChipViewManager.updateChips(TEST_MIME_TYPES);
160 
161         int totalChipLength = TEST_MIME_TYPES.length + TEST_OTHER_TYPES.length;
162         assertThat(mChipGroup.getChildCount()).isEqualTo(totalChipLength);
163     }
164 
165     @Test
testUpdateChips_documentsFilterOnlyAvailableAboveR()166     public void testUpdateChips_documentsFilterOnlyAvailableAboveR() throws Exception {
167         mSearchChipViewManager.updateChips(TEST_MIME_TYPES_INCLUDING_DOCUMENT);
168 
169         int totalChipLength = TEST_MIME_TYPES_INCLUDING_DOCUMENT.length + TEST_OTHER_TYPES.length;
170         if (VersionUtils.isAtLeastR()) {
171             assertThat(mChipGroup.getChildCount()).isEqualTo(totalChipLength);
172         } else {
173             assertThat(mChipGroup.getChildCount()).isEqualTo(totalChipLength - 1);
174         }
175     }
176 
177     @Test
testUpdateChips_withSingleMimeType_hasCorrectChipCount()178     public void testUpdateChips_withSingleMimeType_hasCorrectChipCount() throws Exception {
179         mSearchChipViewManager.updateChips(new String[]{"image/*"});
180 
181         assertThat(mChipGroup.getChildCount()).isEqualTo(TEST_OTHER_TYPES.length);
182     }
183 
184     @Test
testGetCheckedChipMimeTypes_hasCorrectValue()185     public void testGetCheckedChipMimeTypes_hasCorrectValue() throws Exception {
186         mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
187 
188         String[] checkedMimeTypes =
189                 mSearchChipViewManager.getCheckedChipQueryArgs()
190                         .getStringArray(DocumentsContract.QUERY_ARG_MIME_TYPES);
191 
192         assertThat(MimeTypes.mimeMatches(TEST_MIME_TYPES, checkedMimeTypes[0])).isTrue();
193         assertThat(MimeTypes.mimeMatches(TEST_MIME_TYPES, checkedMimeTypes[1])).isTrue();
194     }
195 
196     @Test
testRestoreCheckedChipItems_hasCorrectValue()197     public void testRestoreCheckedChipItems_hasCorrectValue() throws Exception {
198         Bundle bundle = new Bundle();
199         bundle.putIntArray(Shared.EXTRA_QUERY_CHIPS, new int[]{2});
200 
201         mSearchChipViewManager.restoreCheckedChipItems(bundle);
202 
203         assertThat(mSearchChipViewManager.mCheckedChipItems.size()).isEqualTo(1);
204         Iterator<SearchChipData> iterator = mSearchChipViewManager.mCheckedChipItems.iterator();
205         assertThat(iterator.next().getChipType()).isEqualTo(2);
206     }
207 
208     @Test
testSaveInstanceState_hasCorrectValue()209     public void testSaveInstanceState_hasCorrectValue() throws Exception {
210         mSearchChipViewManager.mCheckedChipItems = getFakeSearchChipDataList();
211         Bundle bundle = new Bundle();
212 
213         mSearchChipViewManager.onSaveInstanceState(bundle);
214 
215         final int[] chipTypes = bundle.getIntArray(Shared.EXTRA_QUERY_CHIPS);
216         assertThat(chipTypes.length).isEqualTo(1);
217         assertThat(chipTypes[0]).isEqualTo(CHIP_TYPE);
218     }
219 
220     @Test
testBindMirrorGroup_sameValue()221     public void testBindMirrorGroup_sameValue() throws Exception {
222         mSearchChipViewManager.updateChips(new String[] {"*/*"});
223 
224         ViewGroup mirror = spy(new LinearLayout(mContext));
225         mSearchChipViewManager.bindMirrorGroup(mirror);
226 
227         assertThat(View.VISIBLE).isEqualTo(mirror.getVisibility());
228         assertThat(mChipGroup.getChildCount()).isEqualTo(mirror.getChildCount());
229         assertThat(mChipGroup.getChildAt(0).getTag()).isEqualTo(mirror.getChildAt(0).getTag());
230     }
231 
232     @Test
testBindMirrorGroup_showRow()233     public void testBindMirrorGroup_showRow() throws Exception {
234         mSearchChipViewManager.updateChips(new String[] {"image/*"});
235 
236         ViewGroup mirror = spy(new LinearLayout(mContext));
237         mSearchChipViewManager.bindMirrorGroup(mirror);
238 
239         assertThat(View.VISIBLE).isEqualTo(mirror.getVisibility());
240     }
241 
242     @Test
testChipChecked_resetScroll()243     public void testChipChecked_resetScroll() {
244         // Mock chip group's parent chain according to search_chip_row.xml.
245         FrameLayout parent = spy(new FrameLayout(mContext));
246         HorizontalScrollView grandparent = spy(new HorizontalScrollView(mContext));
247         parent.addView(mChipGroup);
248         grandparent.addView(parent);
249         // Verify that getParent().getParent() returns the HorizontalScrollView mock.
250         ViewParent result = mChipGroup.getParent().getParent();
251         assertEquals(grandparent, result);
252 
253         mSearchChipViewManager.initChipSets(
254                 new String[] {"image/*", "audio/*", "video/*", "text/*"});
255         mSearchChipViewManager.updateChips(new String[] {"*/*"});
256 
257         // Manually set HorizontalScrollView's scrollX to something larger than 0.
258         grandparent.scrollTo(100, 0);
259         assertTrue(grandparent.getScaleX() > 0);
260 
261         assertEquals(6, mChipGroup.getChildCount());
262         Chip lastChip = (Chip) mChipGroup.getChildAt(5);
263 
264         // chip.setChecked will trigger reorder animation, which needs to be run inside
265         // the looper thread.
266         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
267             // Check last chip will move it to the first child and reset scroll view.
268             lastChip.setChecked(true);
269             assertEquals(0, grandparent.getScrollX());
270         });
271     }
272 
getFakeSearchChipDataList()273     private static Set<SearchChipData> getFakeSearchChipDataList() {
274         final Set<SearchChipData> chipDataList = new HashSet<>();
275         chipDataList.add(new SearchChipData(CHIP_TYPE, 0 /* titleRes */, TEST_MIME_TYPES));
276         return chipDataList;
277     }
278 
assertDrawablesEqual(Drawable actual, Drawable expected)279     private void assertDrawablesEqual(Drawable actual, Drawable expected) {
280         Bitmap bitmap1 =
281                 Bitmap.createBitmap(
282                         actual.getIntrinsicWidth(),
283                         actual.getIntrinsicHeight(),
284                         Bitmap.Config.ARGB_8888);
285         Canvas canvas1 = new Canvas(bitmap1);
286         actual.setBounds(0, 0, actual.getIntrinsicWidth(), actual.getIntrinsicHeight());
287         actual.draw(canvas1);
288 
289         Bitmap bitmap2 =
290                 Bitmap.createBitmap(
291                         expected.getIntrinsicWidth(),
292                         expected.getIntrinsicHeight(),
293                         Bitmap.Config.ARGB_8888);
294         Canvas canvas2 = new Canvas(bitmap2);
295         expected.setBounds(0, 0, expected.getIntrinsicWidth(), expected.getIntrinsicHeight());
296         expected.draw(canvas2);
297 
298         assertTrue("Drawables are not equal", bitmap1.sameAs(bitmap2));
299     }
300 }
301