今天我们将使用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: 我被点击了!!!");
}
});
}
}
<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>
代码具体的详情请看注释。
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 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() {
}
}
<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>
···
方法 | 说明 |
---|---|
android:accessibilityEventTypes | 设置响应事件的类型typeAllMask,typeViewClicked,typeViewFocused,typeNotificationStateChanged,typeWindowStateChanged等 |
android:accessibilityFeedbackType | 设置反馈给用户的方式 |
android:canRetrieveWindowContent | 是否检索当前窗口的内容,即是否可以获取当前窗口的View |
android:description | 服务申请的权限描述说明 |
<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" />
使用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;
}
}
<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/MyAndroidDemo/ActivtyChange
https://blog.csdn.net/c794904140/article/details/52153148