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;}

read more...

2017年3月15日 星期三

android app背景的touch事件

android app背景的touch事件,在網路上有資料,整理一下

1.
AndroidManifest.xml要給權限,加入

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />



AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yplin.myapplication11">
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.VIBRATE" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity11">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".floatActivity"></activity>
    </application>

</manifest>


2.
取得WindowManager(是一個更上層的obj,可google一下)
建立dunmmy view(空的view)
設置LayoutParams
根據LayoutParams將dummy view加mWindowManager



package com.example.yplin.myapplication11;

import android.graphics.PixelFormat;


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

import android.view.MotionEvent;
import android.view.View;

import android.view.WindowManager;
import android.widget.LinearLayout;

//繼承AppCompatActivityAppCompatActivity實做OnTouchListener
public class MainActivity11 extends AppCompatActivity implements   View.OnTouchListener {

    private LinearLayout lc=null;
    private CustomView1 canvas_view =null;
    @Override    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main11);
        lc=(LinearLayout)findViewById(R.id.view1);
        canvas_view = new CustomView1(this);
        Context mContext=getApplicationContext();
        // 取得WindowManagerWindowManager        
        WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        //建立一個dummy的view        
        LinearLayout mDummyView = new LinearLayout(mContext);
        //新設置LayoutParams叫params,注意androidManifest.xml需給權限        
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                1, /* width */                1, /* height */                
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSPARENT        );
        //可更改dummy view設置params        
        //mDummyView.setLayoutParams(params);        
        //dummy view設置touch傾聽事件        
        mDummyView.setOnTouchListener(this);
        //根據params將dummy view加mWindowManager
        mWindowManager.addView(mDummyView, params);
    }
    //實作OnTouchListener的onTouch傾聽事件方法    
        @Override    
        public boolean onTouch(View v, MotionEvent event) {
        //LinearLayout lc=(LinearLayout)findViewById(R.id.view1);        
        if(lc.indexOfChild(canvas_view)==-1) {

            lc.addView(canvas_view);
        }
        else        {
            lc.removeAllViews();
        }
        return false;
    }

    class CustomView1 extends View{

        Paint paint;

        public CustomView1(Context context) {
            super(context);
            paint = new Paint();                   //設置一新畫筆            
            paint.setColor(Color.RED);             //黄色            
            paint.setStrokeJoin(Paint.Join.ROUND);
            paint.setStrokeCap(Paint.Cap.ROUND);
            paint.setStrokeWidth(3);               //設置一筆刷大小3        }

        //create circle        
        @Override        
        protected void onDraw(Canvas canvas) {

            canvas.drawCircle(100, 100, 90, paint);

        }

    }
}


實際結果,背景下觸摸螢幕,touch事件被這app接收了


















































read more...

2017年3月12日 星期日

用SIKULI去android手機內抓圖做點擊

一般網路上看到的,都是用電腦接手機投射到電腦螢幕,抓電腦手機螢幕的圖,在外國網站有人用sikuli直接去手機內抓螢幕的圖,控制手機,而不是在電腦螢幕去做控制,終於試出來了,你要先安裝adb,若你有安裝android studio,其實就有adb了,在安裝目錄下.../Android/sdk/platform-tools/adb device -l

1.
terminal下執行下面指令連接手機

../Android/sdk/platform-tools/adb device -l

2.
sikuli API android目錄有一個ADBScreen class,import它,叫用start函式,會傳回android螢幕物件screen,我們就可在android手機內做動作,譬如螢幕抓圖,螢幕點擊.....等,下面是一個簡單抓圖和螢幕點擊

import org.sikuli.android.ADBScreen as Ascreen #把ADBScreen直接設成Ascreen
x=Ascreen.start()      #傳回androi screen


3.
SIKULI-IDE的python程式

#####################convert######################
#請用android studio看jar的程式
assert load('/Users/yplin/sikulix.jar')
import org.sikuli.android.ADBScreen as Ascreen #把ADBScreen直接設成Ascreen
import subprocess
#有安裝android studio找到adb用adb devices -l連接手機
#subprocess.call("/Users/yplin/Library/Android/sdk/platform-tools/adb devices -ladb devices -l".split())
#import org.sikuli.android.ADBClient as Aclient
#print(Aclient.getDevice())

x=Ascreen.start()      #傳回androi screen
use(x)                 #use
x.needsUnLock = False
wakeUp(2)
#store_path_base = os.getenv("HOME")
#new_path=os.path.join(store_path_base,"SIKULI/PNG/t.png")
new_path="/Users/yplin"
reg=x.newRegion(0,0,1000,1000)  #手機區域region
#reg.highlight(1)
img=x.capture(reg)      #抓手機螢幕圖
click(reg)              #點擊手機螢幕
img.save(new_path)      #存檔

popup("capture picture ok")

抓到圖的結果(左邊下面的小圖):

read more...

2016年7月22日 星期五

SKILL寫的可做出各種SUB RING

使用ROD寫出SUB RIN,可畫出N RING ,P RING,SINGLE RING,DOUBLE RING,DOUBLE RING可指定RING是P在內側或外側,CONTACT各要幾排


ENTER PATH SINGLE RING



ENTER BOX DOUBLE RING

 ENTER BOX DOUBLE RING設M1可chop,其它不可chop






read more...

2016年1月30日 星期六

想念的加州公園

這是第四次來美國了,己經來一個月了,上上星期,到單車店刷卡400多美金,買了一輛單車來騎,這星期六,自己騎著單車到公園散心,天氣不錯,躺在公園的草地上,中午有出一些太陽,很溫暖,左公園繞一陣子,看見鴨子在濕地,游來游夫,悠閒的樣子。

 想到以前來美國住的旅館晃晃,沿著單車道騎,想到以前住旅館時,假日都是走這,小跑步到公園散心,有著許多回憶,好懷念啊,下午4點多了,去附近吃飯,回到住的己經5點多了。
拍了一些照片,留下紀念





read more...

2015年12月25日 星期五

在樹莓派使用 python gtk2 cairo 製照動畫

安裝
apt-get install python-cairo python-cairo-dev python-gi-cairo python-gtk2 python-gtk2-dev 

把之前寫的程式稍微改一下,就能在樹莓派製作game,gtk3有bug,動畫會停住,還是gtk2好用,本來想寫很多,人一懶工作又累,假日又要運動,每次都寫一些就停掉了 ,之前python寫的程式game,拿到樹莓派,哈哈,還滿順的,有空再據續寫.





read more...

2015年12月18日 星期五

raspberry pi2裝debian8

終於在raspberry pi2裝好debian8,不是樹梅派官方下載的那個jessie,覺的還是debian好用,執行上比其他版本快很多,這個dd完只有幾百M,一開始只能進到文字模式,沒聲音也沒x windows,套件要自己從網路安裝(apt-get install),以前剛玩debiab都是要自己一個個慢慢裝,現在網路快多了,都有整個包好的OS DVD可下載,已經很久沒一個一個裝了,遇到問題google一下應該都有答案.

VLC和KODI很難搞,哈哈,查了網路一堆資料,試了好幾個星期,終於搞定,有點成就感,debian8裝完VCL沒畫面,試很久都失敗,連原碼下載編譯都一樣,換樹梅派官方的那個jessie源裝也失敗,後來大概知道是video output沒裝上,網路上找不到裝video output資料,死馬當活馬醫,apt-cache search把還疑的試著裝看看,竟然有畫面了,不過不順暢,試太多東西可能衝突,就dd一個新的os,重裝VLC,果然就OK,4顆cpu大約60~70%,很順,GPU硬體加速有起來,超興奮,不過只能全螢幕播放.

KODI也是試很久,最後也給我弄起來了,不過只能切換到root,啟動KODI,一般使用者起不來,應該只是權限關係,算了已經很OK了,把樹梅派弄跟桌機差不多一模一樣了.


桌面登入安裝lightdm,桌面管理安裝icewm,聲音系統安裝alsa


收集安裝了一些套件,程式開發anjuta(加glade),mp3音樂播放器xmms2,影音播放器VLC,視聽中心KODI,安裝GPIO,安裝中文輸入和檔案瀏覽器,web瀏覽器,文字編輯器,相片瀏覽器,設定spi-dev model


VLC
KODI



  我的樹莓派  debian8 桌面
新 kernel
xmms2
anjuta
vlc
kodi







read more...