欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-08-17 08:38:19
引言: 在做用户的头像时,忽然想到前段时间(可能是很久以前了),支付宝传出偷偷拍摄用户的生活照,真实头像,被喷的很厉害。然而作为android开发者的我第一反应竟然是握...

引言:
在做用户的头像时,忽然想到前段时间(可能是很久以前了),支付宝传出偷偷拍摄用户的生活照,真实头像,被喷的很厉害。然而作为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中的帧图像质量又是非常感人的。所以不得不说这真是没什么办法。

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