com.parrot.cyclops.CameraView.java Source code

Java tutorial

Introduction

Here is the source code for com.parrot.cyclops.CameraView.java

Source

/**
 * Copyright (C) 2013-2015 Parrot
 *
 * authors: Andrei Mandychev <andrei.mandychev@parrot.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

package com.parrot.cyclops;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Notification;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
import android.view.ViewParent;

public class CameraView extends SurfaceView implements CameraCtrl.SyncCallback {

    public interface OnCameraStateListener extends CameraCtrl.SyncCallback {
        public void onOpen(boolean opened);

        public void onUpdateDisplay(int displayId);

        public void onClose();
    }

    private static final String TAG = "CameraView";
    private static final boolean DEBUG = true;

    private int mCameraId = Cyclops.REAR_CAMERA_ID;
    private Context mContext = null;
    private boolean mBound = false;
    private CyclopsService mCyclopsService = null;
    private CameraCtrl mCameraCtrl = null;
    private SurfaceHolder mSurfaceHolder = null; // Don't delete

    private OnCameraStateListener mCameraStateListener = null;

    private Method mSetTitleMethod = null;
    private boolean mIsClosingNeeded = false;
    private boolean mExtDisplay = false;

    private boolean mIsCameraMirrored = false;
    private boolean mIsCameraRotatedBy180 = false;

    public CameraView(Context context) {
        super(context);
        initCameraView(context);
    }

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initCameraView(context);
    }

    public CameraView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initCameraView(context);
    }

    private void initCameraView(Context context) {
        if (DEBUG)
            Log.d(TAG, "initCameraView");
        mContext = context;
        getHolder().addCallback(mSHCallback);
        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        // Use reflection to call a hidden method of SurfaceView added by Parrot.
        Class<?> params[] = new Class[1];
        params[0] = String.class;
        try {
            mSetTitleMethod = getClass().getSuperclass().getDeclaredMethod("setTitle", params);
        } catch (NoSuchMethodException e) {
            Log.e(TAG, e.toString());
            mSetTitleMethod = null;
        }
    }

    public void setCameraId(int cameraId) {
        mCameraId = cameraId;
    }

    public void setCameraStateListener(OnCameraStateListener listener) {
        mCameraStateListener = listener;
    }

    public int setCameraDisplay(int displayId) {
        mExtDisplay = (displayId == Cyclops.DISPLAY_TYPE_HDMI_TV) ? true : false;
        setVoutVideoView(mExtDisplay);
        int res = mCameraCtrl.setCameraDisplayId(displayId);
        if (mCameraStateListener != null && res == 0) {
            mCameraStateListener.onUpdateDisplay(displayId);
        }
        return res;
    }

    public int getCameraDisplay() {
        return mCameraCtrl.getCameraDisplayId();
    }

    public void start(boolean isClosingNeeded) {
        logdebug("start");
        mIsClosingNeeded = isClosingNeeded;
        // Bind to CyclopsService
        Intent intent = new Intent(mContext, CyclopsService.class);
        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    public void stop(boolean force) {
        logdebug("stop");
        if (mBound) {
            if (force) {
                if (mCameraCtrl.isCameraOpened()) {
                    mCameraCtrl.setSyncCallback(null);
                    mCameraCtrl.closeCamera();
                    if (mCameraStateListener != null) {
                        mCameraStateListener.onClose();
                    }
                }
            } else {
                if (mCameraCtrl.getCameraDisplayId() == Cyclops.DISPLAY_TYPE_HDMI_TV) {
                    startCyclopsServiceForeground();
                }
            }
            mBound = false;
            mContext.unbindService(mConnection);
        }
    }

    public void setCameraMirroring(boolean mirrored) {
        mIsCameraMirrored = mirrored;
    }

    public void setCameraRotationBy180(boolean rotatedBy180) {
        mIsCameraRotatedBy180 = rotatedBy180;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        logdebug("onTouchEvent");
        ViewParent parent = this.getParent();
        if (parent != null) {
            ViewGroup vg = (ViewGroup) parent;
            return vg.onTouchEvent(ev);
        }
        return super.onTouchEvent(ev);
    }

    SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() {

        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            logdebug("surfaceChanged");
        }

        public void surfaceCreated(SurfaceHolder holder) {
            logdebug("surfaceCreated");
            mSurfaceHolder = holder;
            //setBackgroundDrawable(getResources().getDrawable(R.drawable.bg));
            if (mCyclopsService == null) {
                return;
            }
            mCameraCtrl = mCyclopsService.obtainCameraCntrl(mCameraId);

            if (mCameraCtrl.isCameraOpened()) {
                if (mIsClosingNeeded) {
                    // close camera since it has a SurfaceHolder referencing to the previous CameraView
                    logdebug("Close camera since it has an old SurfaceHolder");
                    mCameraCtrl.closeCamera();
                    mIsClosingNeeded = false;
                } else {
                    logdebug("Camera is already opened, resume from background");
                    return;
                }
            }
            mCameraCtrl.setSyncCallback(CameraView.this);
            boolean opened = mCameraCtrl.openCamera(holder, mIsCameraMirrored, mIsCameraRotatedBy180);
            if (mCameraStateListener != null) {
                mCameraStateListener.onOpen(opened);
            }
            if (opened) {
                int displayId = getCameraDisplay();
                mExtDisplay = (displayId == Cyclops.DISPLAY_TYPE_HDMI_TV) ? true : false;
                // Set a title for SurfaceView if the camera was started on TV
                // from the beginning and don't have updated value
                setVoutVideoView(mExtDisplay);
                if (mCameraStateListener != null) {
                    mCameraStateListener.onUpdateDisplay(displayId);
                }
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            logdebug("surfaceDestroyed");
            if (mCyclopsService == null) {
                // If mCyclopsService is null it means that we didn't bind with 
                // CyclopsService and we didn't do anything with a camera
                // so we just return without any actions.
                return;
            }
            if (mExtDisplay) {
                logdebug("Camera is not closed, keep in background");
            } else {
                mCameraCtrl.setSyncCallback(null);
                mCameraCtrl.closeCamera();
                if (mCameraStateListener != null) {
                    mCameraStateListener.onClose();
                }
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            logdebug("onServiceConnected");
            CyclopsService.CyclopsBinder binder = (CyclopsService.CyclopsBinder) service;
            mCyclopsService = binder.getService();
            mBound = true;
            if (mCameraCtrl != null) {
                if (mCameraCtrl.isCameraOpened()) {
                    logdebug("stopForeground");
                    mCyclopsService.stopForeground(true);
                    // Update mirroring and rotation for camera which is on TV
                    if (mIsCameraMirrored == mCameraCtrl.isCameraMirrored()
                            || mIsCameraRotatedBy180 == mCameraCtrl.isCameraRotatedBy180()) {
                        mCameraCtrl.setCameraTransformation(mIsCameraMirrored, mIsCameraRotatedBy180);
                    }
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            logdebug("onServiceDisconnected");
            mCyclopsService = null;
        }
    };

    private void startCyclopsServiceForeground() {
        logdebug("startCyclopsServiceForeground");
        SystemProperties.setTvBusyByCyclops(true);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
                .setSmallIcon(R.drawable.ic_launcher).setContentTitle(mContext.getString(R.string.notif_title))
                .setContentText(mContext.getString(R.string.notif_text));
        PendingIntent pendingIntent;
        Intent intent = new Intent(mContext, CyclopsActivity.class);
        pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
        builder.setContentIntent(pendingIntent);
        builder.setPriority(NotificationCompat.PRIORITY_MAX);
        Notification notif = builder.build();
        notif.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_NO_CLEAR;
        mCyclopsService.startForeground(Cyclops.CYCLOPS_FOREGROUND_NOTIFICATION, notif);
    }

    private void setVoutVideoView(boolean vout) {
        try {
            // If a window of SurfaceView has a title VoutVideoView
            // than the surface wont be destroyed by WindowManagerService
            // while video is playing on external screen in background mode.
            String title = vout ? "VoutVideoView" : "VideoView";
            logdebug("set title to SurfaceView: " + title);
            mSetTitleMethod.invoke(this, title);
        } catch (IllegalAccessException e) {
            Log.e(TAG, e.toString());
        } catch (InvocationTargetException e) {
            Log.e(TAG, e.toString());
        }
    }

    @Override
    public void onSyncLost() {
        if (mCameraStateListener != null) {
            mCameraStateListener.onSyncLost();
        }
    }

    @Override
    public void onSyncFound() {
        if (mCameraStateListener != null) {
            mCameraStateListener.onSyncFound();
        }
    }

    public void takePicture() {
        if (mCameraCtrl != null) {
            mCameraCtrl.takePicture();
        }
    }

    public Uri getLastUriOfTakenPicture() {
        return (mCameraCtrl != null) ? mCameraCtrl.getLastUriOfTakenPicture() : null;
    }

    private void logdebug(String msg) {
        if (DEBUG)
            Log.d(TAG, "camId=" + mCameraId + ", " + msg);
    }

}