一聚教程网:一个值得你收藏的教程网站

热门教程

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

时间:2022-06-25 23:24:10 编辑:袖梨 来源:一聚教程网

在做用户的头像时,忽然想到前段时间(可能是很久以前了),支付宝传出偷偷拍摄用户的生活照,真实头像,被喷的很厉害。然而作为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 {

  staticfinalString 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.

  booleanpreviewing;

 

  intmCurrentCamIndex =0;

  privateAudioManager manager;

  privateintvolumn;

  privatebooleancanTake=false;

  privateImageView imageView;

 

  @Override

  protectedvoidonCreate(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(newButton.OnClickListener() {

      publicvoidonClick(View arg0) {

        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, Camera camera,String name){

    Camera.Size size = camera.getParameters().getPreviewSize();

    YuvImage image =newYuvImage(data, ImageFormat.NV21, size.width, size.height,null);

    if(image!=null){

      ByteArrayOutputStream stream =newByteArrayOutputStream();

      image.compressToJpeg(newRect(0,0, size.width, size.height),80, stream);

 

      Bitmap bmp = BitmapFactory.decodeByteArray(stream.toByteArray(),0, stream.size());

 

      //**********************

      //因为图片会放生旋转,因此要对图片进行旋转到和手机在一个方向上

      rotateMyBitmap(bmp,name);

      //**********************************

 

 

    }

  }

 

  /** 保存方法 */

  publicvoidsaveBitmap(Bitmap bm,String name) {

    Log.e(TAG,"保存图片");

    File f =newFile("/sdcard/namecard/", name);

    if(f.exists()) {

      f.delete();

    }

    try{

      FileOutputStream out =newFileOutputStream(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

   */

  privatebooleansaveBitmapTofile(byte[] bmp) {

    String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)

        .toString()

        + File.separator

        +"PicTest_"+ System.currentTimeMillis() +".jpg";

    File file =newFile(fileName);

    if(!file.getParentFile().exists()) {

      file.getParentFile().mkdir();

    }

 

    try{

      BufferedOutputStream bos =newBufferedOutputStream(

          newFileOutputStream(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();

    }

    returntrue;

  }

 

  publicvoidsaveMyBitmap(Bitmap mBitmap,String bitName) {

    String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)

        .toString()

        + File.separator

        +"PicTest_"+ System.currentTimeMillis() +".jpg";

    File file =newFile(fileName);

    if(!file.getParentFile().exists()) {

      file.getParentFile().mkdir();

    }

    FileOutputStream fOut =null;

    try{

      fOut =newFileOutputStream(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();

    }

 

  }

 

  publicvoidrotateMyBitmap(Bitmap bmp,String name){

    //*****旋转一下

    Matrix matrix =newMatrix();

    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);

 

  };

  /**

   * 压缩图片

   *

   *@paramimage

   *@return

   */

  publicstaticBitmap compressImage(Bitmap image) {

    ByteArrayOutputStream baos =newByteArrayOutputStream();

    // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中

    image.compress(Bitmap.CompressFormat.JPEG,100, baos);

    // 把压缩后的数据baos存放到ByteArrayInputStream中

    ByteArrayInputStream isBm =newByteArrayInputStream(baos.toByteArray());

    // 把ByteArrayInputStream数据生成图片

    Bitmap bitmap = BitmapFactory.decodeStream(isBm,null,null);

    returnbitmap;

  }

 

 

 

  Camera.ShutterCallback shutterCallback =newCamera.ShutterCallback() {

    @Override

    publicvoidonShutter() {

    }

  };

 

  Camera.PictureCallback rawPictureCallback =newCamera.PictureCallback() {

    @Override

    publicvoidonPictureTaken(byte[] arg0, Camera arg1) {

 

    }

  };

 

  Camera.PictureCallback jpegPictureCallback =newCamera.PictureCallback() {

    @Override

    publicvoidonPictureTaken(byte[] arg0, Camera arg1) {

 

      String fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)

          .toString()

          + File.separator

          +"PicTest_"+ System.currentTimeMillis() +".jpg";

      File file =newFile(fileName);

      if(!file.getParentFile().exists()) {

        file.getParentFile().mkdir();

      }

 

      try{

        BufferedOutputStream bos =newBufferedOutputStream(

            newFileOutputStream(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();

      }

    };

  };

 

  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(String path) {

 

    MediaScannerConnection.scanFile(MainActivity.this,

        newString[] { path },null,

        newMediaScannerConnection.OnScanCompletedListener() {

 

          publicvoidonScanCompleted(String path, Uri uri) {

            Log.i("TAG","Finished scanning "+ path);

          }

        });

  }

 

  publicvoidcameraRefresh(String picPath) {

    Toast.makeText(this,picPath,Toast.LENGTH_SHORT).show();

  }

 

  privatefinalclassSurfaceViewCallbackimplementsandroid.view.SurfaceHolder.Callback {

    publicvoidsurfaceChanged(SurfaceHolder arg0,intarg1,intarg2,intarg3)

    {

      if(previewing) {

        mCamera.stopPreview();

        previewing =false;

      }

 

      try{

        mCamera.setPreviewDisplay(arg0);

        mCamera.startPreview();

        previewing =true;

        setCameraDisplayOrientation(MainActivity.this, mCurrentCamIndex, mCamera);

      }catch(Exception e) {}

    }

    publicvoidsurfaceCreated(SurfaceHolder holder) {

//       mCamera = Camera.open();

      //change to front camera

      mCamera = openFrontFacingCameraGingerbread();

      // get Camera parameters

      Camera.Parameters params = mCamera.getParameters();

 

      ListfocusModes = params.getSupportedFocusModes();

      if(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {

        // Autofocus mode is supported

      }

      mCamera.setPreviewCallback(newCamera.PreviewCallback() {

        @Override

        publicvoidonPreviewFrame(byte[] bytes, Camera camera) {

          Log.e("stuart","onPreviewFrame "+canTake);

          if(canTake) {

            getSurfacePic(bytes, camera,"hahahaah");

            canTake=false;

          }

        }

      });

    }

 

    publicvoidsurfaceDestroyed(SurfaceHolder holder) {

      mCamera.stopPreview();

      mCamera.release();

      mCamera =null;

      previewing =false;

    }

 

 

  }

 

  privateCamera openFrontFacingCameraGingerbread() {

    intcameraCount =0;

    Camera cam =null;

    Camera.CameraInfo cameraInfo =newCamera.CameraInfo();

    cameraCount = Camera.getNumberOfCameras();

 

    for(intcamIdx =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());

        }

      }

    }

 

    returncam;

  }

 

  privatestaticvoidsetCameraDisplayOrientation(Activity activity,intcameraId, Camera camera)

  {

    Camera.CameraInfo info =newCamera.CameraInfo();

    Camera.getCameraInfo(cameraId, info);

    introtation = 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).

    intdegrees =0;

    switch(rotation)

    {

      caseSurface.ROTATION_0: degrees =0;break;

      caseSurface.ROTATION_90: degrees =90;break;

      caseSurface.ROTATION_180: degrees =180;break;

      caseSurface.ROTATION_270: degrees =270;break;

    }

    intresult;

    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中的帧图像质量又是非常感人的。所以不得不说这真是没什么办法。

热门栏目