1. 程式人生 > >【10】AccessibilityService實現探探app的自動化喜歡和不喜歡+ [as 3.0如何開啟層級呼叫uiautomatorviewer]

【10】AccessibilityService實現探探app的自動化喜歡和不喜歡+ [as 3.0如何開啟層級呼叫uiautomatorviewer]

目前版本3.1.8  僅供學習使用!!!!!切勿用作非法用途,否則後果自負!

直接寫這個東西 可能是有些唐突

因為你們可能不知道這是一個什麼玩意 如果你想看過於這個Accessibility服務的一些資料

可以到 我的分類 下面去看

下面將直接進入正題

AccessibilityService 的出現原本為了幫助盲人 來那啥那啥和那啥的

沒想到卻被我們那啥那啥和那啥了

他使用方法需要手動到無障礙裡面去開啟 開啟成功以後程式碼就開始運行了

整合的一般步驟就是

建立服務 繼承AccessibilityService實現相關方法

package com.example.liuan.test;

import android.accessibilityservice.AccessibilityService;
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;

import java.io.DataOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Random;


public class MyService extends AccessibilityService {
    private static final String TAG = "MyService";
    // 大多數的手機包名一樣,聯想部分機型的手機不一樣
    private String[] packageNames = {"com.p1.mobile.putong"};
    int startFlag = 0;

    AccessibilityNodeInfo[] noteInfo = new AccessibilityNodeInfo[2];



    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getSource() != null) {
            findNodesByText(event, "繼續探索");
        }

        switch (event.getEventType()) {
            case AccessibilityEvent.TYPE_VIEW_CLICKED:

                if (SpUtils.getBoolean(this, "reSet", false)) {
                    SpUtils.putBoolean(this, "reSet", false);
                    startFlag = 0;
                }

                if (startFlag == 0) {
                    //記錄正確
                    startFlag = 1;
                    noteInfo[0] = event.getSource();
                } else if (startFlag == 1) {
                    //記錄錯誤  只記錄一次即可
                    startFlag = 2;
                    noteInfo[1] = event.getSource();
                }

                if (startFlag == 2) {
                    autoClick();
                }


                break;


        }

    }

    private void autoClick() {
        try {
            String time = SpUtils.getString(this, "time", "1000");
            Thread.sleep(Integer.parseInt(time));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String time = SpUtils.getString(this, "like", "50");
        int percent = Integer.parseInt(time);
//隨機數字 是1-100
        int random = new Random().nextInt(100) + 1;
//如果設定了百分之50  那麼就是各一半
//如果設定了百分之10  那麼就是點第一個 8   10
        Log.e(TAG, "onAccessibilityEvent:random " + random);
        Log.e(TAG, "onAccessibilityEvent:percent " + percent);
        if (random <= percent) {
            //第一次按鍵
            noteInfo[0].performAction(AccessibilityNodeInfo.ACTION_CLICK);
        } else {
            noteInfo[1].performAction(AccessibilityNodeInfo.ACTION_CLICK);
        }

    }


    @Override
    public void onInterrupt() {
        startFlag = 0;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        startFlag = 0;
    }

    private void findNodesByText(AccessibilityEvent event, String text) {
        List<AccessibilityNodeInfo> nodes = event.getSource().findAccessibilityNodeInfosByText(text);

        if (nodes != null && !nodes.isEmpty()) {
            for (AccessibilityNodeInfo info : nodes) {
                if (info.isClickable()) {// 只有根據節點資訊是下一步,安裝,完成,開啟,且是可以點選的時候,才執行後面的點選操作
                    if ("繼續探索".equals(text)) {
                        info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    }
                }
            }
        }
    }

    private boolean isTextExist(AccessibilityEvent event, String text) {
        List<AccessibilityNodeInfo> nodes = event.getSource().findAccessibilityNodeInfosByText(text);

        if (nodes != null && !nodes.isEmpty()) {
            for (AccessibilityNodeInfo info : nodes) {
                if (info.isClickable()) {// 只有根據節點資訊是下一步,安裝,完成,開啟,且是可以點選的時候,才執行後面的點選操作
                    if ("繼續探索".equals(text)) {
                        return true;
                    }
                }
            }
        }
        return false;

    }

}

我的思路 我簡單來說一下 因為就算你層級用的沒問題 但是卻獲取不到id

我就急中生智 使用了event.getSource() 把這個儲存到陣列 讓自己點兩下 然後開始模仿我的點選

這個方法不僅僅使用與這裡 可以用作好多的地方 

AccessiblityServices 要比指令碼穩定 指令碼存在一種情況就是指令碼會崩潰 或者由於某個地方的偏移導致點選錯誤

這個從節點點選 徹底的解決了這個問題 無論開發者怎麼騷操作 這裡都可以點選 好氣的

獲取那個檔案的id 或者文字什麼的 用到了studio工具中的(本文程式碼風騷 沒有用到這個)

G:\android\sdk\tools\bin\uiautomatorviewer.bat

as 3.0居然喪盡天良把他搞沒了 反正我沒找到 可能是沒了

只能去sdk 目錄下去找了 如果你記憶力夠好的話找到你曾經安裝的sdk目錄

如果記憶力不夠好 可以在alt+ctrl+s System Settings-->Android SDK看到你的sdk目錄 找到開啟即可

具體使用方法這裡不做教程

四大元件逃不過的清單檔案

application節點下
<service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"
            android:label="探探點選"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService" />
            </intent-filter>

            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility" />
        </service>

res/xml/accessibility.xml 沒有這個目錄請自行建立

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/des"
    android:notificationTimeout="100"
    android:packageNames="com.p1.mobile.putong" />
<resources>
<string name="app_name">Test</string>
<string name="des">僅供學習使用,切勿亂搞,否則後果自負</string>
</resources>

MainActivity

package com.example.liuan.test;

import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private Button mBtOpen;
    private EditText mEtDelay;
    private EditText mEtLike;
    /**
     * 儲存配置
     */
    private Button mBtReset;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
//        String apkRoot="chmod 777 "+getPackageCodePath();
//
//        SystemManager.RootCommand(apkRoot);
        initView();
        initData();

    }

    private void initData() {
        SpUtils.putString(this, "time", mEtDelay.getText().toString().trim());
        SpUtils.putString(this, "like", mEtLike.getText().toString().trim());
        SpUtils.putBoolean(MainActivity.this, "reSet", true);
    }


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Toast.makeText(MainActivity.this, "半徑", Toast.LENGTH_LONG);
        switch (keyCode) {

            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
                Toast.makeText(MainActivity.this, "半徑", Toast.LENGTH_LONG);
                mBtOpen.setText("已經關閉");
                return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    private void initView() {
        mBtOpen = (Button) findViewById(R.id.bt_open);
        mBtOpen.setOnClickListener(this);

        mEtDelay = (EditText) findViewById(R.id.et_delay);
        mEtLike = (EditText) findViewById(R.id.et_like);
        mEtLike.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                initData();

            }
        });
        mEtDelay.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                initData();
            }
        });

        mBtReset = (Button) findViewById(R.id.bt_reset);
        mBtReset.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            default:
                break;
            case R.id.bt_open:
                initData();
                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                startActivity(intent);
                break;

            case R.id.bt_reset:
                initData();
                break;
        }
    }
}

SpUtils

package com.example.liuan.test;

import android.content.Context;
import android.content.SharedPreferences;

/**
 * Created by liuan on 2018-04-28.
 */

public class SpUtils {
    private static SharedPreferences sp;
    public  static void getSharedPreference(Context context){
        if(sp==null){
            sp=context.getSharedPreferences("config",context.MODE_PRIVATE);
        }
    }
    public static void putString(Context context,String key,String value){
        getSharedPreference(context);
        sp.edit().putString(key,value).commit();
    }
    public static String getString(Context context,String key,String defValue){
        getSharedPreference(context);
       return sp.getString(key,defValue);
    }
    public static boolean getBoolean(Context context,String key,boolean defValue){
        getSharedPreference(context);
        return sp.getBoolean(key,false);
    }

    public static void putBoolean(Context context,String key,boolean value){
        getSharedPreference(context);
        sp.edit().putBoolean(key,value).commit();
    }
}

佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/bt_open"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="開啟/關閉服務" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_reset"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="儲存配置" />


    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="60sp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="延時:s"
            android:textColor="@android:color/holo_red_dark" />

        <EditText
            android:id="@+id/et_delay"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:inputType="number"
            android:text="6000" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView

            android:layout_width="60sp"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="喜歡:x/%"
            android:textColor="@android:color/holo_red_dark" />

        <EditText
            android:id="@+id/et_like"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:inputType="number"
            android:text="94" />
    </LinearLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    android:layout_gravity="center"
        android:gravity="center"
        android:text="本軟體僅供學習使用~!!!!\n開啟服務 點選一個喜歡  再點選一個不喜歡 就開始了,如果點選喜歡 出現了繼續探索,要進主頁點重新設定 再點選 喜歡 和不喜歡~"
        android:textColor="@android:color/holo_red_dark"
        android:textSize="22sp" />
</LinearLayout>

開始方法 並不是直接點開啟就開啟了

第一步 開啟探探點選的服務

第二步 開啟探探先把其他的第一次引導的東西點完 然後 點一個喜歡 點一個不喜歡  再然後就能看到神奇的自動了

那天我本來想寫了按音量鍵暫停的方法  後來發現沒必要 暫停服務就行了