Skip to content

Instantly share code, notes, and snippets.

@spiritedRunning
Last active August 18, 2020 08:16
Show Gist options
  • Save spiritedRunning/11ffc7314c38515e309831b6c449fe4c to your computer and use it in GitHub Desktop.
Save spiritedRunning/11ffc7314c38515e309831b6c449fe4c to your computer and use it in GitHub Desktop.
Implement of floating window
public class FloatingWindow extends FrameLayout {
private static final String TAG = "FloatCallView";
private Context mContext;
private int mediaType;
public static int viewHeight;
public static int viewWidth;
// View中X坐标
private float xInView;
// View中Y坐标
private float yInView;
// 当前X坐标
private float xInScreen;
// 当前Y坐标
private float yInScreen;
// 记录移动前X坐标
private float mStartX;
// 记录移动前Y坐标
private float mStartY;
// 视频SurfaceView
private SurfaceView videoSurfaceView;
private FrameLayout videoContainerLayout;
private OnClickListener mOnClickListner;
private View mRootView;
// 时间状态显示
private TextView timeStateText;
private WindowManager wm;
private WindowManager.LayoutParams wmParams;
// 当前是否处于呼状态
private boolean isCalling;
public FloatCallView(Context context, int resId, int mediaType) {
super(context);
this.mContext = context;
this.mediaType = TangSDKInstance.getInstance().isVideoCallOpened() ? Constants.CALL_VIDEO_STATE : Constants.CALL_VOICE_STATE;
wm = (WindowManager) getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
LayoutInflater.from(mContext).inflate(resId, this);
initView();
}
private void initView() {
if (mediaType == Constants.CALL_VOICE_STATE) {
mRootView = findViewById(R.id.voice_call_layout);
timeStateText = (TextView) findViewById(R.id.call_state_prompt_text);
} else if (mediaType == Constants.CALL_VIDEO_STATE) {
mRootView = findViewById(R.id.video_call_layout);
// videoSurfaceView = (SurfaceView) findViewById(R.id.video_stream_surfaceview);
}
}
public SurfaceView getVideoSurface() {
if (videoSurfaceView != null) {
return videoSurfaceView;
}
return null;
}
public void setParams(WindowManager.LayoutParams params) {
wmParams = params;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 获取状态栏高度
Rect frame = new Rect();
getWindowVisibleDisplayFrame(frame);
int statusBar = frame.top;
Log.d(TAG, "statusBar: " + statusBar);
int screenWidth = wm.getDefaultDisplay().getWidth();
int screenHeight = wm.getDefaultDisplay().getHeight();
// 获取相对屏幕的坐标,即以屏幕左上角为原点
xInScreen = event.getRawX();
yInScreen = event.getRawY() - statusBar;
Log.i("onTouchEvent", "x: " + xInScreen + ", y: " + yInScreen);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取相对View的坐标,即以此View左上角为原点
xInView = event.getX();
yInView = event.getY();
mStartX = xInScreen;
mStartY = yInScreen;
Log.i("ACTION_DOWN", "xInView: " + xInView + ", mTouchStartY: " + yInView);
break;
case MotionEvent.ACTION_MOVE:
xInScreen = event.getRawX();
yInScreen = event.getRawY() - statusBar;
Log.i("ACTION_MOVE", "xInScreen: " + xInScreen + ", yInScreen: " + yInScreen + ", xInView: " + xInView + ", yInView: " + yInView);
updateViewPosition();
break;
case MotionEvent.ACTION_UP:
if (xInScreen == mStartX && yInScreen == mStartY) {
if (mOnClickListner != null) {
mOnClickListner.onClick(this);
Log.i(TAG, "click floating window");
}
}
if (xInScreen < screenWidth / 2) {
xInScreen = 0;
} else {
xInScreen = screenWidth;
}
updateViewPosition();
break;
}
return true;
}
@Override
public void setOnClickListener(OnClickListener l) {
this.mOnClickListner = l;
}
private void updateViewPosition() {
wmParams.x = (int) (xInScreen - xInView);
wmParams.y = (int) (yInScreen - yInView);
wm.updateViewLayout(this, wmParams);
}
}
/**
* @brief 创建悬浮层用于显示通话状态,视频图像
*/
public void createFloatingWindow() {
floatCallView = new FloatCallView(this);
floatCallView.setOnClickListener(this);
int screenWidth = wm.getDefaultDisplay().getWidth();
int screenHeight = wm.getDefaultDisplay().getHeight();
wmParams = ((MyApplication) getApplication()).getLayoutParams();
// 设置悬浮窗类型
if (DeviceUtil.isSpecialDevice(DeviceUtil.DEVICE_XIAOMI) ||
DeviceUtil.isSpecialDevice(DeviceUtil.DEVICE_MEIZU)) {
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
}
// 设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
// 悬浮窗的对齐方式. 便于调整坐标
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角为原点, 设置悬浮层初始位置
wmParams.x = screenWidth;
wmParams.y = screenHeight / 2;
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
floatCallView.setParams(wmParams);
wm.addView(floatCallView, wmParams);
}
private void removeFloatView() {
if (floatCallView != null) {
wm.removeView(floatCallView);
floatCallView = null;
}
}
/**
* @brief 判断悬浮窗权限是否打开
* @return true 权限已打开 false 无权限
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static boolean invokeFloatOpAllowed(Context context) {
try {
if (getSDKVersion() >= 19) {
Object object = context.getSystemService(Context.APP_OPS_SERVICE);
if (object == null) {
return false;
}
Class localClass = object.getClass();
Class[] arrayOfClass = new Class[3];
arrayOfClass[0] = Integer.TYPE;
arrayOfClass[1] = Integer.TYPE;
arrayOfClass[2] = String.class;
Method method = localClass.getMethod("checkOp", arrayOfClass);
if (method == null) {
return false;
}
Object[] arrayOfObject1 = new Object[3];
// AppOpsManager.OP_SYSTEM_ALERT_WINDOW = 24
arrayOfObject1[0] = Integer.valueOf(24);
arrayOfObject1[1] = Integer.valueOf(Binder.getCallingUid());
arrayOfObject1[2] = context.getPackageName();
int m = ((Integer) method.invoke(object, arrayOfObject1)).intValue();
boolean isAllowed = (m == AppOpsManager.MODE_ALLOWED || m == AppOpsManager.MODE_DEFAULT);
LogUtil.d(TAG, "isAllowed: " + m);
return isAllowed;
}
} catch (Throwable ex) {
LogUtil.e(TAG, "getFloatOpAllowed error: " + ex);
}
return false;
}
@spiritedRunning
Copy link
Author

Add permission

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment