Home | History | Annotate | Download | only in facade
      1 /*
      2  * Copyright (C) 2017 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.googlecode.android_scripting.facade;
     18 
     19 import android.app.Service;
     20 import android.content.Intent;
     21 import android.hardware.Camera;
     22 import android.hardware.Camera.AutoFocusCallback;
     23 import android.hardware.Camera.Parameters;
     24 import android.hardware.Camera.PictureCallback;
     25 import android.net.Uri;
     26 import android.os.Bundle;
     27 import android.provider.MediaStore;
     28 import android.view.SurfaceHolder;
     29 import android.view.SurfaceHolder.Callback;
     30 import android.view.SurfaceView;
     31 import android.view.WindowManager;
     32 
     33 import com.googlecode.android_scripting.BaseApplication;
     34 import com.googlecode.android_scripting.FileUtils;
     35 import com.googlecode.android_scripting.FutureActivityTaskExecutor;
     36 import com.googlecode.android_scripting.Log;
     37 import com.googlecode.android_scripting.future.FutureActivityTask;
     38 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     39 import com.googlecode.android_scripting.rpc.Rpc;
     40 import com.googlecode.android_scripting.rpc.RpcDefault;
     41 import com.googlecode.android_scripting.rpc.RpcParameter;
     42 
     43 import java.io.File;
     44 import java.io.FileNotFoundException;
     45 import java.io.FileOutputStream;
     46 import java.io.IOException;
     47 import java.lang.reflect.Method;
     48 import java.util.concurrent.CountDownLatch;
     49 
     50 /**
     51  * Access Camera functions.
     52  *
     53  */
     54 public class CameraFacade extends RpcReceiver {
     55 
     56   private final Service mService;
     57   private final Parameters mParameters;
     58 
     59   private class BooleanResult {
     60     boolean mmResult = false;
     61   }
     62 
     63   public Camera openCamera(int cameraId) throws Exception {
     64     int sSdkLevel = Integer.parseInt(android.os.Build.VERSION.SDK);
     65     Camera result;
     66     if (sSdkLevel < 9) {
     67       result = Camera.open();
     68     } else {
     69       Method openCamera = Camera.class.getMethod("open", int.class);
     70       result = (Camera) openCamera.invoke(null, cameraId);
     71     }
     72     return result;
     73   }
     74 
     75   public CameraFacade(FacadeManager manager) throws Exception {
     76     super(manager);
     77     mService = manager.getService();
     78     Camera camera = openCamera(0);
     79     try {
     80       mParameters = camera.getParameters();
     81     } finally {
     82       camera.release();
     83     }
     84   }
     85 
     86   @Rpc(description = "Take a picture and save it to the specified path.", returns = "A map of Booleans autoFocus and takePicture where True indicates success. cameraId also included.")
     87   public Bundle cameraCapturePicture(
     88       @RpcParameter(name = "targetPath") final String targetPath,
     89       @RpcParameter(name = "useAutoFocus") @RpcDefault("true") Boolean useAutoFocus,
     90       @RpcParameter(name = "cameraId", description = "Id of camera to use. SDK 9") @RpcDefault("0") Integer cameraId)
     91       throws Exception {
     92     final BooleanResult autoFocusResult = new BooleanResult();
     93     final BooleanResult takePictureResult = new BooleanResult();
     94     Camera camera = openCamera(cameraId);
     95     camera.setParameters(mParameters);
     96 
     97     try {
     98       Method method = camera.getClass().getMethod("setDisplayOrientation", int.class);
     99       method.invoke(camera, 90);
    100     } catch (Exception e) {
    101       Log.e(e);
    102     }
    103 
    104     try {
    105       FutureActivityTask<SurfaceHolder> previewTask = setPreviewDisplay(camera);
    106       camera.startPreview();
    107       if (useAutoFocus) {
    108         autoFocus(autoFocusResult, camera);
    109       }
    110       takePicture(new File(targetPath), takePictureResult, camera);
    111       previewTask.finish();
    112     } catch (Exception e) {
    113       Log.e(e);
    114     } finally {
    115       camera.release();
    116     }
    117 
    118     Bundle result = new Bundle();
    119     result.putBoolean("autoFocus", autoFocusResult.mmResult);
    120     result.putBoolean("takePicture", takePictureResult.mmResult);
    121     result.putInt("cameraId", cameraId);
    122     return result;
    123   }
    124 
    125   private FutureActivityTask<SurfaceHolder> setPreviewDisplay(Camera camera) throws IOException,
    126       InterruptedException {
    127     FutureActivityTask<SurfaceHolder> task = new FutureActivityTask<SurfaceHolder>() {
    128       @Override
    129       public void onCreate() {
    130         super.onCreate();
    131         final SurfaceView view = new SurfaceView(getActivity());
    132         getActivity().setContentView(view);
    133         getActivity().getWindow().setSoftInputMode(
    134             WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
    135         view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    136         view.getHolder().addCallback(new Callback() {
    137           @Override
    138           public void surfaceDestroyed(SurfaceHolder holder) {
    139           }
    140 
    141           @Override
    142           public void surfaceCreated(SurfaceHolder holder) {
    143             setResult(view.getHolder());
    144           }
    145 
    146           @Override
    147           public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    148           }
    149         });
    150       }
    151     };
    152     FutureActivityTaskExecutor taskQueue =
    153         ((BaseApplication) mService.getApplication()).getTaskExecutor();
    154     taskQueue.execute(task);
    155     camera.setPreviewDisplay(task.getResult());
    156     return task;
    157   }
    158 
    159   private void takePicture(final File file, final BooleanResult takePictureResult,
    160       final Camera camera) throws InterruptedException {
    161     final CountDownLatch latch = new CountDownLatch(1);
    162     camera.takePicture(null, null, new PictureCallback() {
    163       @Override
    164       public void onPictureTaken(byte[] data, Camera camera) {
    165         if (!FileUtils.makeDirectories(file.getParentFile(), 0755)) {
    166           takePictureResult.mmResult = false;
    167           return;
    168         }
    169         try {
    170           FileOutputStream output = new FileOutputStream(file);
    171           output.write(data);
    172           output.close();
    173           takePictureResult.mmResult = true;
    174         } catch (FileNotFoundException e) {
    175           Log.e("Failed to save picture.", e);
    176           takePictureResult.mmResult = false;
    177           return;
    178         } catch (IOException e) {
    179           Log.e("Failed to save picture.", e);
    180           takePictureResult.mmResult = false;
    181           return;
    182         } finally {
    183           latch.countDown();
    184         }
    185       }
    186     });
    187     latch.await();
    188   }
    189 
    190   private void autoFocus(final BooleanResult result, final Camera camera)
    191       throws InterruptedException {
    192     final CountDownLatch latch = new CountDownLatch(1);
    193     {
    194       camera.autoFocus(new AutoFocusCallback() {
    195         @Override
    196         public void onAutoFocus(boolean success, Camera camera) {
    197           result.mmResult = success;
    198           latch.countDown();
    199         }
    200       });
    201       latch.await();
    202     }
    203   }
    204 
    205   @Override
    206   public void shutdown() {
    207     // Nothing to clean up.
    208   }
    209 
    210   @Rpc(description = "Starts the image capture application to take a picture and saves it to the specified path.")
    211   public void cameraInteractiveCapturePicture(
    212       @RpcParameter(name = "targetPath") final String targetPath) {
    213     Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    214     File file = new File(targetPath);
    215     intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
    216     AndroidFacade facade = mManager.getReceiver(AndroidFacade.class);
    217     facade.startActivityForResult(intent);
    218   }
    219 }
    220