什么是Android静默拍摄 Android静默拍摄app制作方法

引言:
在做用户的头像时,忽然想到前段时间(可能是很久以前了),支付宝传出偷偷拍摄用户的生活照,真实头像,被喷的很厉害。然而作为Android开发者的我第一反应竟然是握草,他是怎么实现的。在我印象中,iOS对权限的控制是很严格的,偷偷调起摄像头这种行为应该是很困难的。然而Android4.2之前可以说开发者几乎拥有了系统权限,能力之强简直可怕。而现在Android已经到了7.0,虽然大多说用户还是在4.4到6.0的。我想我也来做一个静默拍摄的app。

正文:

所谓静默拍摄就是在用户毫无感知的情况下拍摄。

一般的拍照都会有预览区域,拍照声。去掉这些东西才算是真正意义上的静默拍摄。
首先,做了一个非常正常的自拍软件,就一个按钮。拍完之后存到文件夹的一个位置。然后我试了一下,完全ok并没有什么难度。然后就是清空surfaceView了。我首先想到的就是setVisiblity为gone,然后就报错了。很尴尬。下一个方案就是用高度和宽度都是0的方法,然而并没有什么卵用,更加尴尬。

然后想想没有有什么好办法了那就把这个surfaceView盖住好了,非常完美,随便搞一搞就盖住了,然后照片照样拍。合理。

但是“咔嚓”一声的拍照声实在令人尴尬,然后我就想到了静音,在页面打开的时候就设置静音。看上去这是一个非常稳健的方法,然后就发生了更加尴尬的事情。设置静音的时候,手机振动了一下,震一下也就算了,关键是还没有把拍照的声音去除。然后我就去查了查了相机音量应该是哪个。之后悲催的事情就发生了:

Google的Android开发者为了Android用户的用户体验,也为了避免开发者开发出静默拍摄的app从而侵犯了隐私,他们就把快门声音的播放函数写在了拍照的方法里面,还是写在framework层的。瞬间我就很难过了。作为一个平凡的第三方开发者,我并没有那么多权限去改变framework层的方法。

然后智慧的我决定曲线救国。因为在预览的时候,并没有进行拍照,但实际上我们已经拿到了相机带来的图片流。这很关键。然后我就把这个图片流变成了bitmap,然后保存到了本地,接着就把相机关了。神不知鬼不觉地把自拍拿到了。当然其中有一点小问题,比如图片编码,图片旋转,本地存储,获取帧图像都是各种各样的问题。但这些都是可以解决的。思路依旧是我上面提到的思路,各种表现方式可以由大家自己搞。

public class MainActivity extends AppCompatActivity { static final String TAG = "CAMERA ACTIVITY"; //Camera object Camera mCamera; //Preview surface SurfaceView surfaceView; //Preview surface handle for callback SurfaceHolder surfaceHolder; //Camera button Button btnCapture; //Note if preview windows is on. boolean previewing; int mCurrentCamIndex = 0; private AudioManager manager; private int volumn; private boolean canTake=false; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnCapture = (Button) findViewById(R.id.btn_capture); imageView =(ImageView)findViewById(R.id.iv); btnCapture.setOnClickListener(new Button.OnClickListener() { public void onClick(View arg0) { canTake=true; } }); surfaceView = (SurfaceView) findViewById(R.id.surfaceView1); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(new SurfaceViewCallback()); //surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void getSurfacePic(byte[] data, Camera camera,String name){ Camera.Size size = camera.getParameters().getPreviewSize(); YuvImage image = new YuvImage(data, ImageFormat.NV21, size.width, size.height, null); if(image!=null){ ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compressToJpeg(new Rect(0, 0, size.width, size.height), 80, stream); Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size()); //********************** //因为图片会放生旋转,因此要对图片进行旋转到和手机在一个方向上 rotateMyBitmap(bmp,name); //********************************** } } /** 保存方法 */ public void saveBitmap(Bitmap bm,String name) { Log.e(TAG, "保存图片"); File f = new File("/sdcard/namecard/", name); if (f.exists()) { f.delete(); } try { FileOutputStream out = new FileOutputStream(f); bm.compress(Bitmap.CompressFormat.PNG, 90, out); out.flush(); out.close(); Log.e(TAG, "已经保存"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 保存图片到指定文件夹 * * @param bmp * @return */ private boolean saveBitmapTofile(byte[] bmp) { String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) .toString() + File.separator + "PicTest_" + System.currentTimeMillis() + ".jpg"; File file = new File(fileName); if (!file.getParentFile().exists()) { file.getParentFile().mkdir(); } try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(file)); bos.write(bmp); bos.flush(); bos.close(); scanFileToPhotoAlbum(file.getAbsolutePath()); Toast.makeText(MainActivity.this, "[Test] Photo take and store in" + file.toString(),Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(MainActivity.this, "Picture Failed" + e.toString(), Toast.LENGTH_LONG).show(); } return true; } public void saveMyBitmap(Bitmap mBitmap,String bitName) { String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) .toString() + File.separator + "PicTest_" + System.currentTimeMillis() + ".jpg"; File file = new File(fileName); if (!file.getParentFile().exists()) { file.getParentFile().mkdir(); } FileOutputStream fOut = null; try { fOut = new FileOutputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { if (null != fOut) { mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut); fOut.flush(); fOut.close(); } } catch (Exception e) { e.printStackTrace(); } } public void rotateMyBitmap(Bitmap bmp,String name){ //*****旋转一下 Matrix matrix = new Matrix(); matrix.postRotate(270); Bitmap bitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.RGB_565); Bitmap nbmp2 = Bitmap.createBitmap(bmp, 0,0, bmp.getWidth(), bmp.getHeight(), matrix, true); saveMyBitmap(compressImage(nbmp2),"cool"); //*******显示一下 imageView.setImageBitmap(nbmp2); }; /** * 压缩图片 * * @param image * @return */ public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 image.compress(Bitmap.CompressFormat.JPEG, 100, baos); // 把压缩后的数据baos存放到ByteArrayInputStream中 ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); // 把ByteArrayInputStream数据生成图片 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); return bitmap; } Camera.ShutterCallback shutterCallback = new Camera.ShutterCallback() { @Override public void onShutter() { } }; Camera.PictureCallback rawPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] arg0, Camera arg1) { } }; Camera.PictureCallback jpegPictureCallback = new Camera.PictureCallback() { @Override public void onPictureTaken(byte[] arg0, Camera arg1) { String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) .toString() + File.separator + "PicTest_" + System.currentTimeMillis() + ".jpg"; File file = new File(fileName); if (!file.getParentFile().exists()) { file.getParentFile().mkdir(); } try { BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(file)); bos.write(arg0); bos.flush(); bos.close(); scanFileToPhotoAlbum(file.getAbsolutePath()); Toast.makeText(MainActivity.this, "[Test] Photo take and store in" + file.toString(),Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(MainActivity.this, "Picture Failed" + e.toString(), Toast.LENGTH_LONG).show(); } }; }; public void setVolumnSilence(){ manager = (AudioManager) this .getSystemService(Context.AUDIO_SERVICE); manager.setStreamMute(AudioManager.STREAM_SYSTEM, false); volumn = manager.getStreamVolume(AudioManager.STREAM_SYSTEM); if (volumn != 0) { // 如果需要静音并且当前未静音(muteMode的设置可以放在Preference中) manager.setStreamVolume(AudioManager.STREAM_SYSTEM, 0, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE); } } public void scanFileToPhotoAlbum(String path) { MediaScannerConnection.scanFile(MainActivity.this, new String[] { path }, null, new MediaScannerConnection.OnScanCompletedListener() { public void onScanCompleted(String path, Uri uri) { Log.i("TAG", "Finished scanning " + path); } }); } public void cameraRefresh(String picPath) { Toast.makeText(this,picPath,Toast.LENGTH_SHORT).show(); } private final class SurfaceViewCallback implements android.view.SurfaceHolder.Callback { public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { if (previewing) { mCamera.stopPreview(); previewing = false; } try { mCamera.setPreviewDisplay(arg0); mCamera.startPreview(); previewing = true; setCameraDisplayOrientation(MainActivity.this, mCurrentCamIndex, mCamera); } catch (Exception e) {} } public void surfaceCreated(SurfaceHolder holder) { // mCamera = Camera.open(); //change to front camera mCamera = openFrontFacingCameraGingerbread(); // get Camera parameters Camera.Parameters params = mCamera.getParameters(); List<String> focusModes = params.getSupportedFocusModes(); if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { // Autofocus mode is supported } mCamera.setPreviewCallback(new Camera.PreviewCallback() { @Override public void onPreviewFrame(byte[] bytes, Camera camera) { Log.e("stuart","onPreviewFrame "+canTake); if(canTake) { getSurfacePic(bytes, camera, "hahahaah"); canTake=false; } } }); } public void surfaceDestroyed(SurfaceHolder holder) { mCamera.stopPreview(); mCamera.release(); mCamera = null; previewing = false; } } private Camera openFrontFacingCameraGingerbread() { int cameraCount = 0; Camera cam = null; Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); cameraCount = Camera.getNumberOfCameras(); for (int camIdx = 0; camIdx < cameraCount; camIdx++) { Camera.getCameraInfo(camIdx, cameraInfo); if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { try { cam = Camera.open(camIdx); mCurrentCamIndex = camIdx; } catch (RuntimeException e) { Log.e(TAG, "Camera failed to open: " + e.getLocalizedMessage()); } } } return cam; } private static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) { Camera.CameraInfo info = new Camera.CameraInfo(); Camera.getCameraInfo(cameraId, info); int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); //degrees the angle that the picture will be rotated clockwise. Valid values are 0, 90, 180, and 270. //The starting position is 0 (landscape). int degrees = 0; switch (rotation) { case Surface.ROTATION_0: degrees = 0; break; case Surface.ROTATION_90: degrees = 90; break; case Surface.ROTATION_180: degrees = 180; break; case Surface.ROTATION_270: degrees = 270; break; } int result; if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { result = (info.orientation + degrees) % 360; result = (360 - result) % 360; // compensate the mirror } else { // back-facing result = (info.orientation - degrees + 360) % 360; } camera.setDisplayOrientation(result); } }

基本上呢,这一个代码就能实现简单的静默拍照了。

依旧存在的问题:

图片质量实在有点低。

目前来看这也是没有办法的,因为我只能取到surfaceView的帧图像,而显示在preview中的帧图像质量又是非常感人的。所以不得不说这真是没什么办法。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-09-03 12:11:15

什么是Android静默拍摄 Android静默拍摄app制作方法的相关文章

Android实现使用微信登录第三方APP的方法_Android

本文实例讲述了Android实现使用微信登录第三方APP的方法.分享给大家供大家参考,具体如下: 使用微信登录APP,免去注册过程,现在已经有很多的类似应用了.集成该功能过程不复杂,但还是有一些地方需要注意的. 开始之前,需要做下面的准备工作. 1.到微信开放平台注册你的APP,并申请开通微信登录的权限.参考这里: https://open.weixin.qq.com// 2.下载Android SDK和签名查看工具,请参考: https://open.weixin.qq.com/cgi-bin

Android实现开机自动启动Service或app的方法_Android

本文实例讲述了Android实现开机自动启动Service或app的方法.分享给大家供大家参考,具体如下: 第一步:首先创建一个广播接收者,重构其抽象方法 onReceive(Context context, Intent intent),在其中启动你想要启动的Service或app. import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; i

Android实现开机自动启动Service或app的方法

本文实例讲述了Android实现开机自动启动Service或app的方法.分享给大家供大家参考,具体如下: 第一步:首先创建一个广播接收者,重构其抽象方法 onReceive(Context context, Intent intent),在其中启动你想要启动的Service或app. import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; i

Android应用中仿今日头条App制作ViewPager指示器_Android

一.概述顶部ViewPager指示器的字体变色,该效果图是这样的: 大概是今天头条的app,神奇的地方就在于,切换ViewPager页面的时候,顶部指示器改成了字体颜色的变化,个人觉得还是不错的. 那么核心的地方就是做一个支持字体这样逐渐染色就可以了,我大概想了32s,扫描了一些可能实现的方案,最终定位了一个靠谱的,下面我就带大家开始实现的征程. 实现之前贴一下我们的效果图:1.简单使用 效果如上图了,关于颜失色的改变我添加了两个方向,一个是左方向,一个是有方向. 单纯的使用,可能觉得没什么意思

Android学习教程之圆形Menu菜单制作方法(1)_Android

本文实例为大家分享了Android圆形菜单的使用方法,供大家参考,具体内容如下 MainActivity.java代码: package siso.handlerdemo; import android.app.NotificationManager; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.vi

Android学习教程之圆形Menu菜单制作方法(1)

本文实例为大家分享了Android圆形菜单的使用方法,供大家参考,具体内容如下 MainActivity.java代码: package siso.handlerdemo; import android.app.NotificationManager; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.vi

android 开发调用摄像头如何 拍摄格式为mp4的视频文件

问题描述 android 开发调用摄像头如何 拍摄格式为mp4的视频文件 现在拍摄的是3gp的 需要改格式 但是弄了好久 也没找到什么有用的资料 谁有好的完整性的demo或资源能够提供的啊 谢谢了 解决方案 // 视频文件的存放地址 private String strVideoPath = Environment.getExternalStorageDirectory() .getAbsolutePath() + "rec_video.mp4"; 存的时候用.mp4,调用摄像头原生的

Android 调用系统相机拍摄获取照片的两种方法实现实例_Android

Android 调用系统相机拍摄获取照片的两种方法实现实例 在我们Android开发中经常需要做这个一个功能,调用系统相机拍照,然后获取拍摄的照片.下面是我总结的两种方法获取拍摄之后的照片,一种是通过Bundle来获取压缩过的照片,一种是通过SD卡获取的原图. 下面是演示代码: 布局文件: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http:

Android编程之软件的安装和卸载方法_Android

本文实例讲述了Android编程之软件的安装和卸载方法.分享给大家供大家参考,具体如下: 安装:从sdcard String fileName = Environment.getExternalStorageDirectory() + "/myApp.apk"; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + filePath),&