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