| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.hcgallery; |
| |
| import android.app.ActionBar; |
| import android.app.Activity; |
| import android.app.Fragment; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.hardware.Camera; |
| import android.hardware.Camera.CameraInfo; |
| import android.hardware.Camera.Size; |
| import android.os.Bundle; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.Menu; |
| import android.view.MenuInflater; |
| import android.view.MenuItem; |
| import android.view.SurfaceHolder; |
| import android.view.SurfaceView; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import java.io.IOException; |
| import java.util.List; |
| |
| public class CameraFragment extends Fragment { |
| |
| private Preview mPreview; |
| Camera mCamera; |
| int mNumberOfCameras; |
| int mCurrentCamera; // Camera ID currently chosen |
| int mCameraCurrentlyLocked; // Camera ID that's actually acquired |
| |
| // The first rear facing camera |
| int mDefaultCameraId; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| // Create a container that will hold a SurfaceView for camera previews |
| mPreview = new Preview(this.getActivity()); |
| |
| // Find the total number of cameras available |
| mNumberOfCameras = Camera.getNumberOfCameras(); |
| |
| // Find the ID of the rear-facing ("default") camera |
| CameraInfo cameraInfo = new CameraInfo(); |
| for (int i = 0; i < mNumberOfCameras; i++) { |
| Camera.getCameraInfo(i, cameraInfo); |
| if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) { |
| mCurrentCamera = mDefaultCameraId = i; |
| } |
| } |
| setHasOptionsMenu(mNumberOfCameras > 1); |
| } |
| |
| @Override |
| public void onActivityCreated(Bundle savedInstanceState) { |
| super.onActivityCreated(savedInstanceState); |
| // Add an up arrow to the "home" button, indicating that the button will go "up" |
| // one activity in the app's Activity heirarchy. |
| // Calls to getActionBar() aren't guaranteed to return the ActionBar when called |
| // from within the Fragment's onCreate method, because the Window's decor hasn't been |
| // initialized yet. Either call for the ActionBar reference in Activity.onCreate() |
| // (after the setContentView(...) call), or in the Fragment's onActivityCreated method. |
| Activity activity = this.getActivity(); |
| ActionBar actionBar = activity.getActionBar(); |
| actionBar.setDisplayHomeAsUpEnabled(true); |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| return mPreview; |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| // Use mCurrentCamera to select the camera desired to safely restore |
| // the fragment after the camera has been changed |
| mCamera = Camera.open(mCurrentCamera); |
| mCameraCurrentlyLocked = mCurrentCamera; |
| mPreview.setCamera(mCamera); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| |
| // Because the Camera object is a shared resource, it's very |
| // important to release it when the activity is paused. |
| if (mCamera != null) { |
| mPreview.setCamera(null); |
| mCamera.release(); |
| mCamera = null; |
| } |
| } |
| |
| @Override |
| public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { |
| if (mNumberOfCameras > 1) { |
| // Inflate our menu which can gather user input for switching camera |
| inflater.inflate(R.menu.camera_menu, menu); |
| } else { |
| super.onCreateOptionsMenu(menu, inflater); |
| } |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| // Handle item selection |
| switch (item.getItemId()) { |
| case R.id.menu_switch_cam: |
| // Release this camera -> mCameraCurrentlyLocked |
| if (mCamera != null) { |
| mCamera.stopPreview(); |
| mPreview.setCamera(null); |
| mCamera.release(); |
| mCamera = null; |
| } |
| |
| // Acquire the next camera and request Preview to reconfigure |
| // parameters. |
| mCurrentCamera = (mCameraCurrentlyLocked + 1) % mNumberOfCameras; |
| mCamera = Camera.open(mCurrentCamera); |
| mCameraCurrentlyLocked = mCurrentCamera; |
| mPreview.switchCamera(mCamera); |
| |
| // Start the preview |
| mCamera.startPreview(); |
| return true; |
| case android.R.id.home: |
| Intent intent = new Intent(this.getActivity(), MainActivity.class); |
| intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP); |
| startActivity(intent); |
| return true; |
| |
| default: |
| return super.onOptionsItemSelected(item); |
| } |
| } |
| } |
| |
| // ---------------------------------------------------------------------- |
| |
| /** |
| * A simple wrapper around a Camera and a SurfaceView that renders a centered |
| * preview of the Camera to the surface. We need to center the SurfaceView |
| * because not all devices have cameras that support preview sizes at the same |
| * aspect ratio as the device's display. |
| */ |
| class Preview extends ViewGroup implements SurfaceHolder.Callback { |
| private final String TAG = "Preview"; |
| |
| SurfaceView mSurfaceView; |
| SurfaceHolder mHolder; |
| Size mPreviewSize; |
| List<Size> mSupportedPreviewSizes; |
| Camera mCamera; |
| boolean mSurfaceCreated = false; |
| |
| Preview(Context context) { |
| super(context); |
| |
| mSurfaceView = new SurfaceView(context); |
| addView(mSurfaceView); |
| |
| // Install a SurfaceHolder.Callback so we get notified when the |
| // underlying surface is created and destroyed. |
| mHolder = mSurfaceView.getHolder(); |
| mHolder.addCallback(this); |
| mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); |
| } |
| |
| public void setCamera(Camera camera) { |
| mCamera = camera; |
| if (mCamera != null) { |
| mSupportedPreviewSizes = mCamera.getParameters() |
| .getSupportedPreviewSizes(); |
| if (mSurfaceCreated) requestLayout(); |
| } |
| } |
| |
| public void switchCamera(Camera camera) { |
| setCamera(camera); |
| try { |
| camera.setPreviewDisplay(mHolder); |
| } catch (IOException exception) { |
| Log.e(TAG, "IOException caused by setPreviewDisplay()", exception); |
| } |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| // We purposely disregard child measurements because act as a |
| // wrapper to a SurfaceView that centers the camera preview instead |
| // of stretching it. |
| final int width = resolveSize(getSuggestedMinimumWidth(), |
| widthMeasureSpec); |
| final int height = resolveSize(getSuggestedMinimumHeight(), |
| heightMeasureSpec); |
| setMeasuredDimension(width, height); |
| |
| if (mSupportedPreviewSizes != null) { |
| mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, |
| height); |
| } |
| |
| if (mCamera != null) { |
| Camera.Parameters parameters = mCamera.getParameters(); |
| parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); |
| |
| mCamera.setParameters(parameters); |
| } |
| } |
| |
| @Override |
| protected void onLayout(boolean changed, int l, int t, int r, int b) { |
| if (getChildCount() > 0) { |
| final View child = getChildAt(0); |
| |
| final int width = r - l; |
| final int height = b - t; |
| |
| int previewWidth = width; |
| int previewHeight = height; |
| if (mPreviewSize != null) { |
| previewWidth = mPreviewSize.width; |
| previewHeight = mPreviewSize.height; |
| } |
| |
| // Center the child SurfaceView within the parent. |
| if (width * previewHeight > height * previewWidth) { |
| final int scaledChildWidth = previewWidth * height |
| / previewHeight; |
| child.layout((width - scaledChildWidth) / 2, 0, |
| (width + scaledChildWidth) / 2, height); |
| } else { |
| final int scaledChildHeight = previewHeight * width |
| / previewWidth; |
| child.layout(0, (height - scaledChildHeight) / 2, width, |
| (height + scaledChildHeight) / 2); |
| } |
| } |
| } |
| |
| public void surfaceCreated(SurfaceHolder holder) { |
| // The Surface has been created, acquire the camera and tell it where |
| // to draw. |
| try { |
| if (mCamera != null) { |
| mCamera.setPreviewDisplay(holder); |
| } |
| } catch (IOException exception) { |
| Log.e(TAG, "IOException caused by setPreviewDisplay()", exception); |
| } |
| if (mPreviewSize == null) requestLayout(); |
| mSurfaceCreated = true; |
| } |
| |
| public void surfaceDestroyed(SurfaceHolder holder) { |
| // Surface will be destroyed when we return, so stop the preview. |
| if (mCamera != null) { |
| mCamera.stopPreview(); |
| } |
| } |
| |
| private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) { |
| final double ASPECT_TOLERANCE = 0.1; |
| double targetRatio = (double) w / h; |
| if (sizes == null) |
| return null; |
| |
| Size optimalSize = null; |
| double minDiff = Double.MAX_VALUE; |
| |
| int targetHeight = h; |
| |
| // Try to find an size match aspect ratio and size |
| for (Size size : sizes) { |
| double ratio = (double) size.width / size.height; |
| if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) |
| continue; |
| if (Math.abs(size.height - targetHeight) < minDiff) { |
| optimalSize = size; |
| minDiff = Math.abs(size.height - targetHeight); |
| } |
| } |
| |
| // Cannot find the one match the aspect ratio, ignore the requirement |
| if (optimalSize == null) { |
| minDiff = Double.MAX_VALUE; |
| for (Size size : sizes) { |
| if (Math.abs(size.height - targetHeight) < minDiff) { |
| optimalSize = size; |
| minDiff = Math.abs(size.height - targetHeight); |
| } |
| } |
| } |
| return optimalSize; |
| } |
| |
| public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { |
| // Now that the size is known, set up the camera parameters and begin |
| // the preview. |
| Camera.Parameters parameters = mCamera.getParameters(); |
| parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height); |
| requestLayout(); |
| |
| mCamera.setParameters(parameters); |
| mCamera.startPreview(); |
| } |
| |
| } |