什么是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中的帧图像质量又是非常感人的。所以不得不说这真是没什么办法。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: 详细讲解网站关键词的定义