parent
36656ea085
commit
083c306d2b
@ -0,0 +1,81 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.net.Uri; |
||||||
|
import android.os.Build; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.support.annotation.RequiresApi; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于在内部自动申请权限 |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
public class FloatActivity extends Activity { |
||||||
|
|
||||||
|
private static List<PermissionListener> mPermissionListenerList; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
if (Build.VERSION.SDK_INT >= 23){ |
||||||
|
requestAlertWindowPermission(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresApi(api = 23) |
||||||
|
private void requestAlertWindowPermission() { |
||||||
|
Intent intent = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION"); |
||||||
|
intent.setData(Uri.parse("package:" + getPackageName())); |
||||||
|
startActivityForResult(intent, 123); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@RequiresApi(api = 23) |
||||||
|
@Override |
||||||
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
||||||
|
super.onActivityResult(requestCode, resultCode, data); |
||||||
|
if (Build.VERSION.SDK_INT >= 23){ |
||||||
|
//todo 用23以上编译即可出现canDrawOverlays
|
||||||
|
if (Util.hasPermission(this)) { |
||||||
|
mPermissionListener.onSuccess(); |
||||||
|
} else { |
||||||
|
mPermissionListener.onFail(); |
||||||
|
} |
||||||
|
} |
||||||
|
finish(); |
||||||
|
} |
||||||
|
|
||||||
|
static synchronized void request(Context context, PermissionListener permissionListener) { |
||||||
|
if (mPermissionListenerList == null) { |
||||||
|
mPermissionListenerList = new ArrayList<>(); |
||||||
|
mPermissionListener = new PermissionListener() { |
||||||
|
@Override |
||||||
|
public void onSuccess() { |
||||||
|
for (PermissionListener listener : mPermissionListenerList) { |
||||||
|
listener.onSuccess(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFail() { |
||||||
|
for (PermissionListener listener : mPermissionListenerList) { |
||||||
|
listener.onFail(); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
Intent intent = new Intent(context, FloatActivity.class); |
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); |
||||||
|
context.startActivity(intent); |
||||||
|
} |
||||||
|
mPermissionListenerList.add(permissionListener); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private static PermissionListener mPermissionListener; |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.app.Application; |
||||||
|
import android.content.BroadcastReceiver; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.IntentFilter; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.Handler; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 17-12-1. |
||||||
|
* 用于控制悬浮窗显示周期 |
||||||
|
* 使用了三种方法针对返回桌面时隐藏悬浮按钮 |
||||||
|
* 1.startCount计数,针对back到桌面可以及时隐藏 |
||||||
|
* 2.监听home键,从而及时隐藏 |
||||||
|
* 3.resumeCount计时,针对一些只执行onPause不执行onStop的奇葩情况 |
||||||
|
*/ |
||||||
|
|
||||||
|
class FloatLifecycle extends BroadcastReceiver implements Application.ActivityLifecycleCallbacks { |
||||||
|
|
||||||
|
private static final long delay = 300; |
||||||
|
private Handler mHandler; |
||||||
|
private Class[] activities; |
||||||
|
private boolean showFlag; |
||||||
|
private int resumeCount; |
||||||
|
private boolean appBackground; |
||||||
|
private LifecycleListener mLifecycleListener; |
||||||
|
|
||||||
|
|
||||||
|
FloatLifecycle(Context applicationContext, boolean showFlag, Class[] activities, LifecycleListener lifecycleListener) { |
||||||
|
this.showFlag = showFlag; |
||||||
|
this.activities = activities; |
||||||
|
mLifecycleListener = lifecycleListener; |
||||||
|
mHandler = new Handler(); |
||||||
|
((Application) applicationContext).registerActivityLifecycleCallbacks(this); |
||||||
|
applicationContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private boolean needShow(Activity activity) { |
||||||
|
if (activities == null) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
for (Class a : activities) { |
||||||
|
if (a.isInstance(activity)) { |
||||||
|
return showFlag; |
||||||
|
} |
||||||
|
} |
||||||
|
return !showFlag; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityResumed(Activity activity) { |
||||||
|
resumeCount++; |
||||||
|
if (needShow(activity)) { |
||||||
|
mLifecycleListener.onShow(); |
||||||
|
} |
||||||
|
|
||||||
|
if (appBackground) { |
||||||
|
appBackground = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityPaused(Activity activity) { |
||||||
|
resumeCount--; |
||||||
|
mHandler.postDelayed(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (resumeCount == 0) { |
||||||
|
appBackground = true; |
||||||
|
//mLifecycleListener.onPostHide();
|
||||||
|
} |
||||||
|
} |
||||||
|
}, delay); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityStarted(Activity activity) { |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityStopped(Activity activity) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onReceive(Context context, Intent intent) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivitySaveInstanceState(Activity activity, Bundle outState) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onActivityDestroyed(Activity activity) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,111 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.PixelFormat; |
||||||
|
import android.os.Build; |
||||||
|
import android.view.View; |
||||||
|
import android.view.WindowManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 17-11-14. |
||||||
|
* 7.1及以上需申请权限 |
||||||
|
*/ |
||||||
|
|
||||||
|
class FloatPhone extends FloatView { |
||||||
|
|
||||||
|
private final Context mContext; |
||||||
|
|
||||||
|
private final WindowManager mWindowManager; |
||||||
|
private final WindowManager.LayoutParams mLayoutParams; |
||||||
|
private View mView; |
||||||
|
private int mX, mY; |
||||||
|
|
||||||
|
FloatPhone(Context applicationContext) { |
||||||
|
mContext = applicationContext; |
||||||
|
mWindowManager = (WindowManager) applicationContext.getSystemService(Context.WINDOW_SERVICE); |
||||||
|
mLayoutParams = new WindowManager.LayoutParams(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSize(int width, int height) { |
||||||
|
mLayoutParams.width = width; |
||||||
|
mLayoutParams.height = height; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setView(View view) { |
||||||
|
int layout_type; |
||||||
|
layout_type = WindowManager.LayoutParams.TYPE_PHONE; |
||||||
|
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
||||||
|
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
||||||
|
mLayoutParams.type = layout_type; |
||||||
|
mLayoutParams.windowAnimations = 0; |
||||||
|
mView = view; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setGravity(int gravity, int xOffset, int yOffset) { |
||||||
|
mLayoutParams.gravity = gravity; |
||||||
|
mLayoutParams.x = mX = xOffset; |
||||||
|
mLayoutParams.y = mY = yOffset; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void init() { |
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { |
||||||
|
if (Util.hasPermission(mContext)) { |
||||||
|
mLayoutParams.format = PixelFormat.RGBA_8888; |
||||||
|
mWindowManager.addView(mView, mLayoutParams); |
||||||
|
} else { |
||||||
|
FloatActivity.request(mContext, new PermissionListener() { |
||||||
|
@Override |
||||||
|
public void onSuccess() { |
||||||
|
mLayoutParams.format = PixelFormat.RGBA_8888; |
||||||
|
mWindowManager.addView(mView, mLayoutParams); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFail() { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dismiss() { |
||||||
|
mWindowManager.removeView(mView); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateXY(int x, int y) { |
||||||
|
mLayoutParams.x = mX = x; |
||||||
|
mLayoutParams.y = mY = y; |
||||||
|
mWindowManager.updateViewLayout(mView, mLayoutParams); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
void updateX(int x) { |
||||||
|
mLayoutParams.x = mX = x; |
||||||
|
mWindowManager.updateViewLayout(mView, mLayoutParams); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
void updateY(int y) { |
||||||
|
mLayoutParams.y = mY = y; |
||||||
|
mWindowManager.updateViewLayout(mView, mLayoutParams); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
int getX() { |
||||||
|
return mX; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
int getY() { |
||||||
|
return mY; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.Gravity; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.widget.FrameLayout; |
||||||
|
import android.widget.VideoView; |
||||||
|
|
||||||
|
|
||||||
|
public class FloatPlayerView extends FrameLayout { |
||||||
|
|
||||||
|
private final static String VIDEO_PATH = "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"; |
||||||
|
// private final static String VIDEO_PATH = "storage/emulated/0/beyond.mp4";
|
||||||
|
|
||||||
|
private VideoView mPlayer; |
||||||
|
|
||||||
|
public FloatPlayerView(Context context) { |
||||||
|
super(context); |
||||||
|
init(context); |
||||||
|
} |
||||||
|
|
||||||
|
public FloatPlayerView(Context context, AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
init(context); |
||||||
|
} |
||||||
|
|
||||||
|
public FloatPlayerView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||||
|
super(context, attrs, defStyleAttr); |
||||||
|
init(context); |
||||||
|
} |
||||||
|
|
||||||
|
private void init(Context context) { |
||||||
|
mPlayer = new VideoView(context); |
||||||
|
|
||||||
|
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); |
||||||
|
layoutParams.gravity = Gravity.CENTER; |
||||||
|
addView(mPlayer, layoutParams); |
||||||
|
|
||||||
|
mPlayer.setVideoPath(VIDEO_PATH); |
||||||
|
mPlayer.requestFocus(); |
||||||
|
mPlayer.start(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void onPause() { |
||||||
|
if (mPlayer != null){ |
||||||
|
mPlayer.pause(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void onResume() { |
||||||
|
if (mPlayer != null){ |
||||||
|
mPlayer.resume(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void onDestroy(){ |
||||||
|
if (mPlayer != null){ |
||||||
|
mPlayer.stopPlayback(); |
||||||
|
mPlayer = null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.view.View; |
||||||
|
import android.view.WindowManager; |
||||||
|
import android.widget.Toast; |
||||||
|
|
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义 toast 方式,无需申请权限 |
||||||
|
*/ |
||||||
|
|
||||||
|
class FloatToast extends FloatView { |
||||||
|
|
||||||
|
|
||||||
|
private Toast toast; |
||||||
|
|
||||||
|
private Object mTN; |
||||||
|
private Method show; |
||||||
|
private Method hide; |
||||||
|
|
||||||
|
private int mWidth; |
||||||
|
private int mHeight; |
||||||
|
|
||||||
|
|
||||||
|
FloatToast(Context applicationContext) { |
||||||
|
toast = new Toast(applicationContext); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void setSize(int width, int height) { |
||||||
|
mWidth = width; |
||||||
|
mHeight = height; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setView(View view) { |
||||||
|
toast.setView(view); |
||||||
|
initTN(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setGravity(int gravity, int xOffset, int yOffset) { |
||||||
|
toast.setGravity(gravity, xOffset, yOffset); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void init() { |
||||||
|
try { |
||||||
|
show.invoke(mTN); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dismiss() { |
||||||
|
try { |
||||||
|
hide.invoke(mTN); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void initTN() { |
||||||
|
try { |
||||||
|
//todo android P test
|
||||||
|
Field tnField = toast.getClass().getDeclaredField("mTN"); |
||||||
|
tnField.setAccessible(true); |
||||||
|
mTN = tnField.get(toast); |
||||||
|
show = mTN.getClass().getMethod("show"); |
||||||
|
hide = mTN.getClass().getMethod("hide"); |
||||||
|
|
||||||
|
Field tnParamsField = mTN.getClass().getDeclaredField("mParams"); |
||||||
|
tnParamsField.setAccessible(true); |
||||||
|
WindowManager.LayoutParams params = (WindowManager.LayoutParams) tnParamsField.get(mTN); |
||||||
|
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
||||||
|
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; |
||||||
|
params.width = mWidth; |
||||||
|
params.height = mHeight; |
||||||
|
params.windowAnimations = 0; |
||||||
|
Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView"); |
||||||
|
tnNextViewField.setAccessible(true); |
||||||
|
tnNextViewField.set(mTN, toast.getView()); |
||||||
|
|
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 17-11-14. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
abstract class FloatView { |
||||||
|
|
||||||
|
abstract void setSize(int width, int height); |
||||||
|
|
||||||
|
abstract void setView(View view); |
||||||
|
|
||||||
|
abstract void setGravity(int gravity, int xOffset, int yOffset); |
||||||
|
|
||||||
|
abstract void init(); |
||||||
|
|
||||||
|
abstract void dismiss(); |
||||||
|
|
||||||
|
void updateXY(int x, int y) { |
||||||
|
} |
||||||
|
|
||||||
|
void updateX(int x) { |
||||||
|
} |
||||||
|
|
||||||
|
void updateY(int y) { |
||||||
|
} |
||||||
|
|
||||||
|
int getX() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int getY() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/12/22. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
public abstract class IFloatWindow { |
||||||
|
public abstract void show(); |
||||||
|
|
||||||
|
public abstract void hide(); |
||||||
|
|
||||||
|
public abstract int getX(); |
||||||
|
|
||||||
|
public abstract int getY(); |
||||||
|
|
||||||
|
public abstract void updateX(int x); |
||||||
|
|
||||||
|
public abstract void updateX(@Screen.screenType int screenType,float ratio); |
||||||
|
|
||||||
|
public abstract void updateY(int y); |
||||||
|
|
||||||
|
public abstract void updateY(@Screen.screenType int screenType,float ratio); |
||||||
|
|
||||||
|
public abstract View getView(); |
||||||
|
|
||||||
|
abstract void dismiss(); |
||||||
|
} |
@ -0,0 +1,248 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.animation.Animator; |
||||||
|
import android.animation.AnimatorListenerAdapter; |
||||||
|
import android.animation.ObjectAnimator; |
||||||
|
import android.animation.PropertyValuesHolder; |
||||||
|
import android.animation.TimeInterpolator; |
||||||
|
import android.animation.ValueAnimator; |
||||||
|
import android.os.Build; |
||||||
|
import android.view.MotionEvent; |
||||||
|
import android.view.View; |
||||||
|
import android.view.animation.DecelerateInterpolator; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/12/22. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
public class IFloatWindowImpl extends IFloatWindow { |
||||||
|
|
||||||
|
|
||||||
|
private FloatWindow.WindowBuilder mB; |
||||||
|
private FloatView mFloatView; |
||||||
|
private boolean isShow; |
||||||
|
private boolean once = true; |
||||||
|
private ValueAnimator mAnimator; |
||||||
|
private TimeInterpolator mDecelerateInterpolator; |
||||||
|
|
||||||
|
IFloatWindowImpl(FloatWindow.WindowBuilder b) { |
||||||
|
mB = b; |
||||||
|
if (mB.mMoveType == MoveType.fixed) { |
||||||
|
if (Build.VERSION.SDK_INT >=25) { |
||||||
|
mFloatView = new FloatPhone(b.mApplicationContext); |
||||||
|
} else { |
||||||
|
mFloatView = new FloatToast(b.mApplicationContext); |
||||||
|
} |
||||||
|
} else { |
||||||
|
mFloatView = new FloatPhone(b.mApplicationContext); |
||||||
|
initTouchEvent(); |
||||||
|
} |
||||||
|
mFloatView.setSize(mB.mWidth, mB.mHeight); |
||||||
|
mFloatView.setGravity(mB.gravity, mB.xOffset, mB.yOffset); |
||||||
|
mFloatView.setView(mB.mView); |
||||||
|
new FloatLifecycle(mB.mApplicationContext, mB.mShow, mB.mActivities, new LifecycleListener() { |
||||||
|
@Override |
||||||
|
public void onShow() { |
||||||
|
show(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onHide() { |
||||||
|
hide(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPostHide() { |
||||||
|
postHide(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void show() { |
||||||
|
if (once) { |
||||||
|
mFloatView.init(); |
||||||
|
once = false; |
||||||
|
isShow = true; |
||||||
|
} else { |
||||||
|
if (isShow) return; |
||||||
|
getView().setVisibility(View.VISIBLE); |
||||||
|
isShow = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void hide() { |
||||||
|
if (once || !isShow) return; |
||||||
|
getView().setVisibility(View.INVISIBLE); |
||||||
|
isShow = false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
void dismiss() { |
||||||
|
mFloatView.dismiss(); |
||||||
|
isShow = false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateX(int x) { |
||||||
|
checkMoveType(); |
||||||
|
mB.xOffset = x; |
||||||
|
mFloatView.updateX(x); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateY(int y) { |
||||||
|
checkMoveType(); |
||||||
|
mB.yOffset = y; |
||||||
|
mFloatView.updateY(y); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateX(int screenType, float ratio) { |
||||||
|
checkMoveType(); |
||||||
|
mB.xOffset = (int) ((screenType == Screen.width ? |
||||||
|
Util.getScreenWidth(mB.mApplicationContext) : |
||||||
|
Util.getScreenHeight(mB.mApplicationContext)) * ratio); |
||||||
|
mFloatView.updateX(mB.xOffset); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateY(int screenType, float ratio) { |
||||||
|
checkMoveType(); |
||||||
|
mB.yOffset = (int) ((screenType == Screen.width ? |
||||||
|
Util.getScreenWidth(mB.mApplicationContext) : |
||||||
|
Util.getScreenHeight(mB.mApplicationContext)) * ratio); |
||||||
|
mFloatView.updateY(mB.yOffset); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getX() { |
||||||
|
return mFloatView.getX(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getY() { |
||||||
|
return mFloatView.getY(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public View getView() { |
||||||
|
return mB.mView; |
||||||
|
} |
||||||
|
|
||||||
|
void postHide() { |
||||||
|
if (once || !isShow) return; |
||||||
|
getView().post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
getView().setVisibility(View.INVISIBLE); |
||||||
|
} |
||||||
|
}); |
||||||
|
isShow = false; |
||||||
|
} |
||||||
|
|
||||||
|
private void checkMoveType() { |
||||||
|
if (mB.mMoveType == MoveType.fixed) { |
||||||
|
throw new IllegalArgumentException("FloatWindow of this tag is not allowed to move!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void initTouchEvent() { |
||||||
|
switch (mB.mMoveType) { |
||||||
|
case MoveType.free: |
||||||
|
break; |
||||||
|
default: |
||||||
|
getView().setOnTouchListener(new View.OnTouchListener() { |
||||||
|
float lastX, lastY, changeX, changeY; |
||||||
|
int newX, newY; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean onTouch(View v, MotionEvent event) { |
||||||
|
|
||||||
|
switch (event.getAction()) { |
||||||
|
case MotionEvent.ACTION_DOWN: |
||||||
|
lastX = event.getRawX(); |
||||||
|
lastY = event.getRawY(); |
||||||
|
cancelAnimator(); |
||||||
|
break; |
||||||
|
case MotionEvent.ACTION_MOVE: |
||||||
|
changeX = event.getRawX() - lastX; |
||||||
|
changeY = event.getRawY() - lastY; |
||||||
|
newX = (int) (mFloatView.getX() + changeX); |
||||||
|
newY = (int) (mFloatView.getY() + changeY); |
||||||
|
mFloatView.updateXY(newX, newY); |
||||||
|
lastX = event.getRawX(); |
||||||
|
lastY = event.getRawY(); |
||||||
|
break; |
||||||
|
case MotionEvent.ACTION_UP: |
||||||
|
switch (mB.mMoveType) { |
||||||
|
case MoveType.slide: |
||||||
|
int startX = mFloatView.getX(); |
||||||
|
int endX = (startX * 2 + v.getWidth() > |
||||||
|
Util.getScreenWidth(mB.mApplicationContext)) ? |
||||||
|
Util.getScreenWidth(mB.mApplicationContext) - v.getWidth() : 0; |
||||||
|
mAnimator = ObjectAnimator.ofInt(startX, endX); |
||||||
|
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
||||||
|
@Override |
||||||
|
public void onAnimationUpdate(ValueAnimator animation) { |
||||||
|
int x = (int) animation.getAnimatedValue(); |
||||||
|
mFloatView.updateX(x); |
||||||
|
} |
||||||
|
}); |
||||||
|
startAnimator(); |
||||||
|
break; |
||||||
|
case MoveType.back: |
||||||
|
PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt("x", mFloatView.getX(), mB.xOffset); |
||||||
|
PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt("y", mFloatView.getY(), mB.yOffset); |
||||||
|
mAnimator = ObjectAnimator.ofPropertyValuesHolder(pvhX, pvhY); |
||||||
|
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { |
||||||
|
@Override |
||||||
|
public void onAnimationUpdate(ValueAnimator animation) { |
||||||
|
int x = (int) animation.getAnimatedValue("x"); |
||||||
|
int y = (int) animation.getAnimatedValue("y"); |
||||||
|
mFloatView.updateXY(x, y); |
||||||
|
} |
||||||
|
}); |
||||||
|
startAnimator(); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
|
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void startAnimator() { |
||||||
|
if (mB.mInterpolator == null) { |
||||||
|
if (mDecelerateInterpolator == null) { |
||||||
|
mDecelerateInterpolator = new DecelerateInterpolator(); |
||||||
|
} |
||||||
|
mB.mInterpolator = mDecelerateInterpolator; |
||||||
|
} |
||||||
|
mAnimator.setInterpolator(mB.mInterpolator); |
||||||
|
mAnimator.addListener(new AnimatorListenerAdapter() { |
||||||
|
@Override |
||||||
|
public void onAnimationEnd(Animator animation) { |
||||||
|
mAnimator.removeAllUpdateListeners(); |
||||||
|
mAnimator.removeAllListeners(); |
||||||
|
mAnimator = null; |
||||||
|
} |
||||||
|
}); |
||||||
|
mAnimator.setDuration(mB.mDuration).start(); |
||||||
|
} |
||||||
|
|
||||||
|
private void cancelAnimator() { |
||||||
|
if (mAnimator != null && mAnimator.isRunning()) { |
||||||
|
mAnimator.cancel(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/12/22. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
interface LifecycleListener { |
||||||
|
|
||||||
|
void onShow(); |
||||||
|
|
||||||
|
void onHide(); |
||||||
|
|
||||||
|
void onPostHide(); |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
|
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/12/22. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
public class MoveType { |
||||||
|
static final int fixed = 0; |
||||||
|
public static final int free = 1; |
||||||
|
public static final int active = 2; |
||||||
|
public static final int slide = 3; |
||||||
|
public static final int back = 4; |
||||||
|
|
||||||
|
@IntDef({fixed, free, active, slide, back}) |
||||||
|
@Retention(RetentionPolicy.SOURCE) |
||||||
|
@interface MOVE_TYPE { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/11/14. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
interface PermissionListener { |
||||||
|
void onSuccess(); |
||||||
|
|
||||||
|
void onFail(); |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
|
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/12/23. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
public class Screen { |
||||||
|
public static final int width = 0; |
||||||
|
public static final int height = 1; |
||||||
|
|
||||||
|
@IntDef({width, height}) |
||||||
|
@Retention(RetentionPolicy.SOURCE) |
||||||
|
@interface screenType { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.frank.ffmpeg.floating; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.Point; |
||||||
|
import android.os.Build; |
||||||
|
import android.provider.Settings; |
||||||
|
import android.support.annotation.RequiresApi; |
||||||
|
import android.view.LayoutInflater; |
||||||
|
import android.view.View; |
||||||
|
import android.view.WindowManager; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by yhao on 2017/12/22. |
||||||
|
* https://github.com/yhaolpz
|
||||||
|
*/ |
||||||
|
|
||||||
|
public class Util { |
||||||
|
|
||||||
|
|
||||||
|
static View inflate(Context applicationContext, int layoutId) { |
||||||
|
LayoutInflater inflate = (LayoutInflater) applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
||||||
|
return inflate.inflate(layoutId, null); |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.M) |
||||||
|
public static boolean hasPermission(Context context) { |
||||||
|
return Settings.canDrawOverlays(context); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private static Point sPoint; |
||||||
|
|
||||||
|
static int getScreenWidth(Context context) { |
||||||
|
if (sPoint == null) { |
||||||
|
sPoint = new Point(); |
||||||
|
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); |
||||||
|
wm.getDefaultDisplay().getSize(sPoint); |
||||||
|
} |
||||||
|
return sPoint.x; |
||||||
|
} |
||||||
|
|
||||||
|
static int getScreenHeight(Context context) { |
||||||
|
if (sPoint == null) { |
||||||
|
sPoint = new Point(); |
||||||
|
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); |
||||||
|
wm.getDefaultDisplay().getSize(sPoint); |
||||||
|
} |
||||||
|
return sPoint.y; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue