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

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

(0)

相关推荐

  • Android实现相机拍摄、选择、图片裁剪功能

    最近的一些学习心得: 功能实现:点击圆形头像之后可以实现相册上传或者开启相机,然后把得到的图片经过剪裁,把剪裁过的图片设置为头像的背景图 步骤:第一步:自定义一个类,继承ImageView,重写draw方法,实现外观为圆形 第二步:在xml文件中引用该控件 第三步:实现圆形头像的点击事件,点击后显示对话框界面,询问你是打开相册还是相机(自动省略显示对话框的代码) 第四步:根据用户选择情况,打开相册或者相机 第五步:将拍摄的图片或者相册选中的图片进行剪裁,将结果保存在指定内存区域 第六步:更新头像

  • Android开发仿扫一扫实现拍摄框内的照片功能

    就是仿照现在扫一扫的形式,周围是半透明的遮挡,然后中间是全透明的,拍摄后只截取框内的内容 查了很多博客,实现起来真的太复杂了,本人比较怕麻烦所以在很多地方偷懒了 先上效果图: 第一步:设置照相机预览以及拍照 这是所有步骤的前提,没有预览,用户怎么知道自己拍的什么呢.预览用的是SurfaceView 这篇博文写得已经十分详细了,打开照相机,然后拍照,而且十分简洁!不想别的博客一下就几百行代码不知所云.这篇代码可以复制下去当相机模版使用. 这里遇到一个问题,就是预览的效果是左转90度的,拍出来也是左

  • Android实现返回拍摄的图片功能实例

    本文实例讲述了Android实现返回拍摄的图片功能.分享给大家供大家参考.具体如下: 第一步: try { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(intent, 0); } catch (ActivityNotFoundException e) { // Do nothing for now } 第二步: @Override protected void onActi

  • Android 仿微信图像拍摄和选择界面功能(代码分享)

    插件运行后的画面如下: 下面这张图对图像进行筛选,根据照片产生的源头分(QQ和微信和相机) 点击某文件夹后,可以查看该文件夹下包含的所有的图片 图片选择界面 选中后就跳到已经选择界面的窗口,并且可以对该吃图片上传进行简要的描述 首先我想说明的是这个插件默认是不进行图片筛选的,打开app后会有几十个文件夹,但是个人认为开发中常用的图片基本都来自于QQ中拍摄的照片,微信中拍摄的照片,以及相机直接拍摄的照片,因此我对这个插件进行过滤以及文件夹名称的更改,具体做法,主要是对AlbumHelper类bui

  • Android 调用系统相机拍摄获取照片的两种方法实现实例

    Android 调用系统相机拍摄获取照片的两种方法实现实例 在我们Android开发中经常需要做这个一个功能,调用系统相机拍照,然后获取拍摄的照片.下面是我总结的两种方法获取拍摄之后的照片,一种是通过Bundle来获取压缩过的照片,一种是通过SD卡获取的原图. 下面是演示代码: 布局文件: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http:

  • Android仿微信拍摄短视频

    近期做项目需要添加上传短视频功能,功能设置为类似于微信,点击开始拍摄,设置最长拍摄时间,经过研究最终实现了这个功能,下面就和大家分享一下,希望对你有帮助. 1.视频录制自定义控件: /** * 视频播放控件 */ public class MovieRecorderView extends LinearLayout implements OnErrorListener { private SurfaceView mSurfaceView; private SurfaceHolder mSurfa

  • Android程序静默安装安装后重新启动APP的方法

     一:需求简介 之前boss提出一个需求,运行在广告机上的app,需要完成自动升级的功能,广告机是非触摸屏的,不能通过手动点击,所以app必须做到自动下载,自动安装升级,并且安装完成后,app还要继续运行,最好不借助其它app来实现以上功能.  二:实现思路 实现这个功能第一个想到的方法就是静默安装,由于广告机已经root,静默安装比较顺利,安装app的主要代码如下: /* @pararm apkPath 等待安装的app全路径,如:/sdcard/app/app.apk **/ private

  • Android实现使用微信登录第三方APP的方法

    本文实例讲述了Android实现使用微信登录第三方APP的方法.分享给大家供大家参考,具体如下: 使用微信登录APP,免去注册过程,现在已经有很多的类似应用了.集成该功能过程不复杂,但还是有一些地方需要注意的. 开始之前,需要做下面的准备工作. 1.到微信开放平台注册你的APP,并申请开通微信登录的权限.参考这里: https://open.weixin.qq.com// 2.下载Android SDK和签名查看工具,请参考: https://open.weixin.qq.com/cgi-bin

  • Android实现开机自动启动Service或app的方法

    本文实例讲述了Android实现开机自动启动Service或app的方法.分享给大家供大家参考,具体如下: 第一步:首先创建一个广播接收者,重构其抽象方法 onReceive(Context context, Intent intent),在其中启动你想要启动的Service或app. import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; i

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

    引言: 在做用户的头像时,忽然想到前段时间(可能是很久以前了),支付宝传出偷偷拍摄用户的生活照,真实头像,被喷的很厉害.然而作为Android开发者的我第一反应竟然是握草,他是怎么实现的.在我印象中,iOS对权限的控制是很严格的,偷偷调起摄像头这种行为应该是很困难的.然而Android4.2之前可以说开发者几乎拥有了系统权限,能力之强简直可怕.而现在Android已经到了7.0,虽然大多说用户还是在4.4到6.0的.我想我也来做一个静默拍摄的app. 正文: 所谓静默拍摄就是在用户毫无感知的情况

  • Android学习教程之圆形Menu菜单制作方法(1)

    本文实例为大家分享了Android圆形菜单的使用方法,供大家参考,具体内容如下 MainActivity.java代码: package siso.handlerdemo; import android.app.NotificationManager; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.vi

  • Android应用中仿今日头条App制作ViewPager指示器

    一.概述 顶部ViewPager指示器的字体变色,该效果图是这样的: 大概是今天头条的app,神奇的地方就在于,切换ViewPager页面的时候,顶部指示器改成了字体颜色的变化,个人觉得还是不错的. 那么核心的地方就是做一个支持字体这样逐渐染色就可以了,我大概想了32s,扫描了一些可能实现的方案,最终定位了一个靠谱的,下面我就带大家开始实现的征程. 实现之前贴一下我们的效果图: 1.简单使用 效果如上图了,关于颜失色的改变我添加了两个方向,一个是左方向,一个是有方向. 单纯的使用,可能觉得没什么

  • Android 静默安装和智能安装的实现方法

    1 简介 最近研究了Android的静默安装和智能安装,于是写博客记录一下.静默安装就是无声无息的在后台安装apk,没有任何界面提示.智能安装就是有安装界面,但全部是自动的,不需要用户去点击. 首先强调两点:静默安装必须要root权限 智能安装必须要用户手动开启无障碍服务 2 原理 静默安装.卸载的原理就是利用pm install命令来安装apk,pm uninstall 来卸载apk. 智能安装是利用android系统提供的无障碍服务AccessibilityService,来模拟用户点击,从

  • Android开发教程之调用摄像头功能的方法详解

    本文实例讲述了Android调用摄像头功能的方法.分享给大家供大家参考,具体如下: 我们要调用摄像头的拍照功能,显然 第一步必须加入调用摄像头硬件的权限,拍完照后我们要将图片保存在SD卡中,必须加入SD卡读写权限,所以第一步,我们应该在Android清单文件中加入以下代码 摄像头权限: <uses-permission android:name="android.permission.CAMERA"/> SD卡读写权限: <uses-permission androi

  • Android调用系统照相机拍照与摄像的方法

    前言 在很多场景中,都需要用到摄像头去拍摄照片或视频,在照片或视频的基础之上进行处理.但是Android系统源码是开源的,很多设备厂商均可使用,并且定制比较混乱.一般而言,在需要用到摄像头拍照或摄像的时候,均会直接调用系统现有的相机应用,去进行拍照或摄像,我们只取它拍摄的结果进行处理,这样避免了不同设备的摄像头的一些细节问题.本篇博客将介绍在Android应用中,如何调用系统现有的相机应用去拍摄照片与短片,并对其进行处理,最后均会以一个简单的Demo来演示效果. 1.系统现有相机应用的调用 对于

随机推荐