• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.documentsui;
18 
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertNull;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import android.app.Activity;
25 import android.app.Instrumentation;
26 import android.app.UiAutomation;
27 import android.content.Intent;
28 import android.content.pm.ActivityInfo;
29 import android.graphics.Paint;
30 import android.os.Build;
31 import android.os.ParcelFileDescriptor;
32 import android.view.KeyEvent;
33 
34 import androidx.fragment.app.FragmentManager;
35 import androidx.test.filters.LargeTest;
36 import androidx.test.platform.app.InstrumentationRegistry;
37 import androidx.test.rule.ActivityTestRule;
38 import androidx.test.runner.AndroidJUnit4;
39 import androidx.test.uiautomator.UiDevice;
40 
41 import com.android.documentsui.base.DocumentInfo;
42 import com.android.documentsui.dirlist.RenameDocumentFragment;
43 import com.android.documentsui.files.DeleteDocumentFragment;
44 import com.android.documentsui.files.FilesActivity;
45 import com.android.documentsui.queries.SearchFragment;
46 import com.android.documentsui.sorting.SortListFragment;
47 import com.android.documentsui.sorting.SortModel;
48 
49 import com.google.android.material.textfield.TextInputEditText;
50 
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Ignore;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.Mockito;
58 
59 import java.io.FileInputStream;
60 import java.io.IOException;
61 
62 @LargeTest
63 @RunWith(AndroidJUnit4.class)
64 public class DialogUiTest {
65 
66     private static final String CREATE_FRAGEMENT_TAG = "create_directory";
67     CreateDirectoryFragment mCreateDirectoryFragment;
68     FragmentManager mFragmentManager;
69     ScreenDensitySession mScreenDensitySession;
70     Intent mFileActivityIntent;
71 
72     @Rule
73     public ActivityTestRule<FilesActivity> mActivityTestRule = new ActivityTestRule<>(
74             FilesActivity.class);
75 
76     @Before
setup()77     public void setup() {
78         mFileActivityIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
79         mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager();
80         mScreenDensitySession = new ScreenDensitySession();
81 
82         final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
83         device.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
84         device.pressKeyCode(KeyEvent.KEYCODE_MENU);
85     }
86 
87     @After
tearDown()88     public void tearDown() {
89         mScreenDensitySession.close();
90         mCreateDirectoryFragment = null;
91     }
92 
93     @Test
testCreateDialogShows()94     public void testCreateDialogShows() throws Throwable {
95         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
96         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
97         mCreateDirectoryFragment =
98                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
99 
100         assertNotNull("Dialog was null", mCreateDirectoryFragment.getDialog());
101         assertTrue("Dialog was not being shown", mCreateDirectoryFragment.getDialog().isShowing());
102     }
103 
104     @Test
testCreateDialogShowsDismiss()105     public void testCreateDialogShowsDismiss() throws Throwable {
106         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
107         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
108         mCreateDirectoryFragment =
109                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
110 
111         assertNotNull("Dialog was null", mCreateDirectoryFragment.getDialog());
112         assertTrue("Dialog was not being shown", mCreateDirectoryFragment.getDialog().isShowing());
113 
114         mActivityTestRule.runOnUiThread(() -> mCreateDirectoryFragment.getDialog().dismiss());
115         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
116 
117         assertNull("Dialog should be null after dismiss()", mCreateDirectoryFragment.getDialog());
118     }
119 
120     @Test
testCreateDialogShows_textInputEditText_shouldNotTruncateOnPortrait()121     public void testCreateDialogShows_textInputEditText_shouldNotTruncateOnPortrait()
122             throws Throwable {
123         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
124         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
125         mCreateDirectoryFragment =
126                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
127 
128         final TextInputEditText inputView =
129                 mCreateDirectoryFragment.getDialog().findViewById(android.R.id.text1);
130 
131         assertTrue(inputView.getHeight() > getInputTextHeight(inputView));
132     }
133 
134     @Test
testCreateDialog_textInputEditText_shouldNotTruncateOnLargeDensity()135     public void testCreateDialog_textInputEditText_shouldNotTruncateOnLargeDensity()
136             throws Throwable {
137 
138         mScreenDensitySession.setLargeDensity();
139         mActivityTestRule.finishActivity();
140         mActivityTestRule.launchActivity(mFileActivityIntent);
141         mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager();
142 
143         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
144         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
145         mCreateDirectoryFragment =
146                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
147 
148         final TextInputEditText inputView =
149                 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1);
150 
151         assertTrue(inputView.getHeight() > getInputTextHeight(inputView));
152 
153     }
154 
155     @Test
testCreateDialog_textInputEditText_shouldNotTruncateOnLargerDensity()156     public void testCreateDialog_textInputEditText_shouldNotTruncateOnLargerDensity()
157             throws Throwable {
158 
159         mScreenDensitySession.setLargerDensity();
160         mActivityTestRule.finishActivity();
161         mActivityTestRule.launchActivity(mFileActivityIntent);
162         mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager();
163 
164         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
165         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
166         mCreateDirectoryFragment =
167                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
168 
169         final TextInputEditText inputView =
170                 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1);
171 
172         assertTrue(inputView.getHeight() > getInputTextHeight(inputView));
173     }
174 
175     @Test
testCreateDialog_textInputEditText_shouldNotTruncateOnLargestDensity()176     public void testCreateDialog_textInputEditText_shouldNotTruncateOnLargestDensity()
177             throws Throwable {
178 
179         mScreenDensitySession.setLargestDensity();
180         mActivityTestRule.finishActivity();
181         mActivityTestRule.launchActivity(mFileActivityIntent);
182         mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager();
183 
184         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
185         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
186         mCreateDirectoryFragment =
187                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
188 
189         final TextInputEditText inputView =
190                 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1);
191 
192         assertTrue(inputView.getHeight() > getInputTextHeight(inputView));
193     }
194 
195     @Test
196     @Ignore
testCreateDirectoryFragmentShows_textInputEditText_shouldNotTruncateOnLandscape()197     public void testCreateDirectoryFragmentShows_textInputEditText_shouldNotTruncateOnLandscape()
198             throws Throwable {
199         switchOrientation(mActivityTestRule.getActivity());
200         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
201         mScreenDensitySession.setLargestDensity();
202         mActivityTestRule.finishActivity();
203         mActivityTestRule.launchActivity(mFileActivityIntent);
204         mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager();
205 
206         mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager));
207         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
208         mCreateDirectoryFragment =
209                 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG);
210 
211         final TextInputEditText inputView =
212                 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1);
213 
214         assertTrue(
215                 "Failed with inputView height " + inputView.getHeight() + " and input text height "
216                         + getInputTextHeight(inputView),
217                 inputView.getHeight() > getInputTextHeight(inputView));
218 
219         switchOrientation(mActivityTestRule.getActivity());
220     }
221 
222     @Test
testCreateDirectoryFragmentShows_skipWhenStateSaved()223     public void testCreateDirectoryFragmentShows_skipWhenStateSaved() {
224         mFragmentManager = Mockito.mock(FragmentManager.class);
225         Mockito.when(mFragmentManager.isStateSaved()).thenReturn(true);
226 
227         // Use mock FragmentManager will cause NPE then test fail when DialogFragment.show is
228         // called, so test pass means it skip.
229         CreateDirectoryFragment.show(mFragmentManager);
230     }
231 
232     @Test
testDeleteDocumentFragmentShows_skipWhenStateSaved()233     public void testDeleteDocumentFragmentShows_skipWhenStateSaved() {
234         mFragmentManager = Mockito.mock(FragmentManager.class);
235         Mockito.when(mFragmentManager.isStateSaved()).thenReturn(true);
236 
237         DeleteDocumentFragment.show(mFragmentManager, null, null);
238     }
239 
240     @Test
testRenameDocumentFragmentShows_skipWhenStateSaved()241     public void testRenameDocumentFragmentShows_skipWhenStateSaved() {
242         mFragmentManager = Mockito.mock(FragmentManager.class);
243         Mockito.when(mFragmentManager.isStateSaved()).thenReturn(true);
244 
245         RenameDocumentFragment.show(mFragmentManager, new DocumentInfo());
246     }
247 
248     @Test
testSearchFragmentShows_skipWhenStateSaved()249     public void testSearchFragmentShows_skipWhenStateSaved() {
250         mFragmentManager = Mockito.mock(FragmentManager.class);
251         Mockito.when(mFragmentManager.isStateSaved()).thenReturn(true);
252 
253         SearchFragment.showFragment(mFragmentManager, "");
254     }
255 
256     @Test
testSortListFragmentShows_skipWhenStateSaved()257     public void testSortListFragmentShows_skipWhenStateSaved() {
258         mFragmentManager = Mockito.mock(FragmentManager.class);
259         Mockito.when(mFragmentManager.isStateSaved()).thenReturn(true);
260         SortModel sortModel = Mockito.mock(SortModel.class);
261 
262         SortListFragment.show(mFragmentManager, sortModel);
263     }
264 
getInputTextHeight(TextInputEditText v)265     private static int getInputTextHeight(TextInputEditText v) {
266         Paint paint = v.getPaint();
267         final float textSize = paint.getTextSize();
268         final float textSpace = paint.getFontSpacing();
269         return Math.round(textSize + textSpace);
270     }
271 
switchOrientation(final Activity activity)272     private static void switchOrientation(final Activity activity) {
273         final int[] orientations = getOrientations(activity);
274         activity.setRequestedOrientation(orientations[1]);
275     }
276 
getOrientations(final Activity activity)277     private static int[] getOrientations(final Activity activity) {
278         final int originalOrientation = activity.getResources().getConfiguration().orientation;
279         final int newOrientation = originalOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
280                 ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
281                 : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
282         return new int[]{originalOrientation, newOrientation};
283     }
284 
285     private static class ScreenDensitySession implements AutoCloseable {
286         private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density";
287         private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density";
288 
289         private static final float DENSITY_DEFAULT = 1f;
290         private static final float DENSITY_LARGE = 1.09f;
291         private static final float DENSITY_LARGER = 1.19f;
292         private static final float DENSITY_LARGEST = 1.29f;
293 
setDefaultDensity()294         void setDefaultDensity() {
295             final int stableDensity = getStableDensity();
296             final int targetDensity = (int) (stableDensity * DENSITY_DEFAULT);
297             setDensity(targetDensity);
298         }
299 
setLargeDensity()300         void setLargeDensity() {
301             final int stableDensity = getStableDensity();
302             final int targetDensity = (int) (stableDensity * DENSITY_LARGE);
303             setDensity(targetDensity);
304         }
305 
setLargerDensity()306         void setLargerDensity() {
307             final int stableDensity = getStableDensity();
308             final int targetDensity = (int) (stableDensity * DENSITY_LARGER);
309             setDensity(targetDensity);
310         }
311 
setLargestDensity()312         void setLargestDensity() {
313             final int stableDensity = getStableDensity();
314             final int targetDensity = (int) (stableDensity * DENSITY_LARGEST);
315             setDensity(targetDensity);
316         }
317 
318         @Override
close()319         public void close() {
320             resetDensity();
321         }
322 
getStableDensity()323         private int getStableDensity() {
324             final String densityProp;
325             if (Build.IS_EMULATOR) {
326                 densityProp = DENSITY_PROP_EMULATOR;
327             } else {
328                 densityProp = DENSITY_PROP_DEVICE;
329             }
330 
331             return Integer.parseInt(executeShellCommand("getprop " + densityProp).trim());
332         }
333 
setDensity(int targetDensity)334         private void setDensity(int targetDensity) {
335             executeShellCommand("wm density " + targetDensity);
336 
337             // Verify that the density is changed.
338             final String output = executeShellCommand("wm density");
339             final boolean success = output.contains("Override density: " + targetDensity);
340 
341             assertTrue("Failed to set density to " + targetDensity, success);
342         }
343 
resetDensity()344         private void resetDensity() {
345             executeShellCommand("wm density reset");
346         }
347     }
348 
executeShellCommand(String cmd)349     public static String executeShellCommand(String cmd) {
350         try {
351             return runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd);
352         } catch (IOException e) {
353             fail("Failed reading command output: " + e);
354             return "";
355         }
356     }
357 
runShellCommand(Instrumentation instrumentation, String cmd)358     public static String runShellCommand(Instrumentation instrumentation, String cmd)
359             throws IOException {
360         return runShellCommand(instrumentation.getUiAutomation(), cmd);
361     }
362 
runShellCommand(UiAutomation automation, String cmd)363     public static String runShellCommand(UiAutomation automation, String cmd)
364             throws IOException {
365         if (cmd.startsWith("pm grant ") || cmd.startsWith("pm revoke ")) {
366             throw new UnsupportedOperationException("Use UiAutomation.grantRuntimePermission() "
367                     + "or revokeRuntimePermission() directly, which are more robust.");
368         }
369         ParcelFileDescriptor pfd = automation.executeShellCommand(cmd);
370         byte[] buf = new byte[512];
371         int bytesRead;
372         FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
373         StringBuffer stdout = new StringBuffer();
374         while ((bytesRead = fis.read(buf)) != -1) {
375             stdout.append(new String(buf, 0, bytesRead));
376         }
377         fis.close();
378         return stdout.toString();
379     }
380 }
381