突破小米悬浮窗权限控制–不需要权限的悬浮窗
在上一篇文章讲了Android的Toast拓展,在原生Toast基础上对显示时长和显示动画做了二次封装,强化了Toast的部分功能。也分析了对于二次封装的ExToast设计原理,以及Toast的关键点。如果不了解的可以看看下面的链接。
常用悬浮窗与Toast
之前分析过,Toast其实就是系统悬浮窗的一种,那它跟常用的系统悬浮窗有什么区别呢?
先看一下常用的Andoird系统悬浮窗写法:
// 获取应用的Context
mContext = context.getApplicationContext();
// 获取WindowManager
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mView = setUpView(context);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
// 类型
params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
params.flags = flags;
params.format = PixelFormat.TRANSLUCENT;
params.width = LayoutParams.MATCH_PARENT;
params.height = LayoutParams.MATCH_PARENT;
params.gravity = Gravity.CENTER;
mWindowManager.addView(mView, params);
再看看在Toast源码里面的写法关键代码:
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
// 类型
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
...
// 获取应用的context
Context context = mView.getContext().getApplicationContext();
// 获取WindowManager
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
...
if (mView.getParent() != null) {
mWM.removeView(mView);
}
mWM.addView(mView, mParams);
上面的两段代码大致流程都是一样的:创建WindowManager.LayoutParams做窗口的配置->通过context获取WindowManager服务->通过WindowManager服务添加悬浮窗View
主要的不同点在于WindowManager.LayoutParams的type。
WindowManager.LayoutParams的type有很多种,包括各种系统对话框,锁屏窗口,电话窗口等等,但这些窗口基本上都是需要权限的。
而我们平时使用的Toast,并不需要权限就能显示,那就可以尝试直接把悬浮窗的类型设成TYPE_TOAST,来定制一个不需要权限的悬浮窗。
下面是demo代码:
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class ADToast implements View.OnTouchListener {
Context mContext;
WindowManager.LayoutParams params;
WindowManager mWM;
View mView;
private float mTouchStartX;
private float mTouchStartY;
private float x;
private float y;
public ADToast(Context context){
this.mContext = context;
params = new WindowManager.LayoutParams();
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = R.style.anim_view;
// 悬浮窗类型,整个demo的关键点
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.gravity = Gravity.LEFT | Gravity.TOP;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater inflate = (LayoutInflater)
mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = inflate.inflate(R.layout.float_tips_layout, null);
mView.setOnTouchListener(this);
}
public void show(){
TextView tv = (TextView)mView.findViewById(R.id.message);
tv.setText("悬浮窗");
if (mView.getParent() != null) {