1 /*
2  * Copyright (C) 2012 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.camera.activity;
18 
19 import android.app.Activity;
20 import android.app.Instrumentation;
21 import android.hardware.Camera;
22 import android.hardware.Camera.AutoFocusCallback;
23 import android.hardware.Camera.CameraInfo;
24 import android.hardware.Camera.Parameters;
25 import android.hardware.Camera.PictureCallback;
26 import android.hardware.Camera.ShutterCallback;
27 import android.test.ActivityInstrumentationTestCase2;
28 import android.view.KeyEvent;
29 import android.view.MotionEvent;
30 import android.view.View;
31 
32 import com.android.camera.CameraHolder;
33 import com.android.camera.CameraManager.CameraProxy;
34 import com.android.camera.R;
35 import com.android.camera.Util;
36 
37 import static com.google.testing.littlemock.LittleMock.mock;
38 import static com.google.testing.littlemock.LittleMock.doAnswer;
39 import static com.google.testing.littlemock.LittleMock.doReturn;
40 import static com.google.testing.littlemock.LittleMock.anyObject;
41 import com.google.testing.littlemock.AppDataDirGuesser;
42 import com.google.testing.littlemock.ArgumentCaptor;
43 import com.google.testing.littlemock.Captor;
44 import com.google.testing.littlemock.LittleMock;
45 import com.google.testing.littlemock.Mock;
46 
47 import java.io.File;
48 import java.io.ByteArrayOutputStream;
49 import java.io.InputStream;
50 import java.io.IOException;
51 import java.util.concurrent.Callable;
52 
53 
54 public class CameraTestCase<T extends Activity> extends ActivityInstrumentationTestCase2<T> {
55     protected CameraInfo mCameraInfo[];
56     protected CameraProxy mMockCamera[];
57     protected CameraInfo mOneCameraInfo[];
58     protected CameraProxy mOneMockCamera[];
59     private static Parameters mParameters;
60     private byte[] mBlankJpeg;
61     @Mock private CameraProxy mMockBackCamera;
62     @Mock private CameraProxy mMockFrontCamera;
63     @Captor private ArgumentCaptor<ShutterCallback> mShutterCallback;
64     @Captor private ArgumentCaptor<PictureCallback> mRawPictureCallback;
65     @Captor private ArgumentCaptor<PictureCallback> mJpegPictureCallback;
66     @Captor private ArgumentCaptor<AutoFocusCallback> mAutoFocusCallback;
67     Callable<Object> mAutoFocusCallable = new AutoFocusCallable();
68     Callable<Object> mTakePictureCallable = new TakePictureCallable();
69 
70     private class TakePictureCallable implements Callable<Object> {
71         @Override
call()72         public Object call() throws Exception {
73             Runnable runnable = new Runnable() {
74                 @Override
75                 public void run() {
76                     readBlankJpeg();
77                     Camera camera = mOneMockCamera[0].getCamera();
78                     mShutterCallback.getValue().onShutter();
79                     mRawPictureCallback.getValue().onPictureTaken(null, camera);
80                     mJpegPictureCallback.getValue().onPictureTaken(mBlankJpeg, camera);
81                 }
82             };
83             // Probably need some delay. Make sure shutter callback is called
84             // after onShutterButtonFocus(false).
85             getActivity().findViewById(R.id.gl_root_view).postDelayed(runnable, 50);
86             return null;
87         }
88    }
89 
90     private class AutoFocusCallable implements Callable<Object> {
91         @Override
call()92         public Object call() throws Exception {
93             Runnable runnable = new Runnable() {
94                 @Override
95                 public void run() {
96                     Camera camera = mOneMockCamera[0].getCamera();
97                     mAutoFocusCallback.getValue().onAutoFocus(true, camera);
98                 }
99             };
100             // Need some delay. Otherwise, focus callback will be run before
101             // onShutterButtonClick
102             getActivity().findViewById(R.id.gl_root_view).postDelayed(runnable, 50);
103             return null;
104         }
105    }
106 
CameraTestCase(Class<T> activityClass)107     public CameraTestCase(Class<T> activityClass) {
108         super(activityClass);
109     }
110 
111     @Override
setUp()112     protected void setUp() throws Exception {
113         super.setUp();
114         AppDataDirGuesser.setInstance(new AppDataDirGuesser() {
115             @Override
116             public File guessSuitableDirectoryForGeneratedClasses() {
117                 return getInstrumentation().getTargetContext().getCacheDir();
118             }
119         });
120         AppDataDirGuesser.getsInstance().guessSuitableDirectoryForGeneratedClasses();
121         LittleMock.initMocks(this);
122         mCameraInfo = new CameraInfo[2];
123         mCameraInfo[0] = new CameraInfo();
124         mCameraInfo[0].facing = CameraInfo.CAMERA_FACING_BACK;
125         mCameraInfo[1] = new CameraInfo();
126         mCameraInfo[1].facing = CameraInfo.CAMERA_FACING_FRONT;
127         mMockCamera = new CameraProxy[2];
128         mMockCamera[0] = mMockBackCamera;
129         mMockCamera[1] = mMockFrontCamera;
130         doReturn(getParameters()).when(mMockCamera[0]).getParameters();
131         doReturn(getParameters()).when(mMockCamera[1]).getParameters();
132 
133         mOneCameraInfo = new CameraInfo[1];
134         mOneCameraInfo[0] = new CameraInfo();
135         mOneCameraInfo[0].facing = CameraInfo.CAMERA_FACING_BACK;
136         mOneMockCamera = new CameraProxy[1];
137         mOneMockCamera[0] = mMockBackCamera;
138         doReturn(getParameters()).when(mOneMockCamera[0]).getParameters();
139 
140         // Mock takePicture call.
141         doAnswer(mTakePictureCallable).when(mMockBackCamera).takePicture(
142                 mShutterCallback.capture(), mRawPictureCallback.capture(),
143                 (PictureCallback) anyObject(), mJpegPictureCallback.capture());
144 
145         // Mock autoFocus call.
146         doAnswer(mAutoFocusCallable).when(mMockBackCamera).autoFocus(
147                 mAutoFocusCallback.capture());
148     }
149 
readBlankJpeg()150     private void readBlankJpeg() {
151         InputStream ins = getActivity().getResources().openRawResource(R.raw.blank);
152         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
153         int size = 0;
154 
155         // Read the entire resource into a local byte buffer.
156         byte[] buffer = new byte[1024];
157         try {
158             while((size = ins.read(buffer, 0, 1024)) >= 0){
159                 outputStream.write(buffer, 0, size);
160             }
161         } catch (IOException e) {
162             // ignore
163         } finally {
164             Util.closeSilently(ins);
165         }
166         mBlankJpeg = outputStream.toByteArray();
167     }
168 
169     @Override
tearDown()170     protected void tearDown() throws Exception {
171         super.tearDown();
172         CameraHolder.injectMockCamera(null,  null);
173     }
174 
internalTestFailToConnect()175     protected void internalTestFailToConnect() throws Exception {
176         CameraHolder.injectMockCamera(mCameraInfo, null);
177 
178         getActivity();
179         Instrumentation inst = getInstrumentation();
180         inst.waitForIdleSync();
181         inst.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER); // close dialog
182     }
183 
performClick(final int id)184     protected void performClick(final int id) {
185         Activity activity = getActivity();
186         getInstrumentation().waitForIdleSync();
187         assertNotNull(activity.findViewById(id));
188         Instrumentation inst = getInstrumentation();
189         inst.runOnMainSync(new Runnable() {
190             @Override
191             public void run() {
192                 View v = getActivity().findViewById(id);
193                 float x = (v.getLeft() + v.getRight()) / 2;
194                 float y = (v.getTop() + v.getBottom()) / 2;
195                 MotionEvent down = MotionEvent.obtain(0, 0,
196                         MotionEvent.ACTION_DOWN, x, y, 0, 0, 0, 0, 0, 0, 0);
197                 MotionEvent up = MotionEvent.obtain(0, 0,
198                         MotionEvent.ACTION_UP, x, y, 0, 0, 0, 0, 0, 0, 0);
199                 View parent = (View) v.getParent();
200                 parent.dispatchTouchEvent(down);
201                 parent.dispatchTouchEvent(up);
202             }
203         });
204         inst.waitForIdleSync();
205     }
206 
assertViewNotExist(int id)207     protected void assertViewNotExist(int id) {
208         Activity activity = getActivity();
209         getInstrumentation().waitForIdleSync();
210         assertNull(activity.findViewById(id));
211     }
212 
assertViewNotVisible(int id)213     protected void assertViewNotVisible(int id) {
214         Activity activity = getActivity();
215         getInstrumentation().waitForIdleSync();
216         View view = activity.findViewById(id);
217         assertTrue(view.getVisibility() != View.VISIBLE);
218     }
219 
getParameters()220     protected static Parameters getParameters() {
221         synchronized (CameraTestCase.class) {
222             if (mParameters == null) {
223                 mParameters = android.hardware.Camera.getEmptyParameters();
224                 mParameters.unflatten("preview-format-values=yuv420sp,yuv420p,yuv422i-yuyv,yuv420p;" +
225                         "preview-format=yuv420sp;" +
226                         "preview-size-values=800x480;preview-size=800x480;" +
227                         "picture-size-values=320x240;picture-size=320x240;" +
228                         "jpeg-thumbnail-size-values=320x240,0x0;jpeg-thumbnail-width=320;jpeg-thumbnail-height=240;" +
229                         "jpeg-thumbnail-quality=60;jpeg-quality=95;" +
230                         "preview-frame-rate-values=30,15;preview-frame-rate=30;" +
231                         "focus-mode-values=continuous-video,auto,macro,infinity,continuous-picture;focus-mode=auto;" +
232                         "preview-fps-range-values=(15000,30000);preview-fps-range=15000,30000;" +
233                         "scene-mode-values=auto,action,night;scene-mode=auto;" +
234                         "flash-mode-values=off,on,auto,torch;flash-mode=off;" +
235                         "whitebalance-values=auto,daylight,fluorescent,incandescent;whitebalance=auto;" +
236                         "effect-values=none,mono,sepia;effect=none;" +
237                         "zoom-supported=true;zoom-ratios=100,200,400;max-zoom=2;" +
238                         "picture-format-values=jpeg;picture-format=jpeg;" +
239                         "min-exposure-compensation=-30;max-exposure-compensation=30;" +
240                         "exposure-compensation=0;exposure-compensation-step=0.1;" +
241                         "horizontal-view-angle=40;vertical-view-angle=40;");
242             }
243         }
244         return mParameters;
245     }
246 }
247