本文主要记录分析Android 系统相机部分源码
- 调用系统相机
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
startActivityForResult(intent, 1);
Intent 的action是 MediaStore.ACTION_IMAGE_CAPTURE
- 系统响应的类是(调用系统相机,不考虑第三方注册,比如这篇注册为系统相机)
- 在线源码地址:http://androidxref.com/(源码见最下面)
- 调用类名:com.android.camera.Camera
- 源码版本 7.1.1_r6(android最新版)
- 类全路径:/packages/apps/LegacyCamera/src/com/android/camera/Camera.java
[注意] 不同android版本文件路径不一样,比如:
2.3.7 :/packages/apps/Camera/src/com/android/camera/Camera.java
4.0.3_r1 : /packages/apps/LegacyCamera/src/com/android/camera/Camera.java
只关心获取Intent参数部分和返回结果部分
获取Intent参数
- mQuickCapture = getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
- private boolean isImageCaptureIntent() {
return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action));
} - private void setupCaptureParams() {
Bundle myExtras = getIntent().getExtras();
if (myExtras != null) {
mSaveUri = (Uri) myExtras.getParcelable(MediaStore.EXTRA_OUTPUT);
mCropValue = myExtras.getString(“crop”);
}
}
可以看出只支持三个参数 - 根据action 判断是否 ImageCapture
- 根据参数 android.intent.extra.quickCapture to return as soon as capturing is completed.
- 输出路径 MediaStore.EXTRA_OUTPUT
- “crop” .equals(“circle”) 是裁剪使用的,也就是说支持直接拍完照裁剪
返回结果 doAttach() 函数里
需要裁剪就内部调用裁剪功能,不需要裁剪时,判断是否有输出Uri,有输出就直接保存,没有就返回BitmapsetResultEx(RESULT_OK, new Intent("inline-data").putExtra("data", bitmap));
系统自带的人脸识别
Camera.setFaceDetectionListener(FaceDetectionListener listener)
在回调里更新FaceView的位置
public void onFaceDetection(Face[] faces, android.hardware.Camera camera);
FaceView 源码位置 :
/packages/apps/LegacyCamera/src/com/android/camera/ui/FaceView.java
不过这个自带的人脸识别已经弃用了,当然其功能还是可以使用的。关于默认打开前置摄像头
从源代码里,Intent不支持传参数控制开哪个摄像头,但是网上有代码可以支持部分手机
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra("android.intent.extras.CAMERA_FACING", android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); // 调用前置摄像头 startActivityForResult(intent, 1);
源码里控制哪个相机打开的代码的是 Camera.java#getPreferredCameraId(),而里面是
1.从SharePreference里获得存储的默认的cameraId,应该是部分系统应用商可以在此处做文章,这只适用于Android Source的开发者,对于普通app开发,原谅我还没有找到好方式注入
2.确实可以Intent传参数打开摄像头,此处修复上文一个bug,
代码在/packages/apps/LegacyCamera/src/com/android/camera/Util.java#getCameraFacingIntentExtras , string tag 是 “android.intent.extras.CAMERA_FACING”,
[附] 源代码:/packages/apps/LegacyCamera/src/com/android/camera/Camera.java
package com.android.camera;
import com.android.camera.ui.CameraPicker;
import com.android.camera.ui.FaceView;
import com.android.camera.ui.IndicatorControlContainer;
import com.android.camera.ui.PopupManager;
import com.android.camera.ui.Rotatable;
import com.android.camera.ui.RotateImageView;
import com.android.camera.ui.RotateLayout;
import com.android.camera.ui.RotateTextToast;
import com.android.camera.ui.SharePopup;
import com.android.camera.ui.ZoomControl;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.graphics.Bitmap;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Face;
import android.hardware.Camera.FaceDetectionListener;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.location.Location;
import android.media.CameraProfile;
import android.media.MediaActionSound;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Formatter;
import java.util.List;
/** The Camera activity which can preview and take pictures. */
public class Camera extends ActivityBase implements FocusManager.Listener,
View.OnTouchListener, ShutterButton.OnShutterButtonListener,
SurfaceHolder.Callback, ModePicker.OnModeChangeListener,
FaceDetectionListener, CameraPreference.OnPreferenceChangedListener,
LocationManager.Listener {
private static final String TAG = "camera";
private static final int CROP_MSG = 1;
private static final int FIRST_TIME_INIT = 2;
private static final int CLEAR_SCREEN_DELAY = 3;
private static final int SET_CAMERA_PARAMETERS_WHEN_IDLE = 4;
private static final int CHECK_DISPLAY_ROTATION = 5;
private static final int SHOW_TAP_TO_FOCUS_TOAST = 6;
private static final int UPDATE_THUMBNAIL = 7;
// The subset of parameters we need to update in setCameraParameters().
private static final int UPDATE_PARAM_INITIALIZE = 1;
private static final int UPDATE_PARAM_ZOOM = 2;
private static final int UPDATE_PARAM_PREFERENCE = 4;
private static final int UPDATE_PARAM_ALL = -1;
// When setCameraParametersWhenIdle() is called, we accumulate the subsets
// needed to be updated in mUpdateSet.
private int mUpdateSet;
private static final int SCREEN_DELAY = 2 * 60 * 1000;
private static final int ZOOM_STOPPED = 0;
private static final int ZOOM_START = 1;
private static final int ZOOM_STOPPING = 2;
private int mZoomState = ZOOM_STOPPED;
private boolean mSmoothZoomSupported = false;
private int mZoomValue; // The current zoom value.
private int mZoomMax;
private int mTargetZoomValue;
private ZoomControl mZoomControl;
private Parameters mParameters;
private Parameters mInitialParams;
private boolean mFocusAreaSupported;
private boolean mMeteringAreaSupported;
private boolean mAeLockSupported;
private boolean mAwbLockSupported;
private MyOrientationEventListener mOrientationListener;
// The degrees of the device rotated clockwise from its natural orientation.
private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
// The orientation compensation for icons and thumbnails. Ex: if the value
// is 90, the UI components should be rotated 90 degrees counter-clockwise.
private int mOrientationCompensation = 0;
private ComboPreferences mPreferences;
private static final String sTempCropFilename = "crop-temp";
private ContentProviderClient mMediaProviderClient;
private SurfaceHolder mSurfaceHolder = null;
private ShutterButton mShutterButton;
private GestureDetector mPopupGestureDetector;
private boolean mOpenCameraFail = false;
private boolean mCameraDisabled = false;
private boolean mFaceDetectionStarted = false;
private View mPreviewPanel; // The container of PreviewFrameLayout.
private PreviewFrameLayout mPreviewFrameLayout;
private View mPreviewFrame; // Preview frame area.
private RotateDialogController mRotateDialog;
// A popup window that contains a bigger thumbnail and a list of apps to share.
private SharePopup mSharePopup;
// The bitmap of the last captured picture thumbnail and the URI of the
// original picture.
private Thumbnail mThumbnail;
// An imageview showing showing the last captured picture thumbnail.
private RotateImageView mThumbnailView;
private ModePicker mModePicker;
private FaceView mFaceView;
private RotateLayout mFocusAreaIndicator;
private Rotatable mReviewCancelButton;
private Rotatable mReviewDoneButton;
// mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
private String mCropValue;
private Uri mSaveUri;
// Small indicators which show the camera settings in the viewfinder.
private TextView mExposureIndicator;
private ImageView mGpsIndicator;
private ImageView mFlashIndicator;
private ImageView mSceneIndicator;
private ImageView mWhiteBalanceIndicator;
private ImageView mFocusIndicator;
// A view group that contains all the small indicators.
private Rotatable mOnScreenIndicators;
// We use a thread in ImageSaver to do the work of saving images and
// generating thumbnails. This reduces the shot-to-shot time.
private ImageSaver mImageSaver;
private MediaActionSound mCameraSound;
private Runnable mDoSnapRunnable = new Runnable() {
public void run() {
onShutterButtonClick();
}
};
private final StringBuilder mBuilder = new StringBuilder();
private final Formatter mFormatter = new Formatter(mBuilder);
private final Object[] mFormatterArgs = new Object[1];
/**
* An unpublished intent flag requesting to return as soon as capturing
* is completed.
*
* TODO: consider publishing by moving into MediaStore.
*/
private static final String EXTRA_QUICK_CAPTURE =
"android.intent.extra.quickCapture";
// The display rotation in degrees. This is only valid when mCameraState is
// not PREVIEW_STOPPED.
private int mDisplayRotation;
// The value for android.hardware.Camera.setDisplayOrientation.
private int mDisplayOrientation;
private boolean mPausing;
private boolean mFirstTimeInitialized;
private boolean mIsImageCaptureIntent;
private static final int PREVIEW_STOPPED = 0;
private static final int IDLE = 1; // preview is active
// Focus is in progress. The exact focus state is in Focus.java.
private static final int FOCUSING = 2;
private static final int SNAPSHOT_IN_PROGRESS = 3;
private int mCameraState = PREVIEW_STOPPED;
private boolean mSnapshotOnIdle = false;
private ContentResolver mContentResolver;
private boolean mDidRegister = false;
private LocationManager mLocationManager;
private final ShutterCallback mShutterCallback = new ShutterCallback();
private final PostViewPictureCallback mPostViewPictureCallback =
new PostViewPictureCallback();
private final RawPictureCallback mRawPictureCallback =
new RawPictureCallback();
private final AutoFocusCallback mAutoFocusCallback =
new AutoFocusCallback();
private final ZoomListener mZoomListener = new ZoomListener();
private final CameraErrorCallback mErrorCallback = new CameraErrorCallback();
private long mFocusStartTime;
private long mCaptureStartTime;
private long mShutterCallbackTime;
private long mPostViewPictureCallbackTime;
private long mRawPictureCallbackTime;
private long mJpegPictureCallbackTime;
private long mOnResumeTime;
private long mPicturesRemaining;
private byte[] mJpegImageData;
// These latency time are for the CameraLatency test.
public long mAutoFocusTime;
public long mShutterLag;
public long mShutterToPictureDisplayedTime;
public long mPictureDisplayedToJpegCallbackTime;
public long mJpegCallbackFinishTime;
// This handles everything about focus.
private FocusManager mFocusManager;
private String mSceneMode;
private Toast mNotSelectableToast;
private Toast mNoShareToast;
private final Handler mHandler = new MainHandler();
private IndicatorControlContainer mIndicatorControlContainer;
private PreferenceGroup mPreferenceGroup;
// multiple cameras support
private int mNumberOfCameras;
private int mCameraId;
private int mFrontCameraId;
private int mBackCameraId;
private boolean mQuickCapture;
/**
* This Handler is used to post message back onto the main thread of the
* application
*/
private class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CLEAR_SCREEN_DELAY: {
getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
break;
}
case FIRST_TIME_INIT: {
initializeFirstTime();
break;
}
case SET_CAMERA_PARAMETERS_WHEN_IDLE: {
setCameraParametersWhenIdle(0);
break;
}
case CHECK_DISPLAY_ROTATION: {
// Set the display orientation if display rotation has changed.
// Sometimes this happens when the device is held upside
// down and camera app is opened. Rotation animation will
// take some time and the rotation value we have got may be
// wrong. Framework does not have a callback for this now.
if (Util.getDisplayRotation(Camera.this) != mDisplayRotation) {
setDisplayOrientation();
}
if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) {
mHandler.sendEmptyMessageDelayed(CHECK_DISPLAY_ROTATION, 100);
}
break;
}
case SHOW_TAP_TO_FOCUS_TOAST: {
showTapToFocusToast();
break;
}
case UPDATE_THUMBNAIL: {
mImageSaver.updateThumbnail();
break;
}
}
}
}
private void resetExposureCompensation() {
String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE,
CameraSettings.EXPOSURE_DEFAULT_VALUE);
if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) {
Editor editor = mPreferences.edit();
editor.putString(CameraSettings.KEY_EXPOSURE, "0");
editor.apply();
if (mIndicatorControlContainer != null) {
mIndicatorControlContainer.reloadPreferences();
}
}
}
private void keepMediaProviderInstance() {
// We want to keep a reference to MediaProvider in camera's lifecycle.
// TODO: Utilize mMediaProviderClient instance to replace
// ContentResolver calls.
if (mMediaProviderClient == null) {
mMediaProviderClient = getContentResolver()
.acquireContentProviderClient(MediaStore.AUTHORITY);
}
}
// Snapshots can only be taken after this is called. It should be called
// once only. We could have done these things in onCreate() but we want to
// make preview screen appear as soon as possible.