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        if (true) {
48            // Disable until this works again.
49            return;
50        }
51        Log.d(LOG_TAG, "starting testScreenshot");
52        // launch the activity.
53        ScreenshotStubActivity activity = getActivity();
54        assertNotNull(activity);
55
56        File screenshotDir = getScreenshotDir();
57        NewScreenshotObserver observer = new NewScreenshotObserver(
58                screenshotDir.getAbsolutePath());
59        observer.startWatching();
60        takeScreenshot();
61        // unlikely, but check if a new screenshot file was already created
62        if (observer.getCreatedPath() == null) {
63            // wait for screenshot to be created
64            synchronized(observer) {
65                observer.wait(SCREEN_WAIT_TIME_SEC*1000);
66            }
67        }
68        assertNotNull(String.format("Could not find screenshot after %d seconds",
69                SCREEN_WAIT_TIME_SEC), observer.getCreatedPath());
70
71        File screenshotFile = new File(screenshotDir, observer.getCreatedPath());
72        try {
73            assertTrue(String.format("Detected new screenshot %s but its not a file",
74                    screenshotFile.getName()), screenshotFile.isFile());
75            assertTrue(String.format("Detected new screenshot %s but its not an image",
76                    screenshotFile.getName()), isValidImage(screenshotFile));
77        } finally {
78            // delete the file to prevent external storage from filing up
79            screenshotFile.delete();
80        }
81    }
82
83    private static class NewScreenshotObserver extends FileObserver {
84        private String mAddedPath = null;
85
86        NewScreenshotObserver(String path) {
87            super(path, FileObserver.CREATE);
88        }
89
90        synchronized String getCreatedPath() {
91            return mAddedPath;
92        }
93
94        @Override
95        public void onEvent(int event, String path) {
96            Log.d(LOG_TAG, String.format("Detected new file added %s", path));
97            synchronized (this) {
98                mAddedPath = path;
99                notify();
100            }
101        }
102    }
103
104    /**
105     * Inject the key sequence to take a screenshot.
106     */
107    private void takeScreenshot() {
108        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
109                KeyEvent.KEYCODE_POWER));
110        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN,
111                KeyEvent.KEYCODE_VOLUME_DOWN));
112        // the volume down key event will cause the 'volume adjustment' UI to appear in the
113        // foreground, and steal UI focus
114        // unfortunately this means the next key event will get directed to the
115        // 'volume adjustment' UI, instead of this test's activity
116        // for this reason this test must be signed with platform certificate, to grant this test
117        // permission to inject key events to another process
118        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
119                KeyEvent.KEYCODE_VOLUME_DOWN));
120        getInstrumentation().sendKeySync(new KeyEvent(KeyEvent.ACTION_UP,
121                KeyEvent.KEYCODE_POWER));
122    }
123
124    /**
125     * Get the directory where screenshot images are stored.
126     */
127    private File getScreenshotDir() {
128        // TODO: get this dir location from a constant
129        return new File(Environment.getExternalStorageDirectory(), "Pictures" + File.separator +
130                "Screenshots");
131    }
132
133    /**
134     * Return true if file is valid image file
135     */
136    private boolean isValidImage(File screenshotFile) {
137        Bitmap b = BitmapFactory.decodeFile(screenshotFile.getAbsolutePath());
138        // TODO: do more checks on image
139        return b != null;
140    }
141}
142