Android監聽使用者行為操作(AccessibilityService)
前言
今天我們將使用AccessibilityService實現:
- 監聽第三方程式的介面變化(監聽第三方程式的啟動的實現原理)。
- 模擬點選第三方應用的按鈕(自動搶紅包程式的實現原理)。
- 監聽第三方程式的點選事件。
如果要測試的第三方應用不為自己的,則需要獲取第三方應用的包名,當前Acvtivity等資訊。可以參考:
Android獲取第三方程式的包名
模擬程式
我們先寫一個模擬程式,該模擬程式只有一個按鈕用於模擬點選事件。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_click).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Toast.makeText(MainActivity.this, "我被點選了!!!", Toast.LENGTH_SHORT).show();
Log.i(TAG, "onClick: 我被點選了!!!");
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/btn_click"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="模擬點選" />
</LinearLayout>
監聽程式
AccessibilityService
程式碼具體的詳情請看註釋。
public class ListeningService extends AccessibilityService {
private static final String TAG = "WindowChange";
@Override
protected void onServiceConnected() {
super.onServiceConnected();
AccessibilityServiceInfo config = new AccessibilityServiceInfo();
//配置監聽的事件型別為介面變化|點選事件
config.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_CLICKED;
config.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
if (Build.VERSION.SDK_INT >= 16) {
config.flags = AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
}
setServiceInfo(config);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
AccessibilityNodeInfo nodeInfo = event.getSource();//當前介面的可訪問節點資訊
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {//介面變化事件
ComponentName componentName = new ComponentName(event.getPackageName().toString(), event.getClassName().toString());
ActivityInfo activityInfo = tryGetActivity(componentName);
boolean isActivity = activityInfo != null;
if (isActivity) {
Log.i(TAG, componentName.flattenToShortString());
//格式為:(包名/.+當前Activity所在包的類名)
//如果是模擬程式的操作介面
if (componentName.flattenToShortString().equals("com.demon.simulationclick/.MainActivity")) {
//當前是模擬程式的主頁面,則模擬點選按鈕
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
//通過id尋找控制元件,id格式為:(包名:id/+制定控制元件的id)
//一般除非第三方應該是自己的,否則我們很難通過這種方式找到控制元件
//List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("com.demon.simulationclick:id/btn_click");
//通過控制元件的text尋找控制元件
List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByText("模擬點選");
if (list != null && list.size() > 0) {
list.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}
}
}
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {//View點選事件
//Log.i(TAG, "onAccessibilityEvent: " + nodeInfo.getText());
if ((nodeInfo.getText() + "").equals("模擬點選")) {
//Toast.makeText(this, "這是來自監聽Service的響應!", Toast.LENGTH_SHORT).show();
Log.i(TAG, "onAccessibilityEvent: 這是來自監聽Service的響應!");
}
}
}
private ActivityInfo tryGetActivity(ComponentName componentName) {
try {
return getPackageManager().getActivityInfo(componentName, 0);
} catch (PackageManager.NameNotFoundException e) {
return null;
}
}
@Override
public void onInterrupt() {
}
}
配置Service
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
···
<service
android:name=".ListeningService"
android:label="@string/app_name"
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/accessibilityservice" />
</service>
···
accessibilityservice.xml
方法 | 說明 |
---|---|
android:accessibilityEventTypes | 設定響應事件的型別typeAllMask,typeViewClicked,typeViewFocused,typeNotificationStateChanged,typeWindowStateChanged等 |
android:accessibilityFeedbackType | 設定反饋給使用者的方式 |
android:canRetrieveWindowContent | 是否檢索當前視窗的內容,即是否可以獲取當前視窗的View |
android:description | 服務申請的許可權描述說明 |
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_service_description"
tools:ignore="UnusedAttribute" />
MainActivity
使用AccessibilityService服務需要申請輔助功能(小米手機叫:無障礙)的服務支援,無法主動給與,需要到指定介面使用者手動開啟。
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Intent intent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!isAccessibilitySettingsOn(MainActivity.this, ListeningService.class.getCanonicalName())) {
Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
startActivity(intent);
} else {
intent = new Intent(MainActivity.this, ListeningService.class);
startService(intent);
}
}
});
findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (intent != null) {
stopService(intent);
}
}
});
}
/**
* 檢測輔助功能是否開啟
*
* @param mContext
* @return boolean
*/
private boolean isAccessibilitySettingsOn(Context mContext, String serviceName) {
int accessibilityEnabled = 0;
// 對應的服務
final String service = getPackageName() + "/" + serviceName;
//Log.i(TAG, "service:" + service);
try {
accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),
android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled);
} catch (Settings.SettingNotFoundException e) {
Log.e(TAG, "Error finding setting, default accessibility to not found: " + e.getMessage());
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
if (accessibilityEnabled == 1) {
Log.v(TAG, "***ACCESSIBILITY IS ENABLED*** -----------------");
String settingValue = Settings.Secure.getString(mContext.getApplicationContext().getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
Log.v(TAG, "-------------- > accessibilityService :: " + accessibilityService + " " + service);
if (accessibilityService.equalsIgnoreCase(service)) {
Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
return true;
}
}
}
} else {
Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
}
return false;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開啟監聽服務" />
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="關閉監聽服務" />
</LinearLayout>
效果
執行監聽程式,給與服務支援後,開啟監聽程式。
然後執行模擬程式,效果如下。
監聽到com.demon.simulationclick/.MainActivity——-模擬點選按鈕—–監聽到按鈕被點選——從監聽Service發出響應
程式碼
https://github.com/Demo-DeMon/ActivtyChange
參考
相關文章
- Android截圖監聽Android
- 2018.03.31、Android-ObjectBox-監聽AndroidObject
- Android AccessibilityService機制原始碼解析Android原始碼
- vue監聽input是否為空(監聽值為物件某個屬性)Vue物件
- JS 監聽使用者頁面訪問&頁面關閉並進行資料上報操作JS
- Android Home鍵、鎖屏鍵監聽Android
- Android 監聽生命週期工具庫Android
- android PopupWindow監聽返回鍵無效Android
- Android掃碼槍監聽封裝Android封裝
- 人員操作行為識別監測
- 獲取執行埠監聽的使用者身份auth-owner
- Android的事件處理——監聽介面方式Android事件
- Android studio(建立、監聽器intent選單)AndroidIntent
- 使用者行為分析,指定操作順序
- 監聽 watch props物件屬性監聽 或深度監聽物件
- Android搖一搖、螢幕方向的監聽Android
- Android監聽軟鍵盤收起與彈出Android
- iPhone曝嚴重漏洞,使用者接聽FaceTime前或被“監聽”!iPhone
- H5頁面監聽Android物理返回鍵H5Android
- Android開屏、鎖屏、解鎖監聽實現Android
- AccessibilityService防禦
- SpringBoot 中釋出ApplicationEventPublisher,監聽ApplicationEvent 非同步操作Spring BootAPP非同步
- 在Linux中,如何進行使用者行為監控?Linux
- Android多程式之Binder解綁監聽的問題Android
- watch監聽
- 使用Swoole的Websocket監聽使用者連線狀態Web
- Androidx為Fragment中的按鈕設定監聽AndroidFragment
- 監聽滑鼠事件事件
- jQuery事件監聽jQuery事件
- 時間監聽
- Flutter事件監聽Flutter事件
- 7、listener監聽
- springboot事件監聽Spring Boot事件
- js 監聽事件JS事件
- JavaScript 事件監聽JavaScript事件
- jfinal中如何使用過濾器監控Druid監聽SQL執行?過濾器UISQL
- Android開發之監聽軟鍵盤狀態(彈出收回)Android
- (課程學習)Android必學-非同步載入 —— 監聽 ListViewAndroid非同步View