1caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot/* 2caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Copyright (C) 2011 The Android Open Source Project 3caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * 4caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Licensed under the Apache License, Version 2.0 (the "License"); 5caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * you may not use this file except in compliance with the License. 6caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * You may obtain a copy of the License at 7caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * 8caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * http://www.apache.org/licenses/LICENSE-2.0 9caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * 10caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Unless required by applicable law or agreed to in writing, software 11caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * distributed under the License is distributed on an "AS IS" BASIS, 12caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * See the License for the specific language governing permissions and 14caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * limitations under the License. 15caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot */ 16caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotpackage com.android.systemui.screenshot; 17caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 18caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.graphics.Bitmap; 19caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.graphics.BitmapFactory; 20caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.os.Environment; 21caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.os.FileObserver; 22caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.test.ActivityInstrumentationTestCase2; 23caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.test.suitebuilder.annotation.LargeTest; 24caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.util.Log; 25caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport android.view.KeyEvent; 26caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 27caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotimport java.io.File; 28caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 29caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot/** 30caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Functional tests for the global screenshot feature. 31caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot */ 32caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot@LargeTest 33caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabotpublic class ScreenshotTest extends ActivityInstrumentationTestCase2<ScreenshotStubActivity> { 34caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 35caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private static final String LOG_TAG = "ScreenshotTest"; 36caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private static final int SCREEN_WAIT_TIME_SEC = 5; 37caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 38caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot public ScreenshotTest() { 39caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot super(ScreenshotStubActivity.class); 40caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 41caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 42caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot /** 43caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * A simple test for screenshots that launches an Activity, injects the key event combo 44caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * to trigger the screenshot, and verifies the screenshot was taken successfully. 45caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot */ 46caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot public void testScreenshot() throws Exception { 47caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot Log.d(LOG_TAG, "starting testScreenshot"); 48caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // launch the activity. 49caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot ScreenshotStubActivity activity = getActivity(); 50caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot assertNotNull(activity); 51caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 52caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot File screenshotDir = getScreenshotDir(); 53caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot NewScreenshotObserver observer = new NewScreenshotObserver( 54caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot screenshotDir.getAbsolutePath()); 55caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot observer.startWatching(); 56caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot takeScreenshot(); 57caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // unlikely, but check if a new screenshot file was already created 58caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot if (observer.getCreatedPath() == null) { 59caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // wait for screenshot to be created 60caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot synchronized(observer) { 61caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot observer.wait(SCREEN_WAIT_TIME_SEC*1000); 62caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 63caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 64caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot assertNotNull(String.format("Could not find screenshot after %d seconds", 65caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot SCREEN_WAIT_TIME_SEC), observer.getCreatedPath()); 66caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 67caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot File screenshotFile = new File(screenshotDir, observer.getCreatedPath()); 68caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot try { 69caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot assertTrue(String.format("Detected new screenshot %s but its not a file", 70caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot screenshotFile.getName()), screenshotFile.isFile()); 71caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot assertTrue(String.format("Detected new screenshot %s but its not an image", 72caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot screenshotFile.getName()), isValidImage(screenshotFile)); 73caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } finally { 74caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // delete the file to prevent external storage from filing up 75caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot screenshotFile.delete(); 76caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 77caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 78caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 79caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private static class NewScreenshotObserver extends FileObserver { 80caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private String mAddedPath = null; 81caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 82caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot NewScreenshotObserver(String path) { 83caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot super(path, FileObserver.CREATE); 84caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 85caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 86caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot synchronized String getCreatedPath() { 87caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot return mAddedPath; 88caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 89caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 90caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot @Override 91caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot public void onEvent(int event, String path) { 92caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot Log.d(LOG_TAG, String.format("Detected new file added %s", path)); 93caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot synchronized (this) { 94caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot mAddedPath = path; 95caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot notify(); 96caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 97caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 98caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 99caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 100caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot /** 101caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Inject the key sequence to take a screenshot. 102caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot */ 103caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private void takeScreenshot() { 104caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, 105caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot KeyEvent.KEYCODE_POWER)); 106caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, 107caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot KeyEvent.KEYCODE_VOLUME_DOWN)); 108caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // the volume down key event will cause the 'volume adjustment' UI to appear in the 109caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // foreground, and steal UI focus 110caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // unfortunately this means the next key event will get directed to the 111caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // 'volume adjustment' UI, instead of this test's activity 112caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // for this reason this test must be signed with platform certificate, to grant this test 113caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // permission to inject key events to another process 114caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, 115caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot KeyEvent.KEYCODE_VOLUME_DOWN)); 116caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, 117caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot KeyEvent.KEYCODE_POWER)); 118caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 119caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 120caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot /** 121caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Get the directory where screenshot images are stored. 122caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot */ 123caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private File getScreenshotDir() { 124caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // TODO: get this dir location from a constant 125caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot return new File(Environment.getExternalStorageDirectory(), "Pictures" + File.separator + 126caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot "Screenshots"); 127caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 128caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot 129caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot /** 130caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot * Return true if file is valid image file 131caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot */ 132caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot private boolean isValidImage(File screenshotFile) { 133caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot Bitmap b = BitmapFactory.decodeFile(screenshotFile.getAbsolutePath()); 134caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot // TODO: do more checks on image 135caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot return b != null; 136caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot } 137caf30a18b52471ebfbc6ae5e853c9a0b9d44905bBrett Chabot} 138