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 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 33 import androidx.fragment.app.FragmentManager; 34 import androidx.test.filters.MediumTest; 35 import androidx.test.platform.app.InstrumentationRegistry; 36 import androidx.test.rule.ActivityTestRule; 37 import androidx.test.runner.AndroidJUnit4; 38 39 import com.android.documentsui.files.FilesActivity; 40 41 import com.google.android.material.textfield.TextInputEditText; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Rule; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 import java.io.FileInputStream; 50 import java.io.IOException; 51 52 @MediumTest 53 @RunWith(AndroidJUnit4.class) 54 public class DialogFragmentTest { 55 56 private static final String CREATE_FRAGEMENT_TAG = "create_directory"; 57 CreateDirectoryFragment mCreateDirectoryFragment; 58 FragmentManager mFragmentManager; 59 ScreenDensitySession mScreenDensitySession; 60 Intent mFileActivityIntent; 61 62 @Rule 63 public ActivityTestRule<FilesActivity> mActivityTestRule = new ActivityTestRule<>( 64 FilesActivity.class); 65 66 @Before setup()67 public void setup() { 68 mFileActivityIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); 69 mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager(); 70 mScreenDensitySession = new ScreenDensitySession(); 71 } 72 73 @After tearDown()74 public void tearDown() { 75 mScreenDensitySession.close(); 76 mCreateDirectoryFragment = null; 77 } 78 79 @Test testCreateDialogShows()80 public void testCreateDialogShows() throws Throwable { 81 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 82 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 83 mCreateDirectoryFragment = 84 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 85 86 assertNotNull("Dialog was null", mCreateDirectoryFragment.getDialog()); 87 assertTrue("Dialog was not being shown", mCreateDirectoryFragment.getDialog().isShowing()); 88 } 89 90 @Test testCreateDialogShowsDismiss()91 public void testCreateDialogShowsDismiss() throws Throwable { 92 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 93 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 94 mCreateDirectoryFragment = 95 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 96 97 assertNotNull("Dialog was null", mCreateDirectoryFragment.getDialog()); 98 assertTrue("Dialog was not being shown", mCreateDirectoryFragment.getDialog().isShowing()); 99 100 mActivityTestRule.runOnUiThread(() -> mCreateDirectoryFragment.dismiss()); 101 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 102 103 assertNull("Dialog should be null after dismiss()", mCreateDirectoryFragment.getDialog()); 104 } 105 106 @Test testCreateDialogShows_textInputEditText_shouldNotTruncateOnPortrait()107 public void testCreateDialogShows_textInputEditText_shouldNotTruncateOnPortrait() 108 throws Throwable { 109 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 110 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 111 mCreateDirectoryFragment = 112 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 113 114 final TextInputEditText inputView = 115 mCreateDirectoryFragment.getDialog().findViewById(android.R.id.text1); 116 117 assertTrue(inputView.getHeight() > getInputTextHeight(inputView)); 118 } 119 120 @Test testCreateDialog_textInputEditText_shouldNotTruncateOnLargeDensity()121 public void testCreateDialog_textInputEditText_shouldNotTruncateOnLargeDensity() 122 throws Throwable { 123 124 mScreenDensitySession.setLargeDensity(); 125 mActivityTestRule.finishActivity(); 126 mActivityTestRule.launchActivity(mFileActivityIntent); 127 mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager(); 128 129 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 130 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 131 mCreateDirectoryFragment = 132 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 133 134 final TextInputEditText inputView = 135 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1); 136 137 assertTrue(inputView.getHeight() > getInputTextHeight(inputView)); 138 139 } 140 141 @Test testCreateDialog_textInputEditText_shouldNotTruncateOnLargerDensity()142 public void testCreateDialog_textInputEditText_shouldNotTruncateOnLargerDensity() 143 throws Throwable { 144 145 mScreenDensitySession.setLargerDensity(); 146 mActivityTestRule.finishActivity(); 147 mActivityTestRule.launchActivity(mFileActivityIntent); 148 mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager(); 149 150 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 151 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 152 mCreateDirectoryFragment = 153 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 154 155 final TextInputEditText inputView = 156 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1); 157 158 assertTrue(inputView.getHeight() > getInputTextHeight(inputView)); 159 } 160 161 @Test testCreateDialog_textInputEditText_shouldNotTruncateOnLargestDensity()162 public void testCreateDialog_textInputEditText_shouldNotTruncateOnLargestDensity() 163 throws Throwable { 164 165 mScreenDensitySession.setLargestDensity(); 166 mActivityTestRule.finishActivity(); 167 mActivityTestRule.launchActivity(mFileActivityIntent); 168 mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager(); 169 170 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 171 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 172 mCreateDirectoryFragment = 173 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 174 175 final TextInputEditText inputView = 176 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1); 177 178 assertTrue(inputView.getHeight() > getInputTextHeight(inputView)); 179 } 180 181 @Test testCreateDirectoryFragmentShows_textInputEditText_shouldNotTruncateOnLandscape()182 public void testCreateDirectoryFragmentShows_textInputEditText_shouldNotTruncateOnLandscape() 183 throws Throwable { 184 switchOrientation(mActivityTestRule.getActivity()); 185 mScreenDensitySession.setLargestDensity(); 186 mActivityTestRule.finishActivity(); 187 mActivityTestRule.launchActivity(mFileActivityIntent); 188 mFragmentManager = mActivityTestRule.getActivity().getSupportFragmentManager(); 189 190 mActivityTestRule.runOnUiThread(() -> CreateDirectoryFragment.show(mFragmentManager)); 191 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 192 mCreateDirectoryFragment = 193 (CreateDirectoryFragment) mFragmentManager.findFragmentByTag(CREATE_FRAGEMENT_TAG); 194 195 final TextInputEditText inputView = 196 mCreateDirectoryFragment.getDialog().getWindow().findViewById(android.R.id.text1); 197 Paint paint = inputView.getPaint(); 198 final float textSize = paint.getTextSize(); 199 200 assertTrue(inputView.getHeight() > Math.round(textSize)); 201 202 switchOrientation(mActivityTestRule.getActivity()); 203 } 204 getInputTextHeight(TextInputEditText v)205 private static int getInputTextHeight(TextInputEditText v) { 206 Paint paint = v.getPaint(); 207 final float textSize = paint.getTextSize(); 208 final float textSpace = paint.getFontSpacing(); 209 return Math.round(textSize + textSpace); 210 } 211 switchOrientation(final Activity activity)212 private static void switchOrientation(final Activity activity) { 213 final int[] orientations = getOrientations(activity); 214 activity.setRequestedOrientation(orientations[1]); 215 } 216 getOrientations(final Activity activity)217 private static int[] getOrientations(final Activity activity) { 218 final int originalOrientation = activity.getResources().getConfiguration().orientation; 219 final int newOrientation = originalOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT 220 ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE 221 : ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 222 return new int[]{originalOrientation, newOrientation}; 223 } 224 225 private static class ScreenDensitySession implements AutoCloseable { 226 private static final String DENSITY_PROP_DEVICE = "ro.sf.lcd_density"; 227 private static final String DENSITY_PROP_EMULATOR = "qemu.sf.lcd_density"; 228 229 private static final float DENSITY_DEFAULT = 1f; 230 private static final float DENSITY_LARGE = 1.09f; 231 private static final float DENSITY_LARGER = 1.19f; 232 private static final float DENSITY_LARGEST = 1.29f; 233 setDefaultDensity()234 void setDefaultDensity() { 235 final int stableDensity = getStableDensity(); 236 final int targetDensity = (int) (stableDensity * DENSITY_DEFAULT); 237 setDensity(targetDensity); 238 } 239 setLargeDensity()240 void setLargeDensity() { 241 final int stableDensity = getStableDensity(); 242 final int targetDensity = (int) (stableDensity * DENSITY_LARGE); 243 setDensity(targetDensity); 244 } 245 setLargerDensity()246 void setLargerDensity() { 247 final int stableDensity = getStableDensity(); 248 final int targetDensity = (int) (stableDensity * DENSITY_LARGER); 249 setDensity(targetDensity); 250 } 251 setLargestDensity()252 void setLargestDensity() { 253 final int stableDensity = getStableDensity(); 254 final int targetDensity = (int) (stableDensity * DENSITY_LARGEST); 255 setDensity(targetDensity); 256 } 257 258 @Override close()259 public void close() { 260 resetDensity(); 261 } 262 getStableDensity()263 private int getStableDensity() { 264 final String densityProp; 265 if (Build.IS_EMULATOR) { 266 densityProp = DENSITY_PROP_EMULATOR; 267 } else { 268 densityProp = DENSITY_PROP_DEVICE; 269 } 270 271 return Integer.parseInt(executeShellCommand("getprop " + densityProp).trim()); 272 } 273 setDensity(int targetDensity)274 private void setDensity(int targetDensity) { 275 executeShellCommand("wm density " + targetDensity); 276 277 // Verify that the density is changed. 278 final String output = executeShellCommand("wm density"); 279 final boolean success = output.contains("Override density: " + targetDensity); 280 281 assertTrue("Failed to set density to " + targetDensity, success); 282 } 283 resetDensity()284 private void resetDensity() { 285 executeShellCommand("wm density reset"); 286 } 287 } 288 executeShellCommand(String cmd)289 public static String executeShellCommand(String cmd) { 290 try { 291 return runShellCommand(InstrumentationRegistry.getInstrumentation(), cmd); 292 } catch (IOException e) { 293 fail("Failed reading command output: " + e); 294 return ""; 295 } 296 } 297 runShellCommand(Instrumentation instrumentation, String cmd)298 public static String runShellCommand(Instrumentation instrumentation, String cmd) 299 throws IOException { 300 return runShellCommand(instrumentation.getUiAutomation(), cmd); 301 } 302 runShellCommand(UiAutomation automation, String cmd)303 public static String runShellCommand(UiAutomation automation, String cmd) 304 throws IOException { 305 if (cmd.startsWith("pm grant ") || cmd.startsWith("pm revoke ")) { 306 throw new UnsupportedOperationException("Use UiAutomation.grantRuntimePermission() " 307 + "or revokeRuntimePermission() directly, which are more robust."); 308 } 309 ParcelFileDescriptor pfd = automation.executeShellCommand(cmd); 310 byte[] buf = new byte[512]; 311 int bytesRead; 312 FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 313 StringBuffer stdout = new StringBuffer(); 314 while ((bytesRead = fis.read(buf)) != -1) { 315 stdout.append(new String(buf, 0, bytesRead)); 316 } 317 fis.close(); 318 return stdout.toString(); 319 } 320 } 321