什么是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,然后保存到了本地,接着就把相机关了。神不知鬼不觉地把自拍拿到了。当然其中有一点小问题,比如图片编码,图片旋转,本地存储,获取帧图像都是各种各样的问题。但这些都是可以解决的。思路依旧是我上面提到的思路,各种表现方式可以由大家自己搞。
publicclassMainActivityextendsAppCompatActivity{ staticfinalStringTAG="CAMERAACTIVITY"; //Cameraobject CameramCamera; //Previewsurface SurfaceViewsurfaceView; //Previewsurfacehandleforcallback SurfaceHoldersurfaceHolder; //Camerabutton ButtonbtnCapture; //Noteifpreviewwindowsison. booleanpreviewing; intmCurrentCamIndex=0; privateAudioManagermanager; privateintvolumn; privatebooleancanTake=false; privateImageViewimageView; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnCapture=(Button)findViewById(R.id.btn_capture); imageView=(ImageView)findViewById(R.id.iv); btnCapture.setOnClickListener(newButton.OnClickListener(){ publicvoidonClick(Viewarg0){ canTake=true; } }); surfaceView=(SurfaceView)findViewById(R.id.surfaceView1); surfaceHolder=surfaceView.getHolder(); surfaceHolder.addCallback(newSurfaceViewCallback()); //surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } publicvoidgetSurfacePic(byte[]data,Cameracamera,Stringname){ Camera.Sizesize=camera.getParameters().getPreviewSize(); YuvImageimage=newYuvImage(data,ImageFormat.NV21,size.width,size.height,null); if(image!=null){ ByteArrayOutputStreamstream=newByteArrayOutputStream(); image.compressToJpeg(newRect(0,0,size.width,size.height),80,stream); Bitmapbmp=BitmapFactory.decodeByteArray(stream.toByteArray(),0,stream.size()); //********************** //因为图片会放生旋转,因此要对图片进行旋转到和手机在一个方向上 rotateMyBitmap(bmp,name); //********************************** } } /**保存方法*/ publicvoidsaveBitmap(Bitmapbm,Stringname){ Log.e(TAG,"保存图片"); Filef=newFile("/sdcard/namecard/",name); if(f.exists()){ f.delete(); } try{ FileOutputStreamout=newFileOutputStream(f); bm.compress(Bitmap.CompressFormat.PNG,90,out); out.flush(); out.close(); Log.e(TAG,"已经保存"); }catch(FileNotFoundExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); }catch(IOExceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } /** *保存图片到指定文件夹 * *@parambmp *@return */ privatebooleansaveBitmapTofile(byte[]bmp){ StringfileName=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) .toString() +File.separator +"PicTest_"+System.currentTimeMillis()+".jpg"; Filefile=newFile(fileName); if(!file.getParentFile().exists()){ file.getParentFile().mkdir(); } try{ BufferedOutputStreambos=newBufferedOutputStream( newFileOutputStream(file)); bos.write(bmp); bos.flush(); bos.close(); scanFileToPhotoAlbum(file.getAbsolutePath()); Toast.makeText(MainActivity.this,"[Test]Phototakeandstorein"+file.toString(),Toast.LENGTH_LONG).show(); }catch(Exceptione){ Toast.makeText(MainActivity.this,"PictureFailed"+e.toString(), Toast.LENGTH_LONG).show(); } returntrue; } publicvoidsaveMyBitmap(BitmapmBitmap,StringbitName){ StringfileName=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) .toString() +File.separator +"PicTest_"+System.currentTimeMillis()+".jpg"; Filefile=newFile(fileName); if(!file.getParentFile().exists()){ file.getParentFile().mkdir(); } FileOutputStreamfOut=null; try{ fOut=newFileOutputStream(file); }catch(FileNotFoundExceptione){ e.printStackTrace(); } try{ if(null!=fOut){ mBitmap.compress(Bitmap.CompressFormat.JPEG,100,fOut); fOut.flush(); fOut.close(); } }catch(Exceptione){ e.printStackTrace(); } } publicvoidrotateMyBitmap(Bitmapbmp,Stringname){ //*****旋转一下 Matrixmatrix=newMatrix(); matrix.postRotate(270); Bitmapbitmap=Bitmap.createBitmap(bmp.getWidth(),bmp.getHeight(),Bitmap.Config.RGB_565); Bitmapnbmp2=Bitmap.createBitmap(bmp,0,0,bmp.getWidth(),bmp.getHeight(),matrix,true); saveMyBitmap(compressImage(nbmp2),"cool"); //*******显示一下 imageView.setImageBitmap(nbmp2); }; /** *压缩图片 * *@paramimage *@return */ publicstaticBitmapcompressImage(Bitmapimage){ ByteArrayOutputStreambaos=newByteArrayOutputStream(); //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 image.compress(Bitmap.CompressFormat.JPEG,100,baos); //把压缩后的数据baos存放到ByteArrayInputStream中 ByteArrayInputStreamisBm=newByteArrayInputStream(baos.toByteArray()); //把ByteArrayInputStream数据生成图片 Bitmapbitmap=BitmapFactory.decodeStream(isBm,null,null); returnbitmap; } Camera.ShutterCallbackshutterCallback=newCamera.ShutterCallback(){ @Override publicvoidonShutter(){ } }; Camera.PictureCallbackrawPictureCallback=newCamera.PictureCallback(){ @Override publicvoidonPictureTaken(byte[]arg0,Cameraarg1){ } }; Camera.PictureCallbackjpegPictureCallback=newCamera.PictureCallback(){ @Override publicvoidonPictureTaken(byte[]arg0,Cameraarg1){ StringfileName=Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) .toString() +File.separator +"PicTest_"+System.currentTimeMillis()+".jpg"; Filefile=newFile(fileName); if(!file.getParentFile().exists()){ file.getParentFile().mkdir(); } try{ BufferedOutputStreambos=newBufferedOutputStream( newFileOutputStream(file)); bos.write(arg0); bos.flush(); bos.close(); scanFileToPhotoAlbum(file.getAbsolutePath()); Toast.makeText(MainActivity.this,"[Test]Phototakeandstorein"+file.toString(),Toast.LENGTH_LONG).show(); }catch(Exceptione){ Toast.makeText(MainActivity.this,"PictureFailed"+e.toString(), Toast.LENGTH_LONG).show(); } }; }; publicvoidsetVolumnSilence(){ 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); } } publicvoidscanFileToPhotoAlbum(Stringpath){ MediaScannerConnection.scanFile(MainActivity.this, newString[]{path},null, newMediaScannerConnection.OnScanCompletedListener(){ publicvoidonScanCompleted(Stringpath,Uriuri){ Log.i("TAG","Finishedscanning"+path); } }); } publicvoidcameraRefresh(StringpicPath){ Toast.makeText(this,picPath,Toast.LENGTH_SHORT).show(); } privatefinalclassSurfaceViewCallbackimplementsandroid.view.SurfaceHolder.Callback{ publicvoidsurfaceChanged(SurfaceHolderarg0,intarg1,intarg2,intarg3) { if(previewing){ mCamera.stopPreview(); previewing=false; } try{ mCamera.setPreviewDisplay(arg0); mCamera.startPreview(); previewing=true; setCameraDisplayOrientation(MainActivity.this,mCurrentCamIndex,mCamera); }catch(Exceptione){} } publicvoidsurfaceCreated(SurfaceHolderholder){ //mCamera=Camera.open(); //changetofrontcamera mCamera=openFrontFacingCameraGingerbread(); //getCameraparameters Camera.Parametersparams=mCamera.getParameters(); ListfocusModes=params.getSupportedFocusModes(); if(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)){ //Autofocusmodeissupported } mCamera.setPreviewCallback(newCamera.PreviewCallback(){ @Override publicvoidonPreviewFrame(byte[]bytes,Cameracamera){ Log.e("stuart","onPreviewFrame"+canTake); if(canTake){ getSurfacePic(bytes,camera,"hahahaah"); canTake=false; } } }); } publicvoidsurfaceDestroyed(SurfaceHolderholder){ mCamera.stopPreview(); mCamera.release(); mCamera=null; previewing=false; } } privateCameraopenFrontFacingCameraGingerbread(){ intcameraCount=0; Cameracam=null; Camera.CameraInfocameraInfo=newCamera.CameraInfo(); cameraCount=Camera.getNumberOfCameras(); for(intcamIdx=0;camIdx 基本上呢,这一个代码就能实现简单的静默拍照了。
依旧存在的问题:
图片质量实在有点低。
目前来看这也是没有办法的,因为我只能取到surfaceView的帧图像,而显示在preview中的帧图像质量又是非常感人的。所以不得不说这真是没什么办法。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。