Android实现后台连续静默拍照

 简单的背景(一堆废话)
前段时间在做一个Android项目,其中涉及到需要实现“后台”静默打开前置摄像头连续拍照进行人脸检测。

尝试过网上各种方法,主要做法就是将预览透明+变小(0.1x0.1),但是这种做法并不能实现所谓的“后台”效果,你只能在一个透明界面啥也干不了,不能进行交互,或者出现闪退等问题。

这个问题基本困扰了我一个多月,在此感谢 https://blog.csdn.net/qq_31530015/article/details/52015170 ,让我一下有了新思路,一天就搞定了这个问题。

 

 总体思路
      mini悬浮窗 + 间隔2s抓取一次预览帧 + 处理(人脸检测/保存图片)
 

 话不多说,上代码
项目源码指路:https://github.com/SleepyRae/PhoneSavior

 

在activity中启动一个service

aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean f) {
                //控制开关字体颜色
               // Intent face = new Intent(FaceDetectActivity.this, CameraService.class);
               // Intent face = new Intent(FaceDetectActivity.this, CameraActivity.class);
                Intent face = new Intent(FaceDetectActivity.this, MyService.class);
                if (f) {
                    aSwitch.setSwitchTextAppearance(FaceDetectActivity.this, R.style.s_true);
                    startService(face);
                   // startService(face);
                    ServiceIsOn = true;
                    Toast.makeText(getApplicationContext(), "开启人脸检测啦", Toast.LENGTH_SHORT).show();
                } else {
                    aSwitch.setSwitchTextAppearance(FaceDetectActivity.this, R.style.s_false);
                    stopService(face);
                    ServiceIsOn = false;
                    //CameraActivity.finishActivity();
                    Toast.makeText(getApplicationContext(), "关掉人脸检测啦", Toast.LENGTH_SHORT).show();
                }
 
            }
        });
 

MyService:

package com.example.inspiron.phonesavior.Service;
 
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
 
public class MyService extends Service {
 
    /**
     * 自定义窗口
     */
    private MyWindow myWindow;
    /**
     * 窗口管理者
     */
    private WindowManager mWindowManager;
    /**
     * 窗口布局参数
     */
    private LayoutParams Params;
 
    private Handler handler = new Handler() {
    public void handleMessage(Message msg) {
 
            if (isHome()&& myWindow!=null) {
                // 如果回到桌面,则显示悬浮窗
                if (!myWindow.isAttachedToWindow()) {
                    mWindowManager.addView(myWindow, Params);
                }
 
            } else {
                // 如果在非桌面,则去掉悬浮窗
                /*if (myWindow.isAttachedToWindow()) {
                    mWindowManager.removeViewImmediate(myWindow);
                }*/
            }
            super.handleMessage(msg);
        };
    };
 
    @Override
    public IBinder onBind(Intent arg0) {
 
        return null;
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("MyService", "onCreate");
        // 定时器类
        Timer timer = new Timer();
        timer.schedule(task, 1000, 1000); // 1s后执行task,经过1s再次执行
        
        //对于6.0以上的设备
        if (Build.VERSION.SDK_INT >= 23) {
            //如果支持悬浮窗功能
            if (Settings.canDrawOverlays(getApplicationContext())) {
                 showWindow();
            } else {
                //手动去开启悬浮窗
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                getApplicationContext().startActivity(intent);
            }
        } else {
                //6.0以下的设备直接开启
                showWindow();
        }
 
    }
 
    private void showWindow() {
        //创建MyWindow的实例
        myWindow = new MyWindow(getApplicationContext());
        //窗口管理者
        mWindowManager = (WindowManager) getSystemService(Service.WINDOW_SERVICE);
        //窗口布局参数
        Params = new WindowManager.LayoutParams();
        //布局坐标,以屏幕左上角为(0,0)
        Params.x = 0;
        Params.y = 0;
 
        //布局类型
        Params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; // 系统提示类型,重要
 
        //布局flags
        Params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 不能抢占聚焦点
        Params.flags = Params.flags | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
        Params.flags = Params.flags | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; // 排版不受限制
        Params.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 
        //布局的gravity
        Params.gravity = Gravity.LEFT | Gravity.TOP;
 
        //布局的宽和高
        Params.width =  1;
        Params.height = 1;
 
        myWindow.setOnTouchListener(new OnTouchListener() {
 
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                 switch (event.getAction()) {                
                 
                 case MotionEvent.ACTION_MOVE:
                    Params.x = (int) event.getRawX() - myWindow.getWidth() / 2;
                    Params.y = (int) event.getRawY() - myWindow.getHeight() / 2;
                    //更新布局位置
                    mWindowManager.updateViewLayout(myWindow, Params);
                    
                    break;
                }
                 return false;
            }
         });
 
    }
 
    //定时发送message给Handler
    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            Message message = new Message();
            handler.sendMessage(message);
        }
    };
 
    
    /**
     * @return 获取桌面(Launcher)的包名
     */
    private List getHomes() {
        List names = new ArrayList();
        PackageManager packageManager = this.getPackageManager();
         
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        List resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo info : resolveInfo) {
            names.add(info.activityInfo.packageName);
        }
        return names;
    }
 
    /**
     * @return 判断当前是否是桌面
     */
    public boolean isHome() {
        ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        List rti = mActivityManager.getRunningTasks(1);
        List strs = getHomes();
        if (strs != null && strs.size() > 0) {
            return strs.contains(rti.get(0).topActivity.getPackageName());
        } else {
            return false;
        }
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        myWindow = null;
    }
}
 

MyWindow:

package com.example.inspiron.phonesavior.Service;
 
import android.app.Service;
import android.content.Context;
import android.graphics.*;
import android.hardware.Camera;
import android.media.FaceDetector;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.example.inspiron.phonesavior.R;
 
import java.io.ByteArrayOutputStream;
import java.io.IOException;
 
public class MyWindow extends LinearLayout implements SurfaceTextureListener {
 
    private TextureView textureView;
 
    /**
     * 相机类
     */
    private Camera myCamera;
    private Context context;
 
    private WindowManager mWindowManager;
    private int num = 0;
    private int curnum = 0;
    private Bitmap bitmap_get = null;
    private int count = 0;
 
    public MyWindow(Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.window, this);
        this.context = context;
        
        initView();
    }
 
    private void initView() {
 
        textureView = (TextureView) findViewById(R.id.textureView);
        textureView.setSurfaceTextureListener(this);
        mWindowManager = (WindowManager) context.getSystemService(Service.WINDOW_SERVICE);
    }
 
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        if (myCamera == null) {
            // 创建Camera实例
            //尝试开启前置摄像头
            Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
            for (int camIdx = 0, cameraCount = Camera.getNumberOfCameras(); camIdx < cameraCount; camIdx++) {
                Camera.getCameraInfo(camIdx, cameraInfo);
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    try {
                        Log.d("Demo", "tryToOpenCamera");
                        myCamera = Camera.open(camIdx);
                    } catch (RuntimeException e) {
                        e.printStackTrace();
                    }
                }
            }
            try {
                // 设置预览在textureView上
                myCamera.setPreviewTexture(surface);
                myCamera.setDisplayOrientation(SetDegree(MyWindow.this));
                // 开始预览
                myCamera.startPreview();
                handler.sendEmptyMessage(BUFFERTAG);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
 
    private void getPreViewImage() {
 
        if (myCamera != null){
            myCamera.setPreviewCallback(new Camera.PreviewCallback(){
 
                @Override
                public void onPreviewFrame(byte[] data, Camera camera) {
                    Camera.Size size = camera.getParameters().getPreviewSize();
                    try{
                        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_get = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
 
                            //**********************
                            //因为图片会放生旋转,因此要对图片进行旋转到和手机在一个方向上
                            bitmap_get = rotateMyBitmap(bitmap_get);
                            //**********************************
 
                            stream.close();
 
 
                        }
                    }catch(Exception ex){
                        Log.e("Sys","Error:"+ex.getMessage());
                    }
                }
 
 
            });
        }
 
 
    }
 
    private void myFace(Bitmap bitmap) {
        bitmap = bitmap.copy(Bitmap.Config.RGB_565, true);
        //假设最多有1张脸
        int MAXfFaces = 1;
        int numOfFaces = 0;
        FaceDetector mFaceDetector = new FaceDetector(bitmap.getWidth(),bitmap.getHeight(),MAXfFaces);
        FaceDetector.Face[] mFace = new FaceDetector.Face[MAXfFaces];
        //获取实际上有多少张脸
        numOfFaces = mFaceDetector.findFaces(bitmap, mFace);
        Log.v("------------->",  "pic num:" + num + "  face num:"+numOfFaces +" count:"+count);
        if(numOfFaces == 1 && num!=curnum){
            count++;
            curnum = num;
            Log.d("pic num:" + num,  "  eyesDistance:"+ mFace[0].eyesDistance() +"  confidence:"+ mFace[0].confidence());
        }
    }
 
    public Bitmap rotateMyBitmap(Bitmap mybmp){
        //*****旋转一下
        Matrix matrix = new Matrix();
        matrix.postRotate(270);
 
        Bitmap bitmap = Bitmap.createBitmap(mybmp.getWidth(), mybmp.getHeight(), Bitmap.Config.ARGB_8888);
 
        Bitmap nbmp2 = Bitmap.createBitmap(mybmp, 0,0, mybmp.getWidth(),  mybmp.getHeight(), matrix, true);
 
        saveImage(nbmp2);
        return nbmp2;
    };
 
    public void saveImage(Bitmap bmp) {
        myFace(bmp);
        /*String fileName ="Camera"+ num +".jpg";
        File file = new File(getExternalStorageDirectory(), fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }*/
    }
 
    public static final int BUFFERTAG = 100;
    public static final int BUFFERTAG1 = 101;
    private boolean isGetBuffer = true;
 
    Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            switch(msg.what){
                case BUFFERTAG:
                    if(count > 60){         //十分钟提示
                        count = 0;
                        Toast.makeText(context.getApplicationContext(), "检测到您持续用眼,请注意用眼", Toast.LENGTH_SHORT).show();
                    }
                    if(isGetBuffer){
                        num++;
                        getPreViewImage();
                        handler.sendEmptyMessageDelayed(BUFFERTAG1, 3000);
 
                    }else{
                        myCamera.setPreviewCallback(null);
                    }
                    break;
                case BUFFERTAG1:
                    myCamera.setPreviewCallback(null);
                    handler.sendEmptyMessageDelayed(BUFFERTAG, 5000);
                    break ;
            }
        };
 
 
    };
 
    Runnable runnable=new Runnable(){
        @Override
        public void run() {
            // TODO Auto-generated method stub  
            //要做的事情,这里再次调用此Runnable对象,以实现每两秒实现一次的定时器操作  
 
            handler.postDelayed(this, 10000);
            Log.d("test", "running!!!");
        }
    };
 
    private int SetDegree(MyWindow myWindow) { 
        // 获得手机的方向
        int rotation = mWindowManager.getDefaultDisplay().getRotation();
        int degree = 0;
        // 根据手机的方向计算相机预览画面应该选择的角度
        switch (rotation) {
        case Surface.ROTATION_0:
            degree = 90;
            break;
        case Surface.ROTATION_90:
            degree = 0;
            break;
        case Surface.ROTATION_180:
            degree = 270;
            break;
        case Surface.ROTATION_270:
            degree = 180;
            break;
        }
        return degree;
    }
 
    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        myCamera.stopPreview(); //停止预览
        myCamera.release();     // 释放相机资源
        myCamera = null;
 
        return false;
    }
 
    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
 
    }
 
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
 
    }
 
}
 

 代码参考
       悬浮窗: https://blog.csdn.net/qq_31530015/article/details/52015170 

       抓取预览帧:https://blog.csdn.net/u010277233/article/details/52193068
————————————————
版权声明:本文为CSDN博主「CodingRae」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/CodingRae/article/details/89679758

你可能感兴趣的:(通讯原理及实例)