/*
* Copyright 2013 wada811<at.wada811@gmail.com>
*
* 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 at.wada811.utils;
import android.annotation.TargetApi;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.media.CamcorderProfile;
import android.os.Build;
import at.wada811.android.library.R;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
public class CameraUtils {
private static final boolean DEBUG = false;
/**
* カメラ機能を持っているか
*
* @param context
* @return
*/
public static boolean hasFeatureCamera(Context context){
boolean hasCamera = false;
if(PreferenceUtils.contains(context, PackageManager.FEATURE_CAMERA)){
hasCamera = PreferenceUtils.getBoolean(context, PackageManager.FEATURE_CAMERA, hasCamera);
}else{
PackageManager packageManager = context.getApplicationContext().getPackageManager();
hasCamera = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
PreferenceUtils.putBoolean(context, PackageManager.FEATURE_CAMERA, hasCamera);
}
return hasCamera;
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static boolean isCameraDisabled(Context context){
if(AndroidUtils.isMoreThanBuildVersion(Build.VERSION_CODES.ICE_CREAM_SANDWICH)){
DevicePolicyManager devicePolicyManager = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE);
return devicePolicyManager.getCameraDisabled(null);
}else{
return false;
}
}
/**
* オートフォーカス機能を持っているか
*
* @param context
* @return
*/
public static boolean hasFeatureAutoFocus(Context context){
boolean hasAutoFocus = false;
if(PreferenceUtils.contains(context, PackageManager.FEATURE_CAMERA_AUTOFOCUS)){
hasAutoFocus = PreferenceUtils.getBoolean(context, PackageManager.FEATURE_CAMERA_AUTOFOCUS, hasAutoFocus);
}else{
PackageManager packageManager = context.getApplicationContext().getPackageManager();
hasAutoFocus = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS);
PreferenceUtils.putBoolean(context, PackageManager.FEATURE_CAMERA_AUTOFOCUS, hasAutoFocus);
}
return hasAutoFocus;
}
/**
* カメラ情報を取得する
*
* @param nCameraId
* @return
*/
public static CameraInfo getCameraInfo(int nCameraId){
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(nCameraId, info);
return info;
}
/**
* 背面カメラかどうかを返す
*
* @param info
* @return
*/
public static boolean isBackCamera(CameraInfo info){
return info.facing == CameraInfo.CAMERA_FACING_BACK;
}
/**
* カメラの向きを取得する
*
* @param activity
* @param nCameraId
* @return displayOrientation
*/
public static int getCameraDisplayOrientation(Context context, CameraInfo info){
if(AndroidUtils.isMoreThanBuildVersion(Build.VERSION_CODES.GINGERBREAD)){
int degrees = DisplayUtils.getRotationDegrees(context);
if(DEBUG){
LogUtils.v("degrees: " + degrees);
}
int displayOrientation;
if(CameraUtils.isBackCamera(info)){
// アウトカメラ
if(DEBUG){
LogUtils.v(String.valueOf(degrees));
}
if(DEBUG){
LogUtils.v(String.valueOf(info.orientation));
}
displayOrientation = (360 - degrees + info.orientation) % 360;
if(DEBUG){
LogUtils.v(String.valueOf(displayOrientation));
}
displayOrientation = (360 + displayOrientation) % 360;
if(DEBUG){
LogUtils.v(String.valueOf(displayOrientation));
}
}else{
// インカメラ
if(DEBUG){
LogUtils.v(String.valueOf(degrees));
}
if(DEBUG){
LogUtils.v(String.valueOf(info.orientation));
}
displayOrientation = (360 - degrees - info.orientation) % 360;
if(DEBUG){
LogUtils.v(String.valueOf(displayOrientation));
}
displayOrientation = (360 + displayOrientation) % 360;
if(DEBUG){
LogUtils.v(String.valueOf(displayOrientation));
}
}
return displayOrientation;
}
return 90;
}
/**
* 画像の向きを取得する
*
* @param cameraId
* @param displayOrientation
* @return pictureOrientaion
*/
public static int getPictureOrientaion(CameraInfo info, int displayOrientation){
int pictureOrientaion;
if(CameraUtils.isBackCamera(info)){
pictureOrientaion = displayOrientation;
}else{
pictureOrientaion = (360 - displayOrientation) % 360;
}
return pictureOrientaion;
}
public static Size getPictureSize(Context context, Camera camera){
final List<Size> sizes = camera.getParameters().getSupportedPictureSizes();
final double ASPECT_TOLERANCE = 0.07;
final boolean isPortrait = DisplayUtils.isPortrait(context);
final int width = DisplayUtils.getWidth(context);
final int height = DisplayUtils.getHeight(context);
if(DEBUG){
LogUtils.v("width: " + width);
}
if(DEBUG){
LogUtils.v("height: " + height);
}
final double targetRatio = isPortrait ? (double)height / width : (double)width / height;
final int targetHeight = isPortrait ? width : height;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
if(DEBUG){
LogUtils.v("targetRatio: " + targetRatio);
}
for(Size size : sizes){
double pictureRatio = isPortrait ? (double)size.height / size.width : (double)size.width / size.height;
pictureRatio = (double)size.width / size.height;
if(DEBUG){
LogUtils.v("size.width: " + size.width);
}
if(DEBUG){
LogUtils.v("size.height: " + size.height);
}
if(DEBUG){
LogUtils.v("pictureRatio: " + pictureRatio);
}
if(Math.abs(pictureRatio - targetRatio) > ASPECT_TOLERANCE){
continue;
}
if(Math.abs(size.height - targetHeight) < minDiff){
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
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);
}
}
}
if(DEBUG){
LogUtils.v("optimalSize.width: " + optimalSize.width);
}
if(DEBUG){
LogUtils.v("optimalSize.height: " + optimalSize.height);
}
return optimalSize;
}
/**
* 撮影サイズをセットする
*
* @param camera
* @param pictureSize
*/
public static void setPictureSize(Camera camera, Size pictureSize){
Parameters params = camera.getParameters();
params.setPictureSize(pictureSize.width, pictureSize.height);
camera.setParameters(params);
}
/**
* Returns the best preview size
*
* @param context
* @param camera
*/
public static Size getOptimalPreviewSize(Context context, Camera camera){
final List<Size> sizes = camera.getParameters().getSupportedPreviewSizes();
final double ASPECT_TOLERANCE = 0.07;
final boolean isPortrait = DisplayUtils.isPortrait(context);
final Size pictureSize = camera.getParameters().getPictureSize();
final int width = pictureSize.width;
final int height = pictureSize.height;
if(DEBUG){
LogUtils.v("width: " + width);
}
if(DEBUG){
LogUtils.v("height: " + height);
}
final double targetRatio = isPortrait ? (double)height / width : (double)width / height;
final int targetHeight = isPortrait ? width : height;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
if(DEBUG){
LogUtils.v("targetRatio: " + targetRatio);
}
for(Size size : sizes){
double previewRatio = isPortrait ? (double)size.height / size.width : (double)size.width / size.height;
previewRatio = (double)size.width / size.height;
if(DEBUG){
LogUtils.v("size.width: " + size.width);
}
if(DEBUG){
LogUtils.v("size.height: " + size.height);
}
if(DEBUG){
LogUtils.v("previewRatio: " + previewRatio);
}
if(Math.abs(previewRatio - targetRatio) > ASPECT_TOLERANCE){
continue;
}
if(Math.abs(size.height - targetHeight) < minDiff){
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
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);
}
}
}
if(DEBUG){
LogUtils.v("optimalSize.width: " + optimalSize.width);
}
if(DEBUG){
LogUtils.v("optimalSize.height: " + optimalSize.height);
}
return optimalSize;
}
/**
* プレビュー画面のサイズをセットする
*
* @param camera
* @param previewSize
*/
public static void setPreviewSize(Camera camera, Size previewSize){
Parameters params = camera.getParameters();
params.setPreviewSize(previewSize.width, previewSize.height);
camera.setParameters(params);
}
/**
* 画面サイズとプレビューサイズ、それらのアスペクト比を保存する
*
* @param context
* @param camera
* @return scaledPreviewSize
*/
public static Size getScaledPreviewSize(Context context, Camera camera){
Size previewSize = camera.getParameters().getPreviewSize();
boolean isPortrait = DisplayUtils.isPortrait(context);
final int displayWidth = DisplayUtils.getWidth(context);
final int displayHeight = DisplayUtils.getHeight(context);
if(DEBUG){
LogUtils.v("displayWidth: " + displayWidth);
}
if(DEBUG){
LogUtils.v("displayHeight: " + displayHeight);
}
PreferenceUtils.putInt(context, context.getString(R.string.keyDisplayWidth), displayWidth);
PreferenceUtils.putInt(context, context.getString(R.string.keyDisplayHeight), displayHeight);
final int previewWidth = isPortrait ? previewSize.height : previewSize.width;
final int previewHeight = isPortrait ? previewSize.width : previewSize.height;
if(DEBUG){
LogUtils.v("previewWidth: " + previewWidth);
}
if(DEBUG){
LogUtils.v("previewHeight: " + previewHeight);
}
PreferenceUtils.putInt(context, context.getString(R.string.keyPreviewWidth), previewWidth);
PreferenceUtils.putInt(context, context.getString(R.string.keyPreviewHeight), previewHeight);
double displayRatio = displayHeight > displayWidth ? (double)displayHeight / displayWidth : (double)displayWidth / displayHeight;
double previewRatio = previewHeight > previewWidth ? (double)previewHeight / previewWidth : (double)previewWidth / previewHeight;
if(DEBUG){
LogUtils.v("displayRatio: " + displayRatio);
}
if(DEBUG){
LogUtils.v("previewRatio: " + previewRatio);
}
PreferenceUtils.putFloat(context, context.getString(R.string.keyDisplayRatio), (float)displayRatio);
PreferenceUtils.putFloat(context, context.getString(R.string.keyPreviewRatio), (float)previewRatio);
int scaledChildWidth = displayWidth;
int scaledChildHeight = displayHeight;
if(displayRatio <= previewRatio){
if(isPortrait){
if(DEBUG){
LogUtils.v("Portrait");
}
scaledChildWidth = (displayHeight * previewWidth) / previewHeight;
scaledChildHeight = (scaledChildWidth * previewHeight) / previewWidth;
}else{
if(DEBUG){
LogUtils.v("Landscape");
}
scaledChildHeight = (displayWidth * previewHeight) / previewWidth;
scaledChildWidth = (scaledChildHeight * previewWidth) / previewHeight;
}
}else{
if(isPortrait){
if(DEBUG){
LogUtils.v("Portrait");
}
scaledChildHeight = (displayWidth * previewHeight) / previewWidth;
scaledChildWidth = (scaledChildHeight * previewWidth) / previewHeight;
}else{
if(DEBUG){
LogUtils.v("Landscape");
}
scaledChildWidth = (displayHeight * previewWidth) / previewHeight;
scaledChildHeight = (scaledChildWidth * previewHeight) / previewWidth;
}
}
if(DEBUG){
LogUtils.v("scaledChildWidth: " + scaledChildWidth);
}
if(DEBUG){
LogUtils.v("scaledChildHeight: " + scaledChildHeight);
}
PreferenceUtils.putInt(context, context.getString(R.string.keyScaledChildWidth), scaledChildWidth);
PreferenceUtils.putInt(context, context.getString(R.string.keyScaledChildHeight), scaledChildHeight);
int scaledWidth = scaledChildWidth;
int scaledHeight = scaledChildHeight;
if(displayRatio <= previewRatio){
if(isPortrait){
if(DEBUG){
LogUtils.v("Portrait");
}
scaledHeight = (displayWidth * previewHeight) / previewWidth;
scaledWidth = (scaledHeight * previewWidth) / previewHeight;
}else{
if(DEBUG){
LogUtils.v("Landscape");
}
scaledWidth = (displayHeight * previewWidth) / previewHeight;
scaledHeight = (scaledWidth * previewHeight) / previewWidth;
}
}else{
if(isPortrait){
if(DEBUG){
LogUtils.v("Portrait");
// scaledWidth = (displayHeight * previewWidth) / previewHeight;
// scaledHeight = (scaledWidth * previewHeight) / previewWidth;
}
}else{
if(DEBUG){
LogUtils.v("Landscape");
// scaledHeight = (displayWidth * previewHeight) / previewWidth;
// scaledWidth = (scaledHeight * previewWidth) / previewHeight;
}
}
}
if(DEBUG){
LogUtils.v("scaledWidth: " + scaledWidth);
}
if(DEBUG){
LogUtils.v("scaledHeight: " + scaledHeight);
}
PreferenceUtils.putInt(context, context.getString(R.string.keyScaledWidth), scaledWidth);
PreferenceUtils.putInt(context, context.getString(R.string.keyScaledHeight), scaledHeight);
return camera.new Size(scaledWidth, scaledHeight);
}
/**
* カメラパラメータをJSONに変換する
*
* @param params
* @return
*/
public static String toJson(Camera.Parameters params){
String[] data = params.flatten().split(";");
JSONObject json = new JSONObject();
for(String element : data){
String[] splits = element.split("=");
String key = splits[0];
if(DEBUG){
LogUtils.v(key);
}
String value = splits.length > 1 ? splits[1] : "";
if(DEBUG){
LogUtils.v(value);
}
try{
json.put(key, value);
}catch(JSONException e){
e.printStackTrace();
}
}
return json.toString();
}
/**
* プロファイルをJSONに変換する
*
* @param params
* @return
*/
public static String toJson(CamcorderProfile camcorderProfile){
JSONObject json = new JSONObject();
try{
json.put("audioBitRate: ", camcorderProfile.audioBitRate);
json.put("audioChannels: ", camcorderProfile.audioChannels);
json.put("audioCodec: ", camcorderProfile.audioCodec);
json.put("audioSampleRate: ", camcorderProfile.audioSampleRate);
json.put("duration: ", camcorderProfile.duration);
json.put("fileFormat: ", camcorderProfile.fileFormat);
json.put("quality: ", camcorderProfile.quality);
json.put("videoBitRate: ", camcorderProfile.videoBitRate);
json.put("videoCodec: ", camcorderProfile.videoCodec);
json.put("videoFrameHeight: ", camcorderProfile.videoFrameHeight);
json.put("videoFrameRate: ", camcorderProfile.videoFrameRate);
json.put("videoFrameWidth: ", camcorderProfile.videoFrameWidth);
}catch(JSONException e){
e.printStackTrace();
}
if(DEBUG){
LogUtils.v("camcorderProfile.audioBitRate: " + camcorderProfile.audioBitRate);
LogUtils.v("camcorderProfile.audioChannels: " + camcorderProfile.audioChannels);
LogUtils.v("camcorderProfile.audioCodec: " + camcorderProfile.audioCodec);
LogUtils.v("camcorderProfile.audioSampleRate: " + camcorderProfile.audioSampleRate);
LogUtils.v("camcorderProfile.duration: " + camcorderProfile.duration);
LogUtils.v("camcorderProfile.fileFormat: " + camcorderProfile.fileFormat);
LogUtils.v("camcorderProfile.quality: " + camcorderProfile.quality);
LogUtils.v("camcorderProfile.videoBitRate: " + camcorderProfile.videoBitRate);
LogUtils.v("camcorderProfile.videoCodec: " + camcorderProfile.videoCodec);
LogUtils.v("camcorderProfile.videoFrameHeight: " + camcorderProfile.videoFrameHeight);
LogUtils.v("camcorderProfile.videoFrameRate: " + camcorderProfile.videoFrameRate);
LogUtils.v("camcorderProfile.videoFrameWidth: " + camcorderProfile.videoFrameWidth);
}
return json.toString();
}
/**
* シャッター音を消せるか
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean canDisableShutterSound(CameraInfo info){
boolean canDisableShutterSound = false;
if(AndroidUtils.isMoreThanBuildVersion(Build.VERSION_CODES.JELLY_BEAN_MR1)){
canDisableShutterSound = info.canDisableShutterSound;
}
return canDisableShutterSound;
}
/**
* 顔検出が使えるか
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static boolean isFaceDetectionSupported(Context context, Camera camera){
boolean isFaceDetectionSupported = false;
int maxNumDetectedFaces = 0;
if(AndroidUtils.isMoreThanBuildVersion(Build.VERSION_CODES.ICE_CREAM_SANDWICH)){
maxNumDetectedFaces = camera.getParameters().getMaxNumDetectedFaces();
isFaceDetectionSupported = maxNumDetectedFaces > 0;
}
PreferenceUtils.putBoolean(context, context.getString(R.string.keySupportFaceDetection), isFaceDetectionSupported);
PreferenceUtils.putInt(context, context.getString(R.string.keyMaxNumDetectedFaces), maxNumDetectedFaces);
return isFaceDetectionSupported;
}
}