2017年4月2日 星期日

Android5.0 使用mediaProjection抓取手機螢幕





花了ㄧ些時間,不用root手機,要如何用android java程式抓取手機螢幕的畫面,最後找到android5.0以上版本,我們可以使用mediaProjectio這個API來截取螢幕或錄制螢幕,首先要實作onActivity這個method,要讓使用者決定是否要啟用,會出現對話框,使用者按確定後,取得ImageReader就可做後續處理,若使用者按Canselled就不能使用mediaProjection來截取螢幕,google開放這樣做法,就不會有隱私權的問題,上面影片我還使用windowmanager來達成抓取開啟任何app,都能截取畫面,一開使按確定,使用者開啟瀏覽器,按左上角play,點擊螢幕截取框框中畫面,加到左下角主頁面中的image view,按左上角離開,就會跳回主頁面看到截圖,目前整個程式,我還沒寫很完整,寫好在放上來,寫到這樣都是google上找的資料。

補充一下,這個VirtualDisplay,是在onActivityResult中初始化,才是正確的,不是在事件中去初始化,這樣程式app,只會產生一個VirtualDisplay,若是在事件中初始化,或放在執行緒,那就會產生太多的VirtualDisplay,會導致內存問題的.
            vp=mp.createVirtualDisplay(..........................);
就我的意識,這VirtualDisplay和機器螢幕是同步,獲取ImgaeReader就是即時的.
        

A.實作onActivityResult讓使用者對話開始使用mediaProjection

MyActivity:
    ............
    private MediaProjectionManager mpm;
    private MediaProjection mp;
    private ImageReader ir=null;
    private VirtualDisplay vp;
    private static final int REQUEST_NUMBER=1001;
    .....................
    mpm=(MediaProjectionManager)getSystemService(MEDIA_PROJECTION_SERVICE);
    startActivityForResult(mpm.createScreenCaptureIntent(),REQUEST_NUMBER);
    .....................
    //讓使用者決定可以使用mediaProjection,跳出對話框,使用者按OK,便可使用ir抓取screen picture,否則return
    @Override
    public void onActivityResult(int requestCode,int resultCode,Intent data){

        if(REQUEST_NUMBER==requestCode){
            if(resultCode!=RESULT_OK){
                Toast.makeText(this,"USER CANCELLED",Toast.LENGTH_LONG).show();
                return;
            }
            DisplayMetrics ds=new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(ds);
            int dpi=ds.densityDpi;
            int dw=ds.widthPixels;
            int dh=ds.heightPixels;
            mp=mpm.getMediaProjection(resultCode,data);
            ir=ImageReader.newInstance(dw,dh, PixelFormat.RGBA_8888,2);
            //建立虛擬display
            vp=mp.createVirtualDisplay("ScreenCapture",dw,dh,dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,ir.getSurface(),null,null);
        }

B.取得ImageReader後,做後續Bitmap處理

package com.example.yplin.myapplication11;

import android.graphics.Bitmap;
import android.media.ImageReader;
import android.media.Image;
import android.util.Log;
import java.nio.ByteBuffer;

/**
 * Created by yplin on 2017/3/31.
 * 截取螢幕畫面
 * Activity必須決定可以使用mediaProjection
 * 產生一個ImageReader來截取螢幕畫面
 */

public class capture_screen {
    private MainActivity11 MainA=null;
private Bitmap bp=null;
    public  capture_screen(MainActivity11 ma){
        MainA=ma;
        if(ma.get_imagereader()!=null){
            ImageReader ir=ma.get_imagereader(); //image reader
            int dw=ma.get_screen_width();        //screen width
            int dh=ma.get_screen_height();       //screen height
            Image mimage = ir.acquireLatestImage();
            Image.Plane[] planes = mimage.getPlanes();
            ByteBuffer buffer = planes[0].getBuffer();
            int ps = planes[0].getPixelStride();
            int rs = planes[0].getRowStride();
            int rp = rs - ps * dw;
            bp = Bitmap.createBitmap(dw + rp / ps, dh, Bitmap.Config.ARGB_8888);
            bp.copyPixelsFromBuffer(buffer); //full screen picture
            mimage.close();
        }else{
            Log.d("USAGE:", "使用者必須決定可以使用mediaProjection");
        }
    }
    //BUG x+w不能大於screen width y+h 不能大於screen height,x和y不能小於0
    //Region Screem picture
    public Bitmap get_bitmap(int x,int y,int w,int h){
        MainA.add_image(bp.createBitmap(bp,x,y,w,h));  //把Region Screen 放置Activity的image view
        return bp.createBitmap(bp,x,y,w,h);            //Return Region Screen
    }
    public void clear_bitmap(){bp=null;}

沒有留言: