什么是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中的帧图像质量又是非常感人的。所以不得不说这真是没什么办法。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。