障碍辅助功能AccessibilityService
提到这个障碍功能,可能大家想想到的是前几年很火的自动抢红包功能,基本都是在亮屏时候模拟用户点击事件,市面上也出现不少的红包插件,但是息屏的抢红包软件还是比较少的,前段时间已经完成了一个,但是还没做兼容适配,存在不少bug,目前在我的7.0的华为mate8手机上是完美的。后面开一篇博客专门讲。大致就是用服务实现的。
查看当前应用的Activity
很多同学刚去新公司看别人的代码头疼,又不知道代码写在哪里,时间浪费了,老是去问又不好,我刚来现在的公司时候,就存在这个现象,现在没有那么忙了,就做点贡献。不废话
思路
大致思路,借助AccessibilityService这个服务,相当于是打了一个擦边球,获取当前event的然后通过发消息去刷新一个浮窗
具体实现
1、首先在MainActivity 中 检查权限:
private void checkOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
startActivityForResult(
new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
REQUEST_CODE
);
Toast.makeText(this, "请先授予 \"栈顶通\" 悬浮窗权限", Toast.LENGTH_LONG).show();
}
}
}
2.在点击开启的界面处理点击事件:
public static boolean checkAccessibility(Context context) {
// 判断辅助功能是否开启
if (!AccessibilityUtil.isAccessibilitySettingsOn(context)) {
// 引导至辅助功能设置页面
context.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
);
Toast.makeText(context, "请先开启 \"Activity 栈\" 的辅助功能", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
3.自定义一个弹窗布局
public class FloatingView extends LinearLayout {
private final Context mContext;
private final WindowManager mWindowManager;
private TextView mTvPackageName;
private TextView mTvClassName;
private ImageView mIvClose;
public FloatingView(Context context) {
this(context,null);
}
public FloatingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mContext = context;
mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater.from(context).inflate(R.layout.floating_layout,this);
initView();
}
private void initView() {
mTvPackageName = (TextView) findViewById(R.id.tv_package_name);
mTvClassName = (TextView) findViewById(R.id.tv_class_name);
mIvClose = (ImageView) findViewById(R.id.iv_close);
mIvClose.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "关闭悬浮框", Toast.LENGTH_SHORT).show();
mContext.startService(
new Intent(mContext, TrackerService.class)
.putExtra(TrackerService.COMMAND, TrackerService.COMMAND_CLOSE)
);
}
});
}
Point preP, curP;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
preP = new Point((int)event.getRawX(), (int)event.getRawY());
break;
case MotionEvent.ACTION_MOVE:
curP = new Point((int)event.getRawX(), (int)event.getRawY());
int dx = curP.x - preP.x,
dy = curP.y - preP.y;
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) this.getLayoutParams();
layoutParams.x += dx;
layoutParams.y += dy;
mWindowManager.updateViewLayout(this, layoutParams);
preP = curP;
break;
}
return false;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(TrackerService.ActivityChangedEvent event){
Log.d(TAG, "event:" + event.getPackageName() + ": " + event.getClassName());
String packageName = event.getPackageName(),
className = event.getClassName();
mTvPackageName.setText(packageName);
mTvClassName.setText(
className.startsWith(packageName)?
className.substring(packageName.length()):
className
);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
Log.d(TAG, "event: 服务关闭" );
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Log.d(TAG, "event: 服务关闭" );
EventBus.getDefault().unregister(this);
}
}
4.发送事件
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
Log.d(TAG, "event:" + event.getPackageName() + ": " + event.getClassName());
if(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event.getEventType()){
// if(AccessibilityEvent.TYPE_WINDOWS_CHANGED == event.getEventType()){ //这个坑B
Log.d(TAG, "窗口改变发送方了一次请求" + event.getPackageName() + ": " + event.getClassName());
EventBus.getDefault().post(new ActivityChangedEvent(event.getPackageName().toString(),event.getClassName().toString()));
}
}
这里有个比较坑的地方小心了
5.接受事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(TrackerService.ActivityChangedEvent event){
Log.d(TAG, "event:" + event.getPackageName() + ": " + event.getClassName());
String packageName = event.getPackageName(),
className = event.getClassName();
mTvPackageName.setText(packageName);
mTvClassName.setText(
className.startsWith(packageName)?
className.substring(packageName.length()):
className
);
}
总结
在魅族小米手机上悬浮窗要在设置里面去开