注意:本文章只适用于技术交流,请你友好交流净化开发环境

思考

  • 由于谷歌强制在Android应用开发中编写拍照程序是必需要有图像预览的。这对那些恶意程序比如Android中泛滥的Service在后台偷偷记录手机用户的行为与周边信息。这样的门槛还包括手机厂商自带的相机软件在拍照时必须是有声音,这样要避免一些偷拍的情况;据说oppo find系列及vivo Nex系列可以检测出那些流氓软件这么做了。

步骤

  1. 创建一个surfaView对象

    preview = new SurfaceView(this);holder = preview.getHolder();// deprecated setting, but required on Android versions prior to 3.0holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);holder.addCallback(new SurfaceHolder.Callback() {@Override//The preview must happen at or after this point or takePicture failspublic void surfaceCreated(SurfaceHolder holder) {//创建成功以后打开相机/*** camaraType: Camera.CameraInfo.CAMERA_FACING_FRONT :打开前置摄像头* camaraType: Camera.CameraInfo.CAMERA_FACING_BACK :打开后置摄像头*/try {camera = Camera.open(camaraType);try {camera.setPreviewDisplay(holder);} catch (IOException e) {throw new RuntimeException(e);}camera.startPreview();Log.d(TAG, "Started preview");camera.takePicture(null, null, pictureCallback);} catch (Exception e) {if (camera != null)camera.release();throw new RuntimeException(e);}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}});
    
  2. 使用WindowManager增加一个1*1px的悬浮框
    wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);//设置悬浮框为:1 * 1 :记得用后将其remove否则其他界面得不到交点,并且下拉框会有提示:应用在他应用的上层显示的应用,关于这个设置选项WindowManager.LayoutParams params = new WindowManager.LayoutParams(1, 1, //设置成宽:1px , 高:1pxWindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,0, PixelFormat.UNKNOWN);/*** 根据不同的版本设置:TYPE_APPLICATION_OVERLAY 在低于26版本中报错崩溃* 但是在Android O的系统中,google规定申请android.permission.SYSTEM_ALERT_WINDOW权限的应用需要给悬浮窗口设置如下params.type*/if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;}
  1. 将SurfaceView添加到WindowManager中

    wm.addView(preview, params);
  2. 拍照结果的回掉保存(注意: 上层的1*1px的拍照布局 : 一定要移除,一定要移除,一定要移除,重要的妖怪打三遍---不然切换点击屏幕其他位置手机是获取不到焦点,没有反应的)

    /*** 拍照开始后结果的回调*/
    private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {Log.d(TAG, "onPictureTaken");if(null == data){return;}Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);camera.stopPreview();Matrix matrix = new Matrix();matrix.postRotate((float) 270.0); //旋转拍照结果,可能方向不正确bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, false);Log.d(TAG, "original bitmap width: " + bitmap.getWidth() +" height: " + bitmap.getHeight());Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth()/3, bitmap.getHeight()/3, true);Log.d(TAG,"size bitmap width "+sizeBitmap.getWidth()+" height "+sizeBitmap.getHeight());//裁剪bitmapint leftOffset = (int)(sizeBitmap.getWidth() * 0.25);int topOffset = (int)(sizeBitmap.getHeight() * 0.25);Rect rect = new Rect(leftOffset, topOffset, sizeBitmap.getWidth() - leftOffset,sizeBitmap.getHeight() - topOffset);Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap,rect.left, rect.top, rect.width(), rect.height());try {//保存图片FileOutputStream outputStream = new FileOutputStream(Environment.getExternalStorageDirectory().toString()+"/photoResize.jpg");sizeBitmap.compress(Bitmap.CompressFormat.JPEG, 30, outputStream);outputStream.close();FileOutputStream outputStreamOriginal = new FileOutputStream(Environment.getExternalStorageDirectory().toString()+"/photoOriginal.jpg");bitmap.compress(Bitmap.CompressFormat.JPEG, 20, outputStreamOriginal);outputStreamOriginal.close();FileOutputStream outputStreamCut = new FileOutputStream(Environment.getExternalStorageDirectory().toString()+"/photoCut.jpg");rectBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStreamCut);outputStreamCut.close();//通知系统相册更新sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+ Environment.getExternalStorageDirectory().toString()+"/photoResize.jpg")));sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+ Environment.getExternalStorageDirectory().toString()+"/photoOriginal.jpg")));sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().toString() + "/photoCut.jpg")));Log.d(TAG,"picture saved!");if (camera != null) {camera.release();//移除上层的1*1px的拍照布局 : 一定要移除,不然点击屏幕其他位置手机没有反应wm.removeView(preview);}Toast.makeText(TakePhotoActy.this , "照片保存成功,可以开启服务上传照片然后注意删除本地相册!" , Toast.LENGTH_SHORT).show();
    //                System.exit(0); //如果没有设置removeView()方法,可以通过强制退出实现焦点回归} catch(Exception e) {e.printStackTrace();}}
    };
  3. 添加权限(导入三方权限管理) 点击查看: xxpermissions

    //android6.0以上系统需要动态申请拍照及存储权限,对于测试可以手动打开权限管理给与对应权限即可
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
  4. 权限管理

    //使用三方控件:https://github.com/getActivity/XXPermissions 简单演示
    //build中导入
    implementation 'com.hjq:xxpermissions:5.0'
    if (Build.VERSION.SDK_INT >= 23) {requestPermission();}
    private void requestPermission() {//1. 检查是否已经有该权限XXPermissions.with(this).constantRequest() //可设置被拒绝后继续申请,直到用户授权或者永久拒绝.permission(Permission.WRITE_EXTERNAL_STORAGE , Permission.CAMERA) //不指定权限则自动获取清单中的危险权限.request(new OnPermission() {@Overridepublic void hasPermission(List<String> granted, boolean isAll) {if (isAll) {
    //                            Toast.makeText(TakePhotoActy.this, "获取权限成功", Toast.LENGTH_SHORT).show();}else {
    //                            Toast.makeText(TakePhotoActy.this, "获取权限成功,部分权限未正常授予", Toast.LENGTH_SHORT).show();}}@Overridepublic void noPermission(List<String> denied, boolean quick) {if(quick) {Toast.makeText(TakePhotoActy.this, "拍照需要你授权,否则不能正常使用", Toast.LENGTH_SHORT).show();//如果是被永久拒绝就跳转到应用权限系统设置页面
    //                            XXPermissions.gotoPermissionSettings(TakePhotoActy.this);}else {Toast.makeText(TakePhotoActy.this, "获取权限失败", Toast.LENGTH_SHORT).show();}}});}

全部示例代码(在小米8:8.0系统,魅族6.0上均测试通过)

  1. activity代码:
/*** created by shi on 2018/9/10/010*/
public class TakePhotoActy extends Activity implements View.OnClickListener {private static final String TAG = "shiq";private SurfaceView preview;private SurfaceHolder holder;private final int MESSAGE_LOGIN = 1;private int camaraType = Camera.CameraInfo.CAMERA_FACING_FRONT;/*** 测试息屏状态10s拍照效果*/private Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message msg) {if (msg.what == MESSAGE_LOGIN) {Log.e(TAG, "我是收到的拍照界面");setTakePhoto();}return true;}});private WindowManager wm;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_take);initView();}private void initView() {Button bt_photo_force = findViewById(R.id.bt_photo_force);bt_photo_force.setOnClickListener(this);Button bt_photo_back = findViewById(R.id.bt_photo_back);bt_photo_back.setOnClickListener(this);if (Build.VERSION.SDK_INT >= 23) {requestPermission();}//验证息屏状态下的拍照:五秒后发送//handler.sendEmptyMessageDelayed(MESSAGE_LOGIN, 10000);}/*** 权限管理请求:需要存储及相机权限,如果上传到服务器后删除,加上请求读取权限*/private void requestPermission() {//1. 检查是否已经有该权限XXPermissions.with(this).constantRequest() //可设置被拒绝后继续申请,直到用户授权或者永久拒绝.permission(Permission.WRITE_EXTERNAL_STORAGE , Permission.CAMERA) //不指定权限则自动获取清单中的危险权限.request(new OnPermission() {@Overridepublic void hasPermission(List<String> granted, boolean isAll) {if (isAll) {
//                            Toast.makeText(TakePhotoActy.this, "获取权限成功", Toast.LENGTH_SHORT).show();}else {
//                            Toast.makeText(TakePhotoActy.this, "获取权限成功,部分权限未正常授予", Toast.LENGTH_SHORT).show();}}@Overridepublic void noPermission(List<String> denied, boolean quick) {if(quick) {Toast.makeText(TakePhotoActy.this, "拍照需要你授权,否则不能正常使用", Toast.LENGTH_SHORT).show();//如果是被永久拒绝就跳转到应用权限系统设置页面
//                            XXPermissions.gotoPermissionSettings(TakePhotoActy.this);}else {Toast.makeText(TakePhotoActy.this, "获取权限失败", Toast.LENGTH_SHORT).show();}}});}private Camera camera = null;@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.bt_photo_force:camaraType = Camera.CameraInfo.CAMERA_FACING_FRONT;break;case R.id.bt_photo_back:camaraType = Camera.CameraInfo.CAMERA_FACING_BACK;break;}setTakePhoto();}private void setTakePhoto() {preview = new SurfaceView(this);holder = preview.getHolder();// deprecated setting, but required on Android versions prior to 3.0holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);holder.addCallback(new SurfaceHolder.Callback() {@Override//The preview must happen at or after this point or takePicture failspublic void surfaceCreated(SurfaceHolder holder) {//创建成功以后打开相机/*** camaraType: Camera.CameraInfo.CAMERA_FACING_FRONT :打开前置摄像头* camaraType: Camera.CameraInfo.CAMERA_FACING_BACK :打开后置摄像头*/try {camera = Camera.open(camaraType);try {camera.setPreviewDisplay(holder);} catch (IOException e) {throw new RuntimeException(e);}camera.startPreview();Log.d(TAG, "Started preview");camera.takePicture(null, null, pictureCallback);} catch (Exception e) {if (camera != null)camera.release();throw new RuntimeException(e);}}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}});wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);//设置悬浮框为:1 * 1 :记得用后将其remove否则其他界面得不到交点,并且下拉框会有提示:应用在他应用的上层显示的应用,关于这个设置选项WindowManager.LayoutParams params = new WindowManager.LayoutParams(1, 1, //设置成宽:1px , 高:1pxWindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, 0, PixelFormat.UNKNOWN);/*** 根据不同的版本设置:TYPE_APPLICATION_OVERLAY 在低于26版本中报错崩溃* 但是在Android O的系统中,google规定申请android.permission.SYSTEM_ALERT_WINDOW权限的应用需要给悬浮窗口设置如下params.type*/if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;}wm.addView(preview, params);}/*** 拍照开始后结果的回调*/private Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {@Overridepublic void onPictureTaken(byte[] data, Camera camera) {Log.d(TAG, "onPictureTaken");if (null == data) {return;}Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);camera.stopPreview();Matrix matrix = new Matrix();matrix.postRotate((float) 270.0); //旋转拍照结果,可能方向不正确bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),bitmap.getHeight(), matrix, false);Log.d(TAG, "original bitmap width: " + bitmap.getWidth() +" height: " + bitmap.getHeight());Bitmap sizeBitmap = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth() / 3, bitmap.getHeight() / 3, true);Log.d(TAG, "size bitmap width " + sizeBitmap.getWidth() + " height " + sizeBitmap.getHeight());//裁剪bitmapint leftOffset = (int) (sizeBitmap.getWidth() * 0.25);int topOffset = (int) (sizeBitmap.getHeight() * 0.25);Rect rect = new Rect(leftOffset, topOffset, sizeBitmap.getWidth() - leftOffset,sizeBitmap.getHeight() - topOffset);Bitmap rectBitmap = Bitmap.createBitmap(sizeBitmap,rect.left, rect.top, rect.width(), rect.height());try {//保存图片FileOutputStream outputStream = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/photoResize.jpg");sizeBitmap.compress(Bitmap.CompressFormat.JPEG, 30, outputStream);outputStream.close();FileOutputStream outputStreamOriginal = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/photoOriginal.jpg");bitmap.compress(Bitmap.CompressFormat.JPEG, 20, outputStreamOriginal);outputStreamOriginal.close();FileOutputStream outputStreamCut = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/photoCut.jpg");rectBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStreamCut);outputStreamCut.close();//通知系统相册更新sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().toString() + "/photoResize.jpg")));sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().toString() + "/photoOriginal.jpg")));sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + Environment.getExternalStorageDirectory().toString() + "/photoCut.jpg")));Log.d(TAG, "picture saved!");if (camera != null) {camera.release();//移除上层的1*1px的拍照布局 : 一定要移除,不然点击屏幕其他位置手机没有反应wm.removeView(preview);}Toast.makeText(TakePhotoActy.this, "照片保存成功,可以开启服务上传照片然后注意删除本地相册!", Toast.LENGTH_SHORT).show();//System.exit(0); //如果没有设置removeView()方法,可以通过强制退出实现焦点回归} catch (Exception e) {e.printStackTrace();}}};
}   
  1. 布局文件很简单就是普通的两个button测试

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent"><Button
        android:id="@+id/bt_photo_force"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="前置拍照"/><Button
        android:id="@+id/bt_photo_back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="后置拍照" /></LinearLayout

使用SurfaceView实现手机息屏状态下的静默拍照保存,上传服务器相关推荐

  1. 计算机锁屏之后QQ音乐停止播放了,MAC电脑如何在息屏状态下让QQ音乐能继续播放音乐...

    MAC电脑如何在息屏状态下让QQ音乐能继续播放音乐 一般MAC电脑息屏之后,大部分的应用都是会停止运行的,比如QQ音乐,就不会再播放音乐.今天小编就跟大家分享下MAC电脑如何在息屏状态下让QQ音乐能继 ...

  2. Android息屏状态下启动App

    Android息屏状态下启动App 需求简介 分析 1.点亮屏幕 2.通过startActivity方法启动App 启动失败问题 原因 解决 需求简介 设备在息屏的状态下,通过特定的物理按键去启动Ca ...

  3. 锁屏界面提示某些设置已隐藏_分享华为手机锁屏状态下几大隐藏小功能,快来体验...

    分享华为手机锁屏状态下几大隐藏小功能. 一.熄屏显示 平常想要在手机上看时间.日期等信息,需要点亮屏幕,很费时费电,还损耗手机按键. 熄屏显示,支持在熄屏状态下显示时间.日期.手机电量等基础信息,更有 ...

  4. Android APP息屏状态下收到通知解决方案

    1.问题 最近负责的Android APP,用户反馈无法收到通知,尤其是息屏状态下无法收到通知. 这些APP,笔者以前都测试过,可以收到推送的.但测试以后,发现在新的Android上,确实收不到通知和 ...

  5. android熄屏微信收到原理,求助,如何才能在息屏状态下显示收到的微信消息内容?...

    5楼搞错了...重发给你. 本帖最后由 散闲游人 于 2018-4-14 14:18 编辑 如P20的设置和手机管家和m10是一样的,请参考如下设置: 一:点击手机管家,点启动管理,以进入手动管理为例 ...

  6. Android 后台实现录像,无页面息屏状态下后台录像

    用的是电量图标,前台服务,华为手机验证OK public class VideoRecorderService extends Service implements SurfaceHolder.Cal ...

  7. Android app开发:息屏状态下唤醒屏幕

    方式1:申请wake_lock唤醒屏幕 //权限: <uses-permission android:name="android.permission.WAKE_LOCK" ...

  8. 2022-06-30 Android app WakeLock息屏状态下唤醒屏幕并且解锁demo

    一.解锁 //屏锁管理器 KeyguardManager km= (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE ...

  9. android shell检查是否锁屏_锁屏状态下的华为手机不显示消息?这样设置!

    点击蓝字关注我们 最近两天同事小张总是一副闷闷不乐的样子,小编琢磨着,刚过完情人节,这状态不对呀. 后来一问才知道,原来是小张晚上打游戏,女朋友微信找他,小张没看到,所以俩人就吵了起来.果然,男人们都 ...

最新文章

  1. 影响网站转化率的10大误区(上)
  2. 码这么多字只为搞懂如何在Python和Java中使用JSON
  3. 详解Python垃圾回收机制
  4. Linux下C++连MySQL数据库
  5. APP测试流程和测试点
  6. Linux Ubuntu 安装编译Opencv 3.4.3 C++开发环境
  7. 【实用工具】之CSDN表格模板
  8. mongodb处理库 php_MongoDB数据库常用操作PHP代码
  9. [数据可视化] 饼图(Pie Chart)
  10. 计划行为理论(TPB,Theory of Planned Behavior)
  11. ubuntu如何安装libz库
  12. 详解Unity中的粒子系统Particle System (八)
  13. php拼音首拼,PHP 汉字转拼音(可首字母)
  14. 如何设置迪文T5L串口屏的防盗版功能?
  15. 量化交易入门笔记-KD指标策略
  16. Ocean Color数据批量下载——海洋物理分布式活动档案中心PO.DAAC
  17. 【SQL Server】10分钟快速安装SQL Server
  18. Appium中使用swipe方法时候出现的问题建议使用flick方法
  19. 超简单集成ML kit 实现听写单词播报
  20. 微信搜一搜下拉词自动化批量采集机器人

热门文章

  1. CTF Crypto RSA合集(新生赛难度)
  2. 余额宝漏洞 可绕过用户登录 5W奖励“白帽子”
  3. 记录一次联通cdn劫持的请求响应报文
  4. 22西北大学网络和数据中心软件专硕845考研上岸备考经
  5. 骆昊python100天百度网盘_GitHub - wnxy/Python-100-Days: Python - 100天从新手到大师
  6. SecureFX for Mac(跨平台文件传输客户端)
  7. Fluent UDF【8】:编译型UDF
  8. tradingview中绘制市盈率曲选
  9. 连界创新获翊翎资本、连界资本8000万元融资,专注赋能产业升级...
  10. 网关和IP地址不在同一个网段下