diff --git a/app/build.gradle b/app/build.gradle
index 22b7ebb1..d894d171 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -65,6 +65,7 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation project(path: ':floatingx')
detektPlugins 'io.gitlab.arturbosch.detekt:detekt-formatting:1.22.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.2.0'
@@ -74,4 +75,6 @@ dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
// debugImplementation 'com.squareup.leakcanary:leakcanary-object-watcher-android-startup:2.10'
implementation isDev ? project(path: ':floatingx') : "io.github.petterpx:floatingx:$version_name"
+ debugImplementation "com.bytedance.tools.codelocator:codelocator-core:2.0.3"
+ implementation 'com.github.princekin-f:EasyFloat:2.0.4'
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 533b23f8..759c3d07 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,40 +1,46 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.petterp.floatingx.app">
+
+
+
+ android:name=".kotlin.CustomKtApplication"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.FloatingX">
-
-
-
-
-
-
+ android:name="com.petterp.floatingx.app.MainActivity"
+ android:configChanges="keyboard|orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|locale|navigation|fontScale|mcc|mnc|uiMode"
+ android:exported="true"
+ android:windowSoftInputMode="adjustPan">
+ android:name=".test.ImmersedActivity"
+ android:configChanges="keyboard|orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|locale|navigation|fontScale|mcc|mnc|uiMode" />
+ android:name=".test.ScopeActivity"
+ android:configChanges="keyboard|orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|locale|navigation|fontScale|mcc|mnc|uiMode" />
+ android:name=".test.SystemActivity"
+ android:exported="true">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/petterp/floatingx/app/MainActivity.kt b/app/src/main/java/com/petterp/floatingx/app/MainActivity.kt
index acde2a24..6c7f72d5 100644
--- a/app/src/main/java/com/petterp/floatingx/app/MainActivity.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/MainActivity.kt
@@ -11,6 +11,7 @@ import androidx.cardview.widget.CardView
import com.petterp.floatingx.FloatingX
import com.petterp.floatingx.app.simple.FxAnimationImpl
import com.petterp.floatingx.app.test.MultipleFxActivity
+import com.petterp.floatingx.app.test.SystemActivity
import com.petterp.floatingx.util.createFx
class MainActivity : AppCompatActivity() {
@@ -19,7 +20,7 @@ class MainActivity : AppCompatActivity() {
private val activityFx by createFx {
setLayout(R.layout.item_floating)
- setEnableLog(true, "act")
+ setEnableLog(true, "activityFx")
build().toControl(this@MainActivity)
}
@@ -38,7 +39,7 @@ class MainActivity : AppCompatActivity() {
addNestedScrollView {
addLinearLayout {
addItemView("显示全局悬浮窗") {
- FloatingX.control(MultipleFxActivity.TAG_1).show(this@MainActivity)
+ FloatingX.control(MultipleFxActivity.TAG_1).show()
}
addItemView("隐藏全局悬浮窗") {
FloatingX.control(MultipleFxActivity.TAG_1).hide()
@@ -49,7 +50,7 @@ class MainActivity : AppCompatActivity() {
this.updateViewContent {
it.setText(R.id.tvItemFx, "App")
}
- }.show(this@MainActivity)
+ }.show()
}
addItemView("更新当前[全局浮窗]内容-(传递view方式)") {
FloatingX.control(MultipleFxActivity.TAG_1).apply {
@@ -62,7 +63,7 @@ class MainActivity : AppCompatActivity() {
setBackgroundColor(Color.GRAY)
}
}
- show(this@MainActivity)
+ show()
}
}
addItemView("显示一个Activity悬浮窗-(展示与多指触摸)") {
@@ -80,9 +81,43 @@ class MainActivity : AppCompatActivity() {
.setCardBackgroundColor(Color.GREEN)
}
}
+ addItemView("显示windows级别悬浮窗") {
+// val config =
+// FxAppHelper.builder().setLayout(R.layout.item_floating)
+// .setEnableLog(true, "windows")
+// .setContext(applicationContext).build()
+// val layoutParam = WindowManager.LayoutParams().apply {
+// // 设置大小 自适应
+// width = WindowManager.LayoutParams.WRAP_CONTENT
+// height = WindowManager.LayoutParams.WRAP_CONTENT
+// format = PixelFormat.TRANSPARENT
+// /**
+// * 注意,flag的值可以为:
+// * 下面的flags属性的效果形同“锁定”。
+// * 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
+// * LayoutParams.FLAG_NOT_TOUCH_MODAL 不影响后面的事件
+// * LayoutParams.FLAG_NOT_FOCUSABLE 不可聚焦
+// * LayoutParams.FLAG_NOT_TOUCHABLE 不可触摸
+// */
+// flags =
+// WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+// type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+// WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+// } else {
+// WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
+// }
+// }
+// val fx = FxManagerView(applicationContext).init(config)
+// fx.windowManager = windowManager
+// windowManager.addView(fx, layoutParam)
+ windowManager
+ }
addItemView("进入测试页面") {
TestActivity::class.java.start(this@MainActivity)
}
+ addItemView("进入system浮窗测试页面") {
+ SystemActivity::class.java.start(this@MainActivity)
+ }
}
}
}
@@ -105,3 +140,36 @@ class MainActivity : AppCompatActivity() {
return viewGroup
}
}
+
+// class ItemViewTouchListener(val wl: WindowManager.LayoutParams, val windowManager: WindowManager) :
+// View.OnTouchListener {
+// private var x = 0
+// private var y = 0
+// override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
+// when (motionEvent.action) {
+// MotionEvent.ACTION_DOWN -> {
+// x = motionEvent.rawX.toInt()
+// y = motionEvent.rawY.toInt()
+// }
+//
+// MotionEvent.ACTION_MOVE -> {
+// val nowX = motionEvent.rawX.toInt()
+// val nowY = motionEvent.rawY.toInt()
+// val movedX = nowX - x
+// val movedY = nowY - y
+// x = nowX
+// y = nowY
+// wl.apply {
+// x += movedX
+// y += movedY
+// }
+// // 更新悬浮球控件位置
+// windowManager?.updateViewLayout(view, wl)
+// }
+//
+// else -> {
+// }
+// }
+// return false
+// }
+// }
diff --git a/app/src/main/java/com/petterp/floatingx/app/java/CustomJavaApplication.java b/app/src/main/java/com/petterp/floatingx/app/java/CustomJavaApplication.java
index d5e7ec41..0a756367 100644
--- a/app/src/main/java/com/petterp/floatingx/app/java/CustomJavaApplication.java
+++ b/app/src/main/java/com/petterp/floatingx/app/java/CustomJavaApplication.java
@@ -12,9 +12,9 @@
import com.petterp.floatingx.app.simple.FxAnimationImpl;
import com.petterp.floatingx.app.simple.FxConfigStorageToSpImpl;
import com.petterp.floatingx.assist.FxDisplayMode;
-import com.petterp.floatingx.assist.helper.AppHelper;
-import com.petterp.floatingx.assist.helper.ScopeHelper;
-import com.petterp.floatingx.impl.lifecycle.FxTagActivityLifecycleImpl;
+import com.petterp.floatingx.assist.helper.FxAppHelper;
+import com.petterp.floatingx.assist.helper.FxScopeHelper;
+import com.petterp.floatingx.impl.lifecycle.FxProxyTagLifecycleImp;
/**
* java 中的配置示例
@@ -25,7 +25,7 @@ public class CustomJavaApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
- AppHelper helper = AppHelper.builder()
+ FxAppHelper helper = FxAppHelper.builder()
.setLayout(R.layout.item_floating)
// 设置启用日志,tag可以自定义,最终显示为FloatingX-xxx
.setEnableLog(true, "自定义的tag")
@@ -69,7 +69,7 @@ public void onCreate() {
// 设置浮窗展示类型,默认可移动可点击,无需配置
.setDisplayMode(FxDisplayMode.Normal)
//启用悬浮窗,即默认会插入到允许的activity中
- .setTagActivityLifecycle(new FxTagActivityLifecycleImpl() {
+ .setTagActivityLifecycle(new FxProxyTagLifecycleImp() {
@Override
public void onCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
// 允许插入的浮窗activity执行到onCreated时会回调相应方法
@@ -85,7 +85,7 @@ public void onCreated(@NonNull Activity activity, @Nullable Bundle bundle) {
* 创建一个局部悬浮窗
*/
public void createScopeFxSimple(Activity activity) {
- ScopeHelper.builder()
+ FxScopeHelper.builder()
.setLayout(R.layout.item_floating)
.build()
.toControl(activity);
diff --git a/app/src/main/java/com/petterp/floatingx/app/kotlin/CustomKtApplication.kt b/app/src/main/java/com/petterp/floatingx/app/kotlin/CustomKtApplication.kt
index 9cafe53a..a5ec2b2f 100644
--- a/app/src/main/java/com/petterp/floatingx/app/kotlin/CustomKtApplication.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/kotlin/CustomKtApplication.kt
@@ -4,7 +4,6 @@ import android.app.Activity
import android.app.Application
import android.graphics.Color
import android.os.Bundle
-import android.util.Log
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
@@ -20,9 +19,10 @@ import com.petterp.floatingx.app.test.BlackActivity
import com.petterp.floatingx.app.test.MultipleFxActivity
import com.petterp.floatingx.assist.FxDisplayMode
import com.petterp.floatingx.assist.FxGravity
-import com.petterp.floatingx.impl.FxScrollImpl
-import com.petterp.floatingx.impl.lifecycle.FxTagActivityLifecycleImpl
+import com.petterp.floatingx.assist.FxScopeType
+import com.petterp.floatingx.impl.lifecycle.FxProxyTagLifecycleImp
import com.petterp.floatingx.listener.IFxViewLifecycle
+import com.petterp.floatingx.util.FxScrollImpl
/** Kotlin-Application */
class CustomKtApplication : Application() {
@@ -44,6 +44,7 @@ class CustomKtApplication : Application() {
// }
installTag1(this)
+ installTag2(this)
}
companion object {
@@ -51,6 +52,9 @@ class CustomKtApplication : Application() {
fun installTag1(context: Application) {
FloatingX.install {
setContext(context)
+ setSystemScope(FxScopeType.SYSTEM)
+ // 设置浮窗展示类型,默认可移动可点击,无需配置
+ setDisplayMode(FxDisplayMode.Normal)
setLayout(R.layout.item_floating)
// 传递自定义的View
// setLayoutView(
@@ -63,9 +67,9 @@ class CustomKtApplication : Application() {
// )
// 设置悬浮窗默认方向
- setGravity(FxGravity.RIGHT_OR_BOTTOM)
+ setGravity(FxGravity.RIGHT_OR_TOP)
// 启用辅助方向,具体参加方法注释
- setEnableAssistDirection(r = 100f)
+ setEnableAssistDirection(r = 100f, t = 100f)
// 设置启用边缘吸附,默认启用
setEnableEdgeAdsorption(true)
// 设置边缘偏移量
@@ -98,7 +102,7 @@ class CustomKtApplication : Application() {
Toast.makeText(context, "浮窗被点击", Toast.LENGTH_SHORT).show()
}
// 设置tag-Activity生命周期回调时的触发
- setTagActivityLifecycle(object : FxTagActivityLifecycleImpl() {
+ setTagActivityLifecycle(object : FxProxyTagLifecycleImp() {
override fun onCreated(activity: Activity, bundle: Bundle?) {
// 允许插入的浮窗activity执行到onCreated时会回调相应方法
}
@@ -112,26 +116,20 @@ class CustomKtApplication : Application() {
setScrollListener(object : FxScrollImpl() {
override fun down() {
// 按下
- Log.e("petterp", "down")
}
override fun up() {
// 释放
- Log.e("petterp", "up")
}
override fun dragIng(event: MotionEvent, x: Float, y: Float) {
// 正在拖动
-// Log.e("petterp", "dragIng-$x,$y")
}
override fun eventIng(event: MotionEvent) {
// 接收所有事件传递
- Log.e("petterp", "eventIng,${event.x},${event.y}")
}
})
- // 设置浮窗展示类型,默认可移动可点击,无需配置
- setDisplayMode(FxDisplayMode.Normal)
// 设置是否启用日志
setEnableLog(BuildConfig.DEBUG)
// 设置浮窗tag
@@ -139,11 +137,13 @@ class CustomKtApplication : Application() {
// 只有调用了enableFx,默认才会启用fx,否则fx不会自动插入activity
// ps: 这里的只有调用了enableFx仅仅只是配置工具层的标记,后续使用control.show()也会默认启用
enableFx()
- }
+ }.show()
}
fun installTag2(context: Application) {
FloatingX.install {
+ setContext(context)
+ setSystemScope(FxScopeType.APP_ACTIVITY)
setLayoutView(
CardView(context).apply {
setCardBackgroundColor(Color.GRAY)
@@ -151,11 +151,11 @@ class CustomKtApplication : Application() {
addView(
TextView(this.context).apply {
layoutParams = ViewGroup.LayoutParams(
- 60.dp,
+ -2,
60.dp,
)
gravity = Gravity.CENTER
- text = "浮窗2"
+ text = "浮窗2-act"
setTextColor(Color.WHITE)
textSize = 15f
},
@@ -164,6 +164,8 @@ class CustomKtApplication : Application() {
)
setTag(MultipleFxActivity.TAG_2)
setEnableLog(true)
+ setEdgeOffset(20f)
+ setEnableEdgeAdsorption(true)
enableFx()
}
}
diff --git a/app/src/main/java/com/petterp/floatingx/app/test/BlackActivity.kt b/app/src/main/java/com/petterp/floatingx/app/test/BlackActivity.kt
index 7b1f097f..6c8076ab 100644
--- a/app/src/main/java/com/petterp/floatingx/app/test/BlackActivity.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/test/BlackActivity.kt
@@ -17,7 +17,7 @@ class BlackActivity : AppCompatActivity() {
textSize = 50f
}
addItemView("显示全局悬浮窗") {
- FloatingX.control(MultipleFxActivity.TAG_1).show(this@BlackActivity)
+ FloatingX.control(MultipleFxActivity.TAG_1).show()
}
}
}
diff --git a/app/src/main/java/com/petterp/floatingx/app/test/ImmersedActivity.kt b/app/src/main/java/com/petterp/floatingx/app/test/ImmersedActivity.kt
index cb74e1a3..4ed8aaba 100644
--- a/app/src/main/java/com/petterp/floatingx/app/test/ImmersedActivity.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/test/ImmersedActivity.kt
@@ -1,12 +1,8 @@
package com.petterp.floatingx.app.test
-import android.graphics.Color
import android.os.Bundle
-import android.view.View
import android.view.Window
-import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
-import com.petterp.floatingx.app.createLinearLayoutToParent
/** 全屏Activity */
class ImmersedActivity : AppCompatActivity() {
@@ -14,13 +10,13 @@ class ImmersedActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE) // 这行代码一定要在setContentView之前,不然会闪退
- createLinearLayoutToParent {
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
- window.decorView.systemUiVisibility = (
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE // 防止状态栏、底部导航栏隐藏时,内容区域大小发生变化
- or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- ) // Activity会全屏显示,但状态栏不会被隐藏,状态栏依然可见,Activity 顶端布局部分会被状态栏盖住
- window.statusBarColor = Color.TRANSPARENT
- }
+// createLinearLayoutToParent {
+// window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
+// window.decorView.systemUiVisibility = (
+// View.SYSTEM_UI_FLAG_LAYOUT_STABLE // 防止状态栏、底部导航栏隐藏时,内容区域大小发生变化
+// or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+// ) // Activity会全屏显示,但状态栏不会被隐藏,状态栏依然可见,Activity 顶端布局部分会被状态栏盖住
+// window.statusBarColor = Color.TRANSPARENT
+// }
}
}
diff --git a/app/src/main/java/com/petterp/floatingx/app/test/MultipleFxActivity.kt b/app/src/main/java/com/petterp/floatingx/app/test/MultipleFxActivity.kt
index 7f536d32..35696e06 100644
--- a/app/src/main/java/com/petterp/floatingx/app/test/MultipleFxActivity.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/test/MultipleFxActivity.kt
@@ -24,17 +24,17 @@ class MultipleFxActivity : AppCompatActivity() {
if (!FloatingX.isInstalled(TAG_1)) {
CustomKtApplication.installTag1(application)
}
- FloatingX.control(TAG_1).show(this@MultipleFxActivity)
+ FloatingX.control(TAG_1).show()
}
addItemView("显示全局悬浮窗(tag2)") {
if (!FloatingX.isInstalled(TAG_2)) {
CustomKtApplication.installTag2(application)
}
- FloatingX.control(TAG_2).show(this@MultipleFxActivity)
+ FloatingX.control(TAG_2).show()
}
addItemView("重复安装全局悬浮窗(tag1)") {
CustomKtApplication.installTag1(application)
- FloatingX.control(TAG_1).show(this@MultipleFxActivity)
+ FloatingX.control(TAG_1).show()
}
addItemView("隐藏全局悬浮窗(tag1)") {
FloatingX.controlOrNull(TAG_1)?.hide()
diff --git a/app/src/main/java/com/petterp/floatingx/app/test/ScopeActivity.kt b/app/src/main/java/com/petterp/floatingx/app/test/ScopeActivity.kt
index 972e8ff3..bb57f921 100644
--- a/app/src/main/java/com/petterp/floatingx/app/test/ScopeActivity.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/test/ScopeActivity.kt
@@ -14,9 +14,9 @@ import com.petterp.floatingx.app.addNestedScrollView
import com.petterp.floatingx.app.addTextView
import com.petterp.floatingx.app.createLinearLayoutToParent
import com.petterp.floatingx.app.simple.FxAnimationImpl
+import com.petterp.floatingx.assist.FxAdsorbDirection
import com.petterp.floatingx.assist.FxDisplayMode
import com.petterp.floatingx.assist.FxGravity
-import com.petterp.floatingx.util.FxAdsorbDirection
import com.petterp.floatingx.util.createFx
/** @author petterp */
diff --git a/app/src/main/java/com/petterp/floatingx/app/test/SimpleRvActivity.kt b/app/src/main/java/com/petterp/floatingx/app/test/SimpleRvActivity.kt
index 6314c084..57150f5f 100644
--- a/app/src/main/java/com/petterp/floatingx/app/test/SimpleRvActivity.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/test/SimpleRvActivity.kt
@@ -36,7 +36,7 @@ class SimpleRvActivity : AppCompatActivity() {
}
}
FloatingX.controlOrNull(TAG)?.apply {
- if (!isShow()) show(this@SimpleRvActivity)
+ if (!isShow()) show()
}
}
addItemView("隐藏浮窗") {
diff --git a/app/src/main/java/com/petterp/floatingx/app/test/SystemActivity.kt b/app/src/main/java/com/petterp/floatingx/app/test/SystemActivity.kt
new file mode 100644
index 00000000..eb2c0211
--- /dev/null
+++ b/app/src/main/java/com/petterp/floatingx/app/test/SystemActivity.kt
@@ -0,0 +1,58 @@
+package com.petterp.floatingx.app.test
+
+import android.graphics.Color
+import android.os.Bundle
+import android.view.Gravity
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import com.petterp.floatingx.FloatingX
+import com.petterp.floatingx.app.R
+import com.petterp.floatingx.app.addItemView
+import com.petterp.floatingx.app.addLinearLayout
+import com.petterp.floatingx.app.addNestedScrollView
+import com.petterp.floatingx.app.createLinearLayoutToParent
+import com.petterp.floatingx.app.dp
+
+/**
+ *
+ * @author petterp
+ */
+class SystemActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ createLinearLayoutToParent {
+ addNestedScrollView {
+ addLinearLayout {
+ addItemView("移动到(0,0)") {
+ FloatingX.controlOrNull(MultipleFxActivity.TAG_1)?.move(0f, 0f)
+ }
+ addItemView("移动到(-100,0)") {
+ FloatingX.controlOrNull(MultipleFxActivity.TAG_1)?.move(-100f, 0f)
+ }
+ addItemView("移动到(300,0)") {
+ FloatingX.controlOrNull(MultipleFxActivity.TAG_1)?.move(300f, 0f)
+ }
+ addItemView("移动到(500,500)") {
+ FloatingX.controlOrNull(MultipleFxActivity.TAG_1)?.move(500f, 500f)
+ }
+ addItemView("updateView()") {
+ FloatingX.controlOrNull(MultipleFxActivity.TAG_1)?.updateView {
+ TextView(it).apply {
+ layoutParams = ViewGroup.LayoutParams(100.dp, 200.dp)
+ text = "App"
+ gravity = Gravity.CENTER
+ textSize = 15f
+ setBackgroundColor(Color.GRAY)
+ }
+ }
+ }
+ addItemView("updateView2()") {
+ FloatingX.controlOrNull(MultipleFxActivity.TAG_1)
+ ?.updateView(R.layout.item_floating_new)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/petterp/floatingx/app/utils.kt b/app/src/main/java/com/petterp/floatingx/app/utils.kt
index 407c4b1f..1b1685ec 100644
--- a/app/src/main/java/com/petterp/floatingx/app/utils.kt
+++ b/app/src/main/java/com/petterp/floatingx/app/utils.kt
@@ -28,7 +28,7 @@ inline fun Context.createLinearLayout(obj: LinearLayout.() -> Unit) =
LinearLayout(this).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT
+ LinearLayout.LayoutParams.MATCH_PARENT,
)
orientation = LinearLayout.VERTICAL
obj.invoke(this)
@@ -39,11 +39,11 @@ inline fun ViewGroup.addLinearLayout(obj: LinearLayout.() -> Unit) =
LinearLayout(context).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT
+ LinearLayout.LayoutParams.MATCH_PARENT,
)
orientation = LinearLayout.VERTICAL
obj.invoke(this)
- }
+ },
)
inline fun ViewGroup.addFrameLayout(obj: FrameLayout.() -> Unit) =
@@ -51,10 +51,10 @@ inline fun ViewGroup.addFrameLayout(obj: FrameLayout.() -> Unit) =
FrameLayout(context).apply {
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
+ FrameLayout.LayoutParams.MATCH_PARENT,
)
obj.invoke(this)
- }
+ },
)
inline fun ViewGroup.addNestedScrollView(obj: NestedScrollView.() -> Unit) {
@@ -62,7 +62,7 @@ inline fun ViewGroup.addNestedScrollView(obj: NestedScrollView.() -> Unit) {
NestedScrollView(context).apply {
layoutParams = FrameLayout.LayoutParams(-1, -2)
obj.invoke(this)
- }
+ },
)
}
@@ -71,20 +71,20 @@ fun ViewGroup.addItemView(text: String, click: View.OnClickListener) =
Button(context).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
(this as TextView).isAllCaps = false
gravity = Gravity.CENTER
setOnClickListener(click)
this.text = text
- }
+ },
)
inline fun ViewGroup.addTextView(obj: TextView.() -> Unit) {
addView(
TextView(context).apply {
obj.invoke(this)
- }
+ },
)
}
@@ -93,7 +93,15 @@ fun Class<*>.start(context: Context) {
}
val Number.dp: Int
- get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, toFloat(), Resources.getSystem().displayMetrics).toInt()
+ get() = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ toFloat(),
+ Resources.getSystem().displayMetrics,
+ ).toInt()
val Number.dpF: Float
- get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, toFloat(), Resources.getSystem().displayMetrics)
\ No newline at end of file
+ get() = TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP,
+ toFloat(),
+ Resources.getSystem().displayMetrics,
+ )
diff --git a/build.gradle b/build.gradle
index 5cea2fc0..19fd7d25 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,4 +9,6 @@ buildscript {
plugins {
id 'io.gitlab.arturbosch.detekt' version '1.22.0' apply false
id "com.vanniktech.maven.publish" version "0.25.3"
+ id 'com.android.library' version '7.4.2' apply false
+ id 'org.jetbrains.kotlin.android' version '1.5.21' apply false
}
diff --git a/check/detekt/baseline.xml b/check/detekt/baseline.xml
index 6848b30e..eaf1a4d3 100644
--- a/check/detekt/baseline.xml
+++ b/check/detekt/baseline.xml
@@ -1,7 +1,7 @@
- MaxLineLength:FxManagerView.kt$FxManagerView$helper.fxLog?.d("fxView--lifecycle-> onConfigurationChanged--saveLocation:[${x.roundToInt()},${y.roundToInt()}]")
+ MaxLineLength:FxManagerView.kt$FxManagerView$helper.fxLog.d("fxView--lifecycle-> onConfigurationChanged--saveLocation:[${x.roundToInt()},${y.roundToInt()}]")
TooManyFunctions:FxScreenExt.kt$com.petterp.floatingx.util.FxScreenExt.kt
TooGenericExceptionCaught:FxUiExt.kt$e: Exception
PrintStackTrace:FxUiExt.kt$e
@@ -23,9 +23,9 @@
MaxLineLength:FxAppControlImpl.kt$FxAppControlImpl$throw IllegalArgumentException("view.context != Application,The global floating window must use application as context!")
MaxLineLength:FxBasisControlImpl.kt$FxBasisControlImpl$if (getContainerGroup() == null) throw NullPointerException("FloatingX window The parent container cannot be null!")
MaxLineLength:FxBasisControlImpl.kt$FxBasisControlImpl$if (helper.layoutId == 0 && helper.layoutView == null) throw RuntimeException("The layout id cannot be 0 ,and layoutView==null")
- MaxLineLength:FxProxyLifecycleCallBackImpl.kt$FxProxyLifecycleCallBackImpl$fxLog?.d("fxApp->detach? isContainActivity-${activity.isActivityInValid}--enableFx-$enableFx---isParent-$isParent")
+ MaxLineLength:FxProxyLifecycleCallBackImpl.kt$FxProxyLifecycleCallBackImpl$fxLog.d("fxApp->detach? isContainActivity-${activity.isActivityInValid}--enableFx-$enableFx---isParent-$isParent")
MaxLineLength:FxScopeControl.kt$FxScopeControl$throw IllegalArgumentException("view == Application,Scope floating windows cannot use application-level views!")
- MaxLineLength:FxViewConfigHelper.kt$FxViewConfigHelper$helper.fxLog?.d("fxView->updateContainerSize: oldW-($mParentWidth),oldH-($mParentHeight),newW-($parentWidth),newH-($parentHeight)")
+ MaxLineLength:FxViewConfigHelper.kt$FxViewConfigHelper$helper.fxLog.d("fxView->updateContainerSize: oldW-($mParentWidth),oldH-($mParentHeight),newW-($parentWidth),newH-($parentHeight)")
SwallowedException:FxScreenExt.kt$e: Exception
TooGenericExceptionCaught:FxScreenExt.kt$e: Exception
TooGenericExceptionThrown:FxBasisControlImpl.kt$FxBasisControlImpl$throw RuntimeException("The layout id cannot be 0 ,and layoutView==null")
diff --git a/floatingx/proguard-floatingx.pro b/floatingx/proguard-floatingx.pro
index 6e44c7c4..f735c83d 100644
--- a/floatingx/proguard-floatingx.pro
+++ b/floatingx/proguard-floatingx.pro
@@ -1,7 +1,7 @@
-dontwarn com.petterp.floatingx.**
--keep public class com.petterp.floatingx.view.FxManagerView{*;}
+-keep public class com.petterp.floatingx.view.default.FxManagerView{*;}
-keep public class com.petterp.floatingx.view.FxViewHolder{*;}
--keep public class com.petterp.floatingx.util.FxScopeEnum{*;}
+-keep public class com.petterp.floatingx.assist.FxScopeEnum{*;}
-keep public class com.petterp.floatingx.assist.FxGravity{*;}
-keep public class com.petterp.floatingx.util.FxScreenExtKt{
private boolean checkNavigationBarShow(android.content.Context);
diff --git a/floatingx/src/main/AndroidManifest.xml b/floatingx/src/main/AndroidManifest.xml
index 127a4e5e..2459ee17 100644
--- a/floatingx/src/main/AndroidManifest.xml
+++ b/floatingx/src/main/AndroidManifest.xml
@@ -1,2 +1,12 @@
-
\ No newline at end of file
+
+
+
+
+
+
\ No newline at end of file
diff --git a/floatingx/src/main/java/com/petterp/floatingx/FloatingX.kt b/floatingx/src/main/java/com/petterp/floatingx/FloatingX.kt
index 7f904c5a..4f3e311d 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/FloatingX.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/FloatingX.kt
@@ -1,27 +1,21 @@
package com.petterp.floatingx
import android.annotation.SuppressLint
-import android.app.Activity
-import android.app.Application
-import com.petterp.floatingx.assist.helper.AppHelper
-import com.petterp.floatingx.impl.control.FxAppControlImpl
-import com.petterp.floatingx.impl.lifecycle.FxLifecycleCallbackImpl
-import com.petterp.floatingx.impl.lifecycle.FxProxyLifecycleCallBackImpl
+import com.petterp.floatingx.assist.FxScopeType
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.impl.provider.app.FxAppControlImp
+import com.petterp.floatingx.impl.provider.system.FxSystemControlImp
import com.petterp.floatingx.listener.control.IFxAppControl
import com.petterp.floatingx.listener.control.IFxConfigControl
+import com.petterp.floatingx.util.FX_APP_DEFAULT_TAG
/** Single Control To Fx */
@SuppressLint("StaticFieldLeak")
object FloatingX {
private const val FX_DEFAULT_INITIAL_CAPACITY = 3
- private var fxs = HashMap(FX_DEFAULT_INITIAL_CAPACITY)
- private var fxLifecycleCallback: FxLifecycleCallbackImpl? = null
@JvmSynthetic
- internal var context: Application? = null
-
- @JvmSynthetic
- internal const val FX_DEFAULT_TAG = "FX_DEFAULT_TAG"
+ internal val fxs = HashMap(FX_DEFAULT_INITIAL_CAPACITY)
/**
* 安装一个新的全局浮窗,以dsl方式
@@ -29,8 +23,8 @@ object FloatingX {
* 方法含义见 [install(helper: AppHelper)]
*/
@JvmSynthetic
- inline fun install(obj: AppHelper.Builder.() -> Unit) =
- install(AppHelper.builder().apply(obj).build())
+ inline fun install(obj: FxAppHelper.Builder.() -> Unit) =
+ install(FxAppHelper.builder().apply(obj).build())
/**
* 安装一个新的全局浮窗
@@ -41,15 +35,16 @@ object FloatingX {
*
*/
@JvmStatic
- fun install(helper: AppHelper): IFxAppControl {
- if (context == null) {
- throw NullPointerException("context == null, please call AppHelper.setContext(context) to set context")
+ fun install(helper: FxAppHelper): IFxAppControl {
+ fxs[helper.tag]?.cancel()
+ val fxAppControlImp = if (helper.scope == FxScopeType.SYSTEM) {
+ FxSystemControlImp(helper)
+ } else {
+ FxAppControlImp(helper)
}
- if (fxs.isNotEmpty()) fxs[helper.tag]?.cancel()
- val fxAppControlImpl = FxAppControlImpl(helper, FxProxyLifecycleCallBackImpl())
- fxs[helper.tag] = fxAppControlImpl
- if (helper.enableFx) checkAppLifecycleInstall()
- return fxAppControlImpl
+ fxAppControlImp.initProvider()
+ fxs[helper.tag] = fxAppControlImp
+ return fxAppControlImp
}
/**
@@ -59,7 +54,7 @@ object FloatingX {
*/
@JvmStatic
@JvmOverloads
- fun control(tag: String = FX_DEFAULT_TAG): IFxAppControl {
+ fun control(tag: String = FX_APP_DEFAULT_TAG): IFxAppControl {
return getTagFxControl(tag)
}
@@ -70,7 +65,7 @@ object FloatingX {
*/
@JvmStatic
@JvmOverloads
- fun controlOrNull(tag: String = FX_DEFAULT_TAG): IFxAppControl? {
+ fun controlOrNull(tag: String = FX_APP_DEFAULT_TAG): IFxAppControl? {
return fxs[tag]
}
@@ -81,7 +76,7 @@ object FloatingX {
*/
@JvmStatic
@JvmOverloads
- fun configControl(tag: String = FX_DEFAULT_TAG): IFxConfigControl {
+ fun configControl(tag: String = FX_APP_DEFAULT_TAG): IFxConfigControl {
return getTagFxControl(tag).configControl
}
@@ -92,7 +87,7 @@ object FloatingX {
*/
@JvmStatic
@JvmOverloads
- fun configControlOrNull(tag: String = FX_DEFAULT_TAG): IFxConfigControl? {
+ fun configControlOrNull(tag: String = FX_APP_DEFAULT_TAG): IFxConfigControl? {
return fxs[tag]?.configControl
}
@@ -101,7 +96,7 @@ object FloatingX {
* */
@JvmStatic
@JvmOverloads
- fun isInstalled(tag: String = FX_DEFAULT_TAG): Boolean {
+ fun isInstalled(tag: String = FX_APP_DEFAULT_TAG): Boolean {
return fxs[tag] != null
}
@@ -109,48 +104,15 @@ object FloatingX {
@JvmStatic
fun uninstallAll() {
if (fxs.isEmpty()) return
- // 这里需要避免 ConcurrentModificationException
- val keys = fxs.keys.toList()
- keys.forEach {
- fxs[it]?.cancel()
- }
- }
-
- @JvmSynthetic
- internal fun getFxList(): Map = fxs
-
- @JvmSynthetic
- internal fun uninstall(tag: String, control: FxAppControlImpl) {
- if (fxs.values.contains(control)) fxs.remove(tag)
- // 如果全局浮窗为null,自动清空配置
- if (fxs.isEmpty()) {
- release()
+ fxs.values.forEach {
+ it.cancel()
}
+ fxs.clear()
}
- /**
- * 检查AppLifecycle是否安装
- *
- * @param activity 初始化时的activity
- */
- @JvmSynthetic
- internal fun checkAppLifecycleInstall(activity: Activity? = null) {
- if (fxLifecycleCallback != null) return
- FxLifecycleCallbackImpl.updateTopActivity(activity)
- fxLifecycleCallback = FxLifecycleCallbackImpl()
- context?.registerActivityLifecycleCallbacks(fxLifecycleCallback)
- }
-
- private fun getTagFxControl(tag: String): FxAppControlImpl {
- val errorMessage =
- "fxs[$tag]==null!,Please check if FloatingX.install() or AppHelper.setTag() is called."
- return fxs[tag] ?: throw NullPointerException(errorMessage)
- }
-
- private fun release() {
- if (fxLifecycleCallback == null && FxLifecycleCallbackImpl.topActivity == null) return
- context?.unregisterActivityLifecycleCallbacks(fxLifecycleCallback)
- FxLifecycleCallbackImpl.releaseTopActivity()
- fxLifecycleCallback = null
+ private fun getTagFxControl(tag: String): IFxAppControl {
+ val control = fxs[tag]
+ checkNotNull(control) { "fxs[$tag]==null!,Please check if FloatingX.install() or AppHelper.setTag() is called." }
+ return control
}
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/FxAdsorbDirection.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/FxAdsorbDirection.kt
similarity index 75%
rename from floatingx/src/main/java/com/petterp/floatingx/util/FxAdsorbDirection.kt
rename to floatingx/src/main/java/com/petterp/floatingx/assist/FxAdsorbDirection.kt
index 286da684..b3bab8fa 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/util/FxAdsorbDirection.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/FxAdsorbDirection.kt
@@ -1,4 +1,4 @@
-package com.petterp.floatingx.util
+package com.petterp.floatingx.assist
/**
* Fx吸附方向
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/FxAnimation.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/FxAnimation.kt
index da9318b7..d66b8dff 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/assist/FxAnimation.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/FxAnimation.kt
@@ -2,6 +2,7 @@ package com.petterp.floatingx.assist
import android.animation.Animator
import android.widget.FrameLayout
+import com.petterp.floatingx.util.SimpleAnimatorListener
/**
* fx的动画辅助类
@@ -11,9 +12,8 @@ abstract class FxAnimation {
private var startAnimatorJob: Animator? = null
private var endAnimatorJob: Animator? = null
-
- private val Animator.animatorDuration: Long
- get() = duration + startDelay
+ private val startListener = SimpleAnimatorListener()
+ private val endListener = SimpleAnimatorListener()
/** 开始动画 */
abstract fun fromAnimator(view: FrameLayout?): Animator
@@ -35,24 +35,36 @@ abstract class FxAnimation {
internal fun endJobIsRunning(): Boolean = endAnimatorJob?.isRunning ?: false
@JvmSynthetic
- internal fun fromStartAnimator(view: FrameLayout?): Long {
- startAnimatorJob?.cancel()
- startAnimatorJob = fromAnimator(view)
- if (startAnimatorJob?.isRunning == true) {
- return startAnimatorJob?.duration ?: 0
+ internal fun fromStartAnimator(view: FrameLayout?): Boolean {
+ if (startAnimatorJob == null) {
+ startAnimatorJob = fromAnimator(view)
+ startAnimatorJob?.removeListener(startListener)
+ startAnimatorJob?.addListener(startListener)
}
+ if (endJobIsRunning()) endAnimatorJob?.cancel()
startAnimatorJob?.start()
- return startAnimatorJob?.animatorDuration ?: 0
+ return true
}
@JvmSynthetic
- internal fun toEndAnimator(view: FrameLayout?): Long {
- endAnimatorJob?.cancel()
- endAnimatorJob = toAnimator(view)
- if (endAnimatorJob?.isRunning == true) {
- return endAnimatorJob?.duration ?: 0
+ internal fun toEndAnimator(view: FrameLayout?): Boolean {
+ if (endAnimatorJob == null) {
+ endAnimatorJob = toAnimator(view)
+ endAnimatorJob?.removeListener(endListener)
+ endAnimatorJob?.addListener(endListener)
}
+ if (fromJobIsRunning()) startAnimatorJob?.cancel()
endAnimatorJob?.start()
- return endAnimatorJob?.animatorDuration ?: 0
+ return true
+ }
+
+ @JvmSynthetic
+ internal fun setFromAnimatorListener(obj: (() -> Unit)?) {
+ startListener.end = obj
+ }
+
+ @JvmSynthetic
+ internal fun setEndAnimatorListener(obj: (() -> Unit)?) {
+ endListener.end = obj
}
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/FxContentProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/FxContentProvider.kt
new file mode 100644
index 00000000..d286d13a
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/FxContentProvider.kt
@@ -0,0 +1,41 @@
+package com.petterp.floatingx.assist
+
+import android.app.Application
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.database.Cursor
+import android.net.Uri
+import com.petterp.floatingx.impl.lifecycle.FxAppLifecycleProvider
+
+/**
+ * FxDefaultProvider
+ * @author petterp
+ */
+class FxContentProvider : ContentProvider() {
+ override fun onCreate(): Boolean {
+ val application = context?.applicationContext as? Application ?: return true
+ application.registerActivityLifecycleCallbacks(FxAppLifecycleProvider())
+ return true
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array?,
+ selection: String?,
+ selectionArgs: Array?,
+ sortOrder: String?
+ ): Cursor? = null
+
+ override fun getType(uri: Uri): String? = null
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? = null
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int = 0
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array?
+ ): Int = 0
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/FxDisplayMode.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/FxDisplayMode.kt
index 7ce5d3f5..a7abf017 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/assist/FxDisplayMode.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/FxDisplayMode.kt
@@ -12,5 +12,5 @@ enum class FxDisplayMode {
ClickOnly,
// 展示模式:只用于展示,不响应任何事件不能移动
- DisplayOnly
+ DisplayOnly,
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/FxScopeType.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/FxScopeType.kt
new file mode 100644
index 00000000..c5acea71
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/FxScopeType.kt
@@ -0,0 +1,9 @@
+package com.petterp.floatingx.assist
+
+/** Fx插入的不同位置 */
+enum class FxScopeType {
+ SYSTEM, // 在系统中展示,必须有权限
+ AUTO_APP, // 根据用户的权限授予状态,Windows||Activity 自适应处理
+ APP_WINDOWS, // 在应用内展示,需要权限
+ APP_ACTIVITY, // 在应用内展示,无需权限
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/AppHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxAppHelper.kt
similarity index 75%
rename from floatingx/src/main/java/com/petterp/floatingx/assist/helper/AppHelper.kt
rename to floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxAppHelper.kt
index 26d8fedc..45a51713 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/AppHelper.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxAppHelper.kt
@@ -4,16 +4,21 @@ import android.app.Activity
import android.app.Application
import android.content.Context
import com.petterp.floatingx.FloatingX
+import com.petterp.floatingx.assist.FxScopeType
import com.petterp.floatingx.listener.IFxProxyTagActivityLifecycle
-import com.petterp.floatingx.util.FxScopeEnum
+import com.petterp.floatingx.util.FX_APP_DEFAULT_TAG
+import com.petterp.floatingx.util.FX_INSTALL_SCOPE_APP_TAG
+import com.petterp.floatingx.util.FX_INSTALL_SCOPE_SYSTEM_TAG
import com.petterp.floatingx.util.navigationBarHeight
import com.petterp.floatingx.util.statusBarHeight
-/** AppHelper构建器 */
-class AppHelper(
+/** FxAppConfig 构建器 */
+class FxAppHelper(
/** 浮窗tag,默认为 [FloatingX.FX_DEFAULT_TAG] */
@JvmSynthetic
internal var tag: String,
+ @JvmSynthetic
+ internal var context: Application,
/** 黑名单list */
@JvmSynthetic
internal val blackFilterList: MutableList>,
@@ -23,21 +28,23 @@ class AppHelper(
/** 是否允许插入全部Activity */
@JvmSynthetic
internal val isAllInstall: Boolean,
+ @JvmSynthetic
+ internal val scope: FxScopeType,
/** 显示悬浮窗的Activity生命周期回调 */
@JvmSynthetic
internal val fxLifecycleExpand: IFxProxyTagActivityLifecycle?
-) : BasisHelper() {
+) : FxBasisHelper() {
@JvmSynthetic
internal fun updateNavigationBar(activity: Activity?) {
navigationBarHeight = activity?.navigationBarHeight ?: navigationBarHeight
- fxLog?.v("system-> navigationBar-$navigationBarHeight")
+ fxLog.v("system-> navigationBar-$navigationBarHeight")
}
@JvmSynthetic
internal fun updateStatsBar(activity: Activity?) {
statsBarHeight = activity?.statusBarHeight ?: statsBarHeight
- fxLog?.v("system-> statusBarHeight-$statsBarHeight")
+ fxLog.v("system-> statusBarHeight-$statsBarHeight")
}
@JvmSynthetic
@@ -51,13 +58,15 @@ class AppHelper(
(!isAllInstall && whiteInsertList.contains(cls))
}
- class Builder : BasisHelper.Builder() {
+ class Builder : FxBasisHelper.Builder() {
private var whiteInsertList: MutableList> = mutableListOf()
private var blackFilterList: MutableList> = mutableListOf()
private var fxLifecycleExpand: IFxProxyTagActivityLifecycle? = null
private var isEnableAllInstall: Boolean = true
- private var tag = FloatingX.FX_DEFAULT_TAG
+ private var context: Application? = null
+ private var tag = FX_APP_DEFAULT_TAG
private var enableFx = false
+ private var scopeEnum: FxScopeType = FxScopeType.APP_ACTIVITY
/** 设置启用fx */
fun enableFx(): Builder {
@@ -71,9 +80,9 @@ class AppHelper(
* */
fun setContext(context: Context): Builder {
if (context is Application) {
- FloatingX.context = context
+ this.context = context
} else {
- FloatingX.context = context.applicationContext as Application
+ this.context = context.applicationContext as Application
}
return this
}
@@ -115,11 +124,18 @@ class AppHelper(
*/
@Throws(IllegalArgumentException::class)
fun setTag(tag: String): Builder {
- if (tag.isEmpty()) throw IllegalArgumentException("浮窗 tag 不能为 [\"\"],请设置一个合法的tag")
+ check(tag.isNotEmpty()) {
+ "浮窗 tag 不能为 [\"\"],请设置一个合法的tag"
+ }
this.tag = tag
return this
}
+ fun setSystemScope(scope: FxScopeType): Builder {
+ this.scopeEnum = scope
+ return this
+ }
+
/**
* 允许显示浮窗的activity
*
@@ -147,23 +163,31 @@ class AppHelper(
return this
}
- override fun buildHelper(): AppHelper =
- AppHelper(
+ override fun buildHelper(): FxAppHelper {
+ checkNotNull(context) { "context == null, please call AppHelper.setContext(context) to set context" }
+ return FxAppHelper(
tag,
+ context!!,
blackFilterList,
whiteInsertList,
isEnableAllInstall,
+ scopeEnum,
fxLifecycleExpand,
)
+ }
- override fun build(): AppHelper {
+ override fun build(): FxAppHelper {
return super.build().apply {
enableFx = this@Builder.enableFx
// 有可能用户会使用多个浮窗,这里为了防止日志混乱,将浮窗tag赋值给日志tag
if (enableDebugLog && fxLogTag.isEmpty()) {
fxLogTag = tag
}
- initLog(FxScopeEnum.APP_SCOPE.tag)
+ if (scopeEnum == FxScopeType.SYSTEM) {
+ initLog(FX_INSTALL_SCOPE_SYSTEM_TAG)
+ } else {
+ initLog(FX_INSTALL_SCOPE_APP_TAG)
+ }
}
}
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/BasisHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxBasisHelper.kt
similarity index 95%
rename from floatingx/src/main/java/com/petterp/floatingx/assist/helper/BasisHelper.kt
rename to floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxBasisHelper.kt
index 159ce647..19d70276 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/BasisHelper.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxBasisHelper.kt
@@ -3,6 +3,7 @@ package com.petterp.floatingx.assist.helper
import android.view.View
import android.widget.FrameLayout
import androidx.annotation.LayoutRes
+import com.petterp.floatingx.assist.FxAdsorbDirection
import com.petterp.floatingx.assist.FxAnimation
import com.petterp.floatingx.assist.FxBorderMargin
import com.petterp.floatingx.assist.FxDisplayMode
@@ -10,15 +11,20 @@ import com.petterp.floatingx.assist.FxGravity
import com.petterp.floatingx.listener.IFxConfigStorage
import com.petterp.floatingx.listener.IFxScrollListener
import com.petterp.floatingx.listener.IFxViewLifecycle
-import com.petterp.floatingx.util.FxAdsorbDirection
import com.petterp.floatingx.util.FxLog
import kotlin.math.abs
/** 通用构建器helper */
-open class BasisHelper {
+open class FxBasisHelper {
@JvmField
internal var layoutId: Int = 0
+ @JvmField
+ internal var offsetX: Int = 0
+
+ @JvmField
+ internal var offsetY: Int = 0
+
@JvmField
internal var layoutView: View? = null
@@ -52,6 +58,7 @@ open class BasisHelper {
@JvmField
internal var adsorbDirection: FxAdsorbDirection = FxAdsorbDirection.LEFT_OR_RIGHT
+ // TODO: 这里需要在考虑
@JvmField
internal var enableFx: Boolean = false
@@ -88,8 +95,7 @@ open class BasisHelper {
@JvmField
internal var iFxClickListener: View.OnClickListener? = null
- @JvmField
- internal var fxLog: FxLog? = null
+ internal lateinit var fxLog: FxLog
@JvmField
internal var fxLogTag: String = ""
@@ -103,7 +109,7 @@ open class BasisHelper {
@JvmSynthetic
internal fun initLog(scope: String) {
- if (enableDebugLog) fxLog = FxLog.builder("$scope-$fxLogTag")
+ fxLog = FxLog.builder(enableDebugLog, "$scope-$fxLogTag")
}
@JvmSynthetic
@@ -113,7 +119,10 @@ open class BasisHelper {
fxAnimation?.cancelAnimation()
}
- abstract class Builder {
+ val safeEdgeOffSet: Float
+ get() = if (enableEdgeRebound) edgeOffset else 0F
+
+ abstract class Builder {
@LayoutRes
private var layoutId: Int = 0
private var layoutView: View? = null
@@ -126,6 +135,8 @@ open class BasisHelper {
private var defaultY: Float = 0f
private var defaultX: Float = 0f
+ private var offsetX: Int = 0
+ private var offsetY: Int = 0
private var edgeOffset: Float = 0f
private var enableFx: Boolean = false
private var fxBorderMargin: FxBorderMargin = FxBorderMargin()
@@ -164,6 +175,9 @@ open class BasisHelper {
defaultY = this@Builder.defaultY
defaultX = this@Builder.defaultX
+ offsetX = this@Builder.offsetX
+ offsetY = this@Builder.offsetY
+
edgeOffset = this@Builder.edgeOffset
fxBorderMargin = this@Builder.fxBorderMargin
adsorbDirection = this@Builder.edgeAdsorbDirection
@@ -227,6 +241,12 @@ open class BasisHelper {
return this as T
}
+ fun setOffsetXY(x: Int, y: Int): T {
+ this.offsetX = x
+ this.offsetY = y
+ return this as T
+ }
+
/**
* 设置启用屏幕外滚动 默认为true,即悬浮窗可以拖动到全屏任意位置(除了状态栏与导航栏禁止覆盖)
* false时,可拖动范围受 borderMargin-边框偏移 与 moveEdge-边缘偏移 限制
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxScopeHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxScopeHelper.kt
new file mode 100644
index 00000000..29131dd5
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/FxScopeHelper.kt
@@ -0,0 +1,60 @@
+package com.petterp.floatingx.assist.helper
+
+import android.app.Activity
+import android.widget.FrameLayout
+import androidx.fragment.app.Fragment
+import com.petterp.floatingx.impl.provider.scope.FxScopeControl
+import com.petterp.floatingx.listener.control.IFxScopeControl
+import com.petterp.floatingx.util.FX_INSTALL_SCOPE_ACTIVITY_TAG
+import com.petterp.floatingx.util.FX_INSTALL_SCOPE_FRAGMENT_TAG
+import com.petterp.floatingx.util.FX_INSTALL_SCOPE_VIEW_GROUP_TAG
+import com.petterp.floatingx.util.contentView
+
+/** 特定范围的Helper构建器 */
+class FxScopeHelper : FxBasisHelper() {
+
+ /** 插入到Activity中 */
+ fun toControl(activity: Activity): IFxScopeControl {
+ initLog(FX_INSTALL_SCOPE_ACTIVITY_TAG)
+ val control = FxScopeControl(this)
+ control.initProvider()
+ activity.contentView?.let {
+ control.setContainerGroup(it)
+ } ?: fxLog.e("install to Activity the Error,current contentView(R.id.content) = null!")
+ return control
+ }
+
+ /** 插入到Fragment中 */
+ fun toControl(fragment: Fragment): IFxScopeControl {
+ initLog(FX_INSTALL_SCOPE_FRAGMENT_TAG)
+ val rootView = fragment.view as? FrameLayout
+ checkNotNull(rootView) {
+ "Check if your root layout is FrameLayout, or if the init call timing is after onCreateView()!"
+ }
+ val control = FxScopeControl(this)
+ control.initProvider()
+ control.setContainerGroup(rootView)
+ return control
+ }
+
+ /** 插入到ViewGroup中 */
+ fun toControl(group: FrameLayout): IFxScopeControl {
+ initLog(FX_INSTALL_SCOPE_VIEW_GROUP_TAG)
+ val control = FxScopeControl(this)
+ control.initProvider()
+ control.setContainerGroup(group)
+ return control
+ }
+
+ companion object {
+ @JvmStatic
+ fun builder(): Builder = Builder()
+
+ @JvmSynthetic
+ inline fun build(obj: Builder.() -> Unit) = builder().apply(obj).build()
+ }
+
+ class Builder : FxBasisHelper.Builder() {
+ override fun buildHelper(): FxScopeHelper = FxScopeHelper()
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/ScopeHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/assist/helper/ScopeHelper.kt
deleted file mode 100644
index 25058608..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/assist/helper/ScopeHelper.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.petterp.floatingx.assist.helper
-
-import android.app.Activity
-import android.widget.FrameLayout
-import androidx.fragment.app.Fragment
-import com.petterp.floatingx.impl.control.FxScopeControl
-import com.petterp.floatingx.listener.control.IFxScopeControl
-import com.petterp.floatingx.util.FxScopeEnum
-import com.petterp.floatingx.util.contentView
-
-/** 特定范围的Helper构建器 */
-class ScopeHelper : BasisHelper() {
-
- /** 插入到Activity中 */
- fun toControl(activity: Activity): IFxScopeControl {
- initLog(FxScopeEnum.ACTIVITY_SCOPE.tag)
- val control = FxScopeControl(this)
- activity.contentView?.let {
- control.setContainerGroup(it)
- } ?: fxLog?.e("install to Activity the Error,current contentView(R.id.content) = null!")
- return control
- }
-
- /** 插入到Fragment中 */
- fun toControl(fragment: Fragment): IFxScopeControl {
- initLog(FxScopeEnum.FRAGMENT_SCOPE.tag)
- val rootView = fragment.view as? FrameLayout
- checkNotNull(rootView) {
- "Check if your root layout is FrameLayout, or if the init call timing is after onCreateView()!"
- }
- val control = FxScopeControl(this)
- control.setContainerGroup(rootView)
- return control
- }
-
- /** 插入到ViewGroup中 */
- fun toControl(group: FrameLayout): IFxScopeControl {
- initLog(FxScopeEnum.VIEW_GROUP_SCOPE.tag)
- val control = FxScopeControl(this)
- control.setContainerGroup(group)
- return control
- }
-
- companion object {
- @JvmStatic
- fun builder(): Builder = Builder()
-
- @JvmSynthetic
- inline fun build(obj: Builder.() -> Unit) = builder().apply(obj).build()
- }
-
- class Builder : BasisHelper.Builder() {
- override fun buildHelper(): ScopeHelper = ScopeHelper()
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxAppControlImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxAppControlImpl.kt
deleted file mode 100644
index ef0a13a2..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxAppControlImpl.kt
+++ /dev/null
@@ -1,131 +0,0 @@
-package com.petterp.floatingx.impl.control
-
-import android.app.Activity
-import android.app.Application
-import android.content.Context
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.view.OnApplyWindowInsetsListener
-import androidx.core.view.ViewCompat
-import com.petterp.floatingx.FloatingX
-import com.petterp.floatingx.assist.helper.AppHelper
-import com.petterp.floatingx.impl.lifecycle.FxProxyLifecycleCallBackImpl
-import com.petterp.floatingx.listener.control.IFxAppControl
-import com.petterp.floatingx.util.decorView
-import com.petterp.floatingx.util.topActivity
-
-/** 全局控制器 */
-class FxAppControlImpl(
- private val helper: AppHelper,
- private val proxyLifecycleImpl: FxProxyLifecycleCallBackImpl,
-) : FxBasisControlImpl(helper),
- IFxAppControl,
- Application.ActivityLifecycleCallbacks by proxyLifecycleImpl {
-
- init {
- proxyLifecycleImpl.init(helper, this)
- }
-
- private val windowsInsetsListener = OnApplyWindowInsetsListener { _, insets ->
- val statusBar = insets.stableInsetTop
- if (helper.statsBarHeight != statusBar) {
- helper.fxLog?.v("System--StatusBar---old-(${helper.statsBarHeight}),new-($statusBar))")
- helper.statsBarHeight = statusBar
- }
- insets
- }
-
- override fun show(activity: Activity) {
- if (!helper.isCanInstall(activity) || isShow()) return
- if (attach(activity)) {
- getManagerView()?.show()
- updateEnableStatus(true)
- FloatingX.checkAppLifecycleInstall()
- }
- }
-
- override fun detach(activity: Activity) {
- activity.decorView?.let {
- detach(it)
- }
- }
-
- override fun getBindActivity(): Activity? {
- if (getContainerGroup() === topActivity?.decorView) {
- return topActivity
- }
- return null
- }
-
- /** 注意,全局浮窗下,view必须是全局application对应的context! */
- override fun updateView(view: View) {
- if (view.context !is Application) {
- throw IllegalArgumentException("view.context != Application,The global floating window must use application as context!")
- }
- super.updateView(view)
- }
-
- override fun context(): Context = FloatingX.context!!
-
- private fun initWindowsInsetsListener() {
- getManagerView()?.let {
- ViewCompat.setOnApplyWindowInsetsListener(it, windowsInsetsListener)
- it.requestApplyInsets()
- }
- }
-
- internal fun attach(activity: Activity): Boolean {
- activity.decorView?.let {
- if (getContainerGroup() === it) {
- return false
- }
- var isAnimation = false
- if (getManagerView() == null) {
- helper.updateNavigationBar(activity)
- helper.updateStatsBar(activity)
- initManagerView()
- isAnimation = true
- } else {
- if (getManagerView()?.visibility != View.VISIBLE) {
- getManagerView()?.visibility =
- View.VISIBLE
- }
- detach()
- }
- setContainerGroup(it)
- helper.fxLog?.d("fxView-lifecycle-> code->addView")
- helper.iFxViewLifecycle?.postAttach()
- getContainerGroup()?.addView(getManagerView())
- if (isAnimation && helper.enableAnimation && helper.fxAnimation != null) {
- helper.fxLog?.d("fxView->Animation -----start")
- helper.fxAnimation?.fromStartAnimator(getManagerView())
- }
- } ?: helper.fxLog?.e("system -> fxParentView==null")
- return true
- }
-
- override fun detach(container: ViewGroup?) {
- super.detach(container)
- clearContainer()
- }
-
- override fun initManager() {
- // 在清除之前移除insets监听
- clearWindowsInsetsListener()
- super.initManager()
- // 移除之后再添加inset监听
- initWindowsInsetsListener()
- }
-
- override fun reset() {
- // 重置之前记得移除insets
- clearWindowsInsetsListener()
- super.reset()
- FloatingX.uninstall(helper.tag, this)
- }
-
- private fun clearWindowsInsetsListener() {
- val managerView = getManagerView() ?: return
- ViewCompat.setOnApplyWindowInsetsListener(managerView, null)
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxBasisControlImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxBasisControlImpl.kt
deleted file mode 100644
index 299b5d1e..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxBasisControlImpl.kt
+++ /dev/null
@@ -1,303 +0,0 @@
-package com.petterp.floatingx.impl.control
-
-import android.content.Context
-import android.view.View
-import android.view.ViewGroup
-import androidx.annotation.LayoutRes
-import androidx.core.view.ViewCompat
-import com.petterp.floatingx.assist.FxAnimation
-import com.petterp.floatingx.assist.FxDisplayMode
-import com.petterp.floatingx.assist.helper.BasisHelper
-import com.petterp.floatingx.listener.IFxConfigStorage
-import com.petterp.floatingx.listener.IFxScrollListener
-import com.petterp.floatingx.listener.IFxViewLifecycle
-import com.petterp.floatingx.listener.control.IFxConfigControl
-import com.petterp.floatingx.listener.control.IFxControl
-import com.petterp.floatingx.listener.provider.IFxContextProvider
-import com.petterp.floatingx.listener.provider.IFxHolderProvider
-import com.petterp.floatingx.util.FxAdsorbDirection
-import com.petterp.floatingx.util.lazyLoad
-import com.petterp.floatingx.view.FxManagerView
-import com.petterp.floatingx.view.FxViewHolder
-import java.lang.ref.WeakReference
-
-/** Fx基础控制器实现 */
-open class FxBasisControlImpl(private val helper: BasisHelper) : IFxControl, IFxConfigControl {
- private var managerView: FxManagerView? = null
- private var viewHolder: FxViewHolder? = null
- private var mContainer: WeakReference? = null
- private val cancelAnimationRunnable by lazyLoad { Runnable { reset() } }
- private val hideAnimationRunnable by lazyLoad { Runnable { detach() } }
-
- /*
- * 控制接口相关实现
- * */
- override val configControl: IFxConfigControl get() = this
-
- override fun cancel() {
- if ((managerView == null && viewHolder == null)) return
- if (isShow() && helper.enableAnimation && helper.fxAnimation != null) {
- managerView?.removeCallbacks(cancelAnimationRunnable)
- val duration = helper.fxAnimation!!.toEndAnimator(managerView)
- animatorCallback(duration, cancelAnimationRunnable)
- } else {
- reset()
- }
- }
-
- override fun hide() {
- if (!isShow()) return
- updateEnableStatus(false)
- if (helper.enableAnimation && helper.fxAnimation != null) {
- if (helper.fxAnimation!!.endJobIsRunning()) {
- helper.fxLog?.d("fxView->Animation ,endAnimation Executing, cancel this operation!")
- return
- }
- helper.fxLog?.d("fxView->Animation ,endAnimation Running")
- managerView?.removeCallbacks(hideAnimationRunnable)
- val duration = helper.fxAnimation!!.toEndAnimator(managerView)
- animatorCallback(duration, hideAnimationRunnable)
- } else {
- detach()
- }
- }
-
- override fun isShow(): Boolean =
- managerView != null && ViewCompat.isAttachedToWindow(managerView!!) && managerView!!.visibility == View.VISIBLE
-
- override fun getView(): View? = managerView?.childFxView
-
- override fun getViewHolder(): FxViewHolder? = viewHolder
-
- override fun getManagerView(): FxManagerView? = managerView
-
- override fun updateView(@LayoutRes resource: Int) {
- if (resource == 0) throw IllegalArgumentException("resource cannot be 0!")
- helper.layoutView = null
- updateMangerView(resource)
- }
-
- override fun updateView(view: View) {
- helper.layoutView = view
- updateMangerView(0)
- }
-
- override fun updateView(provider: IFxContextProvider) {
- updateView(provider.build(context()))
- }
-
- override fun updateViewContent(provider: IFxHolderProvider) {
- provider.apply(viewHolder ?: return)
- }
-
- override fun setClickListener(time: Long, clickListener: View.OnClickListener) {
- helper.clickTime = time
- helper.enableClickListener = true
- helper.iFxClickListener = clickListener
- }
-
- override fun setClickListener(clickListener: View.OnClickListener) {
- setClickListener(0, clickListener)
- }
-
- override fun move(x: Float, y: Float) {
- move(x, y, true)
- }
-
- override fun moveByVector(x: Float, y: Float) {
- moveByVector(x, y, true)
- }
-
- override fun move(x: Float, y: Float, useAnimation: Boolean) {
- managerView?.moveLocation(x, y, useAnimation)
- }
-
- override fun moveByVector(x: Float, y: Float, useAnimation: Boolean) {
- managerView?.moveLocationByVector(x, y, useAnimation)
- }
-
- /*
- * config配置相关接口实现
- * */
- override fun setEnableClick(isEnable: Boolean) {
- helper.enableClickListener = isEnable
- }
-
- override fun setEnableAnimation(isEnable: Boolean, animationImpl: FxAnimation) {
- helper.enableAnimation = isEnable
- helper.fxAnimation = animationImpl
- }
-
- override fun setEnableAnimation(isEnable: Boolean) {
- helper.enableAnimation = isEnable
- }
-
- override fun setBorderMargin(t: Float, l: Float, b: Float, r: Float) {
- helper.fxBorderMargin.apply {
- this.t = t
- this.l = l
- this.b = b
- this.r = r
- }
- managerView?.moveToEdge()
- }
-
- override fun setEnableEdgeAdsorption(isEnable: Boolean) {
- helper.enableEdgeAdsorption = isEnable
- managerView?.moveToEdge()
- }
-
- override fun setEdgeAdsorbDirection(direction: FxAdsorbDirection) {
- helper.adsorbDirection = direction
- managerView?.moveToEdge()
- }
-
- override fun setEdgeOffset(edgeOffset: Float) {
- helper.edgeOffset = edgeOffset
- managerView?.moveToEdge()
- }
-
- override fun setEnableEdgeRebound(isEnable: Boolean) {
- helper.enableEdgeRebound = isEnable
- managerView?.moveToEdge()
- }
-
- override fun setScrollListener(listener: IFxScrollListener) {
- helper.iFxScrollListener = listener
- }
-
- override fun setViewLifecycleListener(listener: IFxViewLifecycle) {
- helper.iFxViewLifecycle = listener
- }
-
- override fun setEnableSaveDirection(impl: IFxConfigStorage, isEnable: Boolean) {
- helper.iFxConfigStorage = impl
- helper.enableSaveDirection = isEnable
- }
-
- override fun setEnableSaveDirection(isEnable: Boolean) {
- helper.enableSaveDirection = isEnable
- }
-
- override fun clearLocationStorage() {
- helper.iFxConfigStorage?.clear()
- }
-
- override fun setEnableTouch(isEnable: Boolean) {
- val mode = if (isEnable) FxDisplayMode.Normal else FxDisplayMode.ClickOnly
- setDisplayMode(mode)
- }
-
- override fun setDisplayMode(mode: FxDisplayMode) {
- helper.displayMode = mode
- managerView?.updateDisplayMode()
- }
-
- /*
- * 以下方法作为基础实现,供子类自行调用
- * */
- protected open fun updateMangerView(@LayoutRes layout: Int = 0) {
- helper.layoutId = layout
- if (getContainerGroup() == null) throw NullPointerException("FloatingX window The parent container cannot be null!")
- val x = managerView?.x ?: 0f
- val y = managerView?.y ?: 0f
- initManagerView()
- managerView?.restoreLocation(x, y)
- getContainerGroup()?.addView(managerView)
- }
-
- protected fun initManagerView() {
- if (helper.layoutId == 0 && helper.layoutView == null) throw RuntimeException("The layout id cannot be 0 ,and layoutView==null")
- getContainerGroup()?.removeView(managerView)
- initManager()
- }
-
- protected open fun initManager() {
- val context = context() ?: return
- managerView = FxManagerView(context).init(helper)
- val fxContentView = managerView?.childFxView ?: return
- viewHolder = FxViewHolder(fxContentView)
- val fxViewLifecycle = helper.iFxViewLifecycle ?: return
- // 后续此方法将会移除,建议进行过渡
- fxViewLifecycle.initView(fxContentView)
- fxViewLifecycle.initView(viewHolder!!)
- }
-
- protected fun getOrInitManagerView(): FxManagerView? {
- if (managerView == null) initManagerView()
- return managerView
- }
-
- protected fun getContainerGroup(): ViewGroup? {
- return mContainer?.get()
- }
-
- protected open fun detach(container: ViewGroup?) {
- if (managerView != null && container != null) {
- helper.fxLog?.d("fxView-lifecycle-> code->removeView")
- helper.iFxViewLifecycle?.postDetached()
- container.removeView(managerView)
- }
- }
-
- protected fun detach() {
- val containerGroup = getContainerGroup() ?: return
- detach(containerGroup)
- }
-
- protected open fun context(): Context? {
- val context = mContainer?.get()?.context
- if (context == null) {
- helper.fxLog?.e("context = null,check your rule!")
- }
- return context
- }
-
- protected fun clearContainer() {
- mContainer?.clear()
- mContainer = null
- }
-
- @JvmSynthetic
- protected fun FxManagerView.show() {
- helper.enableFx = true
- visibility = View.VISIBLE
- val fxAnimation = helper.fxAnimation ?: return
- if (helper.enableAnimation) {
- if (fxAnimation.fromJobIsRunning()) {
- helper.fxLog?.d("fxView->Animation ,startAnimation Executing, cancel this operation!")
- return
- }
- helper.fxLog?.d("fxView->Animation ,startAnimation Executing, cancel this operation.")
- fxAnimation.fromStartAnimator(this)
- }
- }
-
- @JvmSynthetic
- protected open fun reset() {
- managerView?.removeCallbacks(hideAnimationRunnable)
- managerView?.removeCallbacks(cancelAnimationRunnable)
- detach(mContainer?.get())
- managerView = null
- viewHolder = null
- helper.clear()
- clearContainer()
- helper.fxLog?.d("fxView-lifecycle-> code->cancelFx")
- }
-
- /** 更新启用状态 */
- protected fun updateEnableStatus(newStatus: Boolean) {
- if (helper.enableFx == newStatus) return
- helper.enableFx = newStatus
- }
-
- internal fun setContainerGroup(viewGroup: ViewGroup) {
- mContainer = WeakReference(viewGroup)
- }
-
- private fun animatorCallback(long: Long, runnable: Runnable) {
- val magnetView = managerView ?: return
- magnetView.removeCallbacks(runnable)
- magnetView.postDelayed(runnable, long)
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxScopeControl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxScopeControl.kt
deleted file mode 100644
index 1f926fd3..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/control/FxScopeControl.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.petterp.floatingx.impl.control
-
-import android.app.Application
-import android.view.View
-import androidx.core.view.ViewCompat
-import com.petterp.floatingx.assist.helper.BasisHelper
-import com.petterp.floatingx.listener.control.IFxScopeControl
-
-/** Fx普通View控制器 */
-class FxScopeControl(helper: BasisHelper) :
- FxBasisControlImpl(helper),
- IFxScopeControl {
-
- override fun show() {
- if (isShow()) return
- val managerView = getOrInitManagerView() ?: return
- updateEnableStatus(true)
- if (!ViewCompat.isAttachedToWindow(managerView)) {
- getContainerGroup()?.addView(managerView)
- }
- managerView.show()
- }
-
- override fun updateView(view: View) {
- if (view.context is Application) {
- throw IllegalArgumentException("view == Application,Scope floating windows cannot use application-level views!")
- }
- super.updateView(view)
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxAppLifecycleProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxAppLifecycleProvider.kt
new file mode 100644
index 00000000..7deec65f
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxAppLifecycleProvider.kt
@@ -0,0 +1,48 @@
+package com.petterp.floatingx.impl.lifecycle
+
+import android.app.Activity
+import android.app.Application
+import android.os.Bundle
+import java.lang.ref.WeakReference
+
+/**
+ *
+ * @author petterp
+ */
+class FxAppLifecycleProvider : Application.ActivityLifecycleCallbacks {
+ override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
+ updateTopActivity(activity)
+ }
+
+ override fun onActivityStarted(activity: Activity) {
+ }
+
+ override fun onActivityResumed(activity: Activity) {
+ updateTopActivity(activity)
+ }
+
+ override fun onActivityPaused(activity: Activity) {
+ }
+
+ override fun onActivityStopped(activity: Activity) {
+ }
+
+ override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
+ }
+
+ override fun onActivityDestroyed(activity: Activity) {
+ }
+
+ companion object {
+ private var _currentActivity: WeakReference? = null
+
+ @JvmSynthetic
+ fun getTopActivity(): Activity? = _currentActivity?.get()
+
+ @JvmSynthetic
+ internal fun updateTopActivity(activity: Activity?) {
+ if (activity == null || _currentActivity?.get() === activity) return
+ _currentActivity = WeakReference(activity)
+ }
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxLifecycleCallbackImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxLifecycleCallbackImpl.kt
deleted file mode 100644
index ca530702..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxLifecycleCallbackImpl.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.petterp.floatingx.impl.lifecycle
-
-import android.app.Activity
-import android.app.Application
-import android.os.Bundle
-import com.petterp.floatingx.FloatingX
-import com.petterp.floatingx.impl.control.FxAppControlImpl
-import java.lang.ref.WeakReference
-
-/** App-lifecycle-CallBack */
-class FxLifecycleCallbackImpl : Application.ActivityLifecycleCallbacks {
-
- private val fxList: Collection
- get() = FloatingX.getFxList().values
-
- override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivityCreated(activity, savedInstanceState)
- }
- }
-
- override fun onActivityStarted(activity: Activity) {
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivityStarted(activity)
- }
- }
-
- /** 最开始想到在onActivityPostStarted后插入, 但是最后发现在Android9及以下,此方法不会被调用,故选择了onResume */
- override fun onActivityResumed(activity: Activity) {
- updateTopActivity(activity)
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivityResumed(activity)
- }
- }
-
- override fun onActivityPaused(activity: Activity) {
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivityPaused(activity)
- }
- }
-
- override fun onActivityStopped(activity: Activity) {
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivityStopped(activity)
- }
- }
-
- override fun onActivityDestroyed(activity: Activity) {
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivityDestroyed(activity)
- }
- if (topActivity?.get() === activity) {
- topActivity?.clear()
- topActivity = null
- }
- }
-
- override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
- if (isFxsNotAllow()) return
- for (fx in fxList) {
- fx.onActivitySaveInstanceState(activity, outState)
- }
- }
-
- private fun isFxsNotAllow(): Boolean {
- return fxList.isEmpty()
- }
-
- private fun updateTopActivity(activity: Activity) {
- if (topActivity?.get() != activity) topActivity = WeakReference(activity)
- }
-
- companion object {
- internal var topActivity: WeakReference? = null
-
- @JvmSynthetic
- fun getTopActivity(): Activity? = topActivity?.get()
-
- @JvmSynthetic
- internal fun updateTopActivity(activity: Activity?) {
- if (activity == null || topActivity?.get() === activity) return
- topActivity = WeakReference(activity)
- }
-
- @JvmSynthetic
- internal fun releaseTopActivity() {
- topActivity?.clear()
- topActivity = null
- }
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxTagActivityLifecycleImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxProxyTagLifecycleImp.kt
similarity index 89%
rename from floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxTagActivityLifecycleImpl.kt
rename to floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxProxyTagLifecycleImp.kt
index 5610983e..8634c8d7 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxTagActivityLifecycleImpl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxProxyTagLifecycleImp.kt
@@ -5,7 +5,7 @@ import android.os.Bundle
import com.petterp.floatingx.listener.IFxProxyTagActivityLifecycle
/** IFxProxyTagActivityLifecycle的空实现 */
-open class FxTagActivityLifecycleImpl : IFxProxyTagActivityLifecycle {
+open class FxProxyTagLifecycleImp : IFxProxyTagActivityLifecycle {
override fun onCreated(activity: Activity, bundle: Bundle?) {}
override fun onStarted(activity: Activity) {}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxProxyLifecycleCallBackImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxTempAppLifecycleImp.kt
similarity index 67%
rename from floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxProxyLifecycleCallBackImpl.kt
rename to floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxTempAppLifecycleImp.kt
index 73c2bfdc..85240978 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxProxyLifecycleCallBackImpl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/lifecycle/FxTempAppLifecycleImp.kt
@@ -3,8 +3,8 @@ package com.petterp.floatingx.impl.lifecycle
import android.app.Activity
import android.app.Application
import android.os.Bundle
-import com.petterp.floatingx.assist.helper.AppHelper
-import com.petterp.floatingx.impl.control.FxAppControlImpl
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.impl.provider.app.FxAppControlImp
import com.petterp.floatingx.listener.IFxProxyTagActivityLifecycle
import com.petterp.floatingx.util.FxLog
import com.petterp.floatingx.util.decorView
@@ -15,19 +15,19 @@ import com.petterp.floatingx.util.lazyLoad
*
* @author petterp
*/
-class FxProxyLifecycleCallBackImpl : Application.ActivityLifecycleCallbacks {
+class FxTempAppLifecycleImp(
+ private val helper: FxAppHelper,
+ private val appControl: FxAppControlImp
+) : Application.ActivityLifecycleCallbacks {
- private var helper: AppHelper? = null
- private var appControl: FxAppControlImpl? = null
-
- private val fxLog: FxLog?
- get() = helper?.fxLog
+ private val fxLog: FxLog
+ get() = helper.fxLog
private val enableFx: Boolean
- get() = helper?.enableFx ?: false
+ get() = helper.enableFx
private val appLifecycleCallBack: IFxProxyTagActivityLifecycle?
- get() = helper?.fxLifecycleExpand
+ get() = helper.fxLifecycleExpand
private val insertCls by lazyLoad {
mutableMapOf, Boolean>()
@@ -36,7 +36,7 @@ class FxProxyLifecycleCallBackImpl : Application.ActivityLifecycleCallbacks {
private val Activity.name: String
get() = javaClass.name.split(".").last()
private val Activity.isParent: Boolean
- get() = appControl?.getManagerView()?.parent === decorView
+ get() = appControl.getManagerView()?.parent === decorView
private val Activity.isActivityInValid: Boolean
get() {
@@ -44,12 +44,6 @@ class FxProxyLifecycleCallBackImpl : Application.ActivityLifecycleCallbacks {
return insertCls[cls] ?: isInsertActivity(cls)
}
- /** 初始化helper与app控制器 */
- fun init(helper: AppHelper, control: FxAppControlImpl) {
- this.helper = helper
- this.appControl = control
- }
-
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
if (!enableFx) return
if (appLifecycleCallBack != null && activity.isActivityInValid) {
@@ -59,30 +53,27 @@ class FxProxyLifecycleCallBackImpl : Application.ActivityLifecycleCallbacks {
override fun onActivityStarted(activity: Activity) {
if (!enableFx) return
- helper?.fxLifecycleExpand?.onStarted(activity)
+ helper.fxLifecycleExpand?.onStarted(activity)
}
/** 最开始想到在onActivityPostStarted后插入, 但是最后发现在Android9及以下,此方法不会被调用,故选择了onResume */
override fun onActivityResumed(activity: Activity) {
if (!enableFx) return
val activityName = activity.name
- fxLog?.d("fxApp->insert, insert [$activityName] Start ---------->")
+ fxLog.d("fxApp->insert, insert [$activityName] Start ---------->")
val isActivityInValid = activity.isActivityInValid
if (isActivityInValid) {
appLifecycleCallBack?.onResumes(activity)
} else {
- fxLog?.d("fxApp->insert, insert [$activityName] Fail ,This activity is not in the list of allowed inserts.")
+ fxLog.d("fxApp->insert, insert [$activityName] Fail ,This activity is not in the list of allowed inserts.")
return
}
val isParent = activity.isParent
if (isParent) {
- fxLog?.d("fxApp->insert, insert [$activityName] Fail ,The current Activity has been inserted.")
+ fxLog.d("fxApp->insert, insert [$activityName] Fail ,The current Activity has been inserted.")
return
}
- appControl?.let {
- it.attach(activity)
- fxLog?.d("fxApp->insert, insert [$activityName] Success--------------->")
- } ?: fxLog?.d("fxApp->insert, insert [$activityName] Fail ,appControl = null.")
+ appControl.reAttach(activity)
}
override fun onActivityPaused(activity: Activity) {
@@ -105,8 +96,8 @@ class FxProxyLifecycleCallBackImpl : Application.ActivityLifecycleCallbacks {
if (activity.isActivityInValid) it.onDestroyed(activity)
}
val isParent = activity.isParent
- fxLog?.d("fxApp->detach? isContainActivity-${activity.isActivityInValid}--enableFx-$enableFx---isParent-$isParent")
- if (isParent) appControl?.detach(activity)
+ fxLog.d("fxApp->detach? isContainActivity-${activity.isActivityInValid}--enableFx-$enableFx---isParent-$isParent")
+ if (isParent) appControl.destroyToDetach(activity)
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
@@ -117,10 +108,10 @@ class FxProxyLifecycleCallBackImpl : Application.ActivityLifecycleCallbacks {
}
private fun isInsertActivity(cls: Class<*>): Boolean =
- helper?.let {
+ helper.let {
// 条件 允许全局安装&&不在黑名单 || 禁止全局安装&&在白名单
val isInsert = it.isCanInstall(cls)
insertCls[cls] = isInsert
isInsert
- } ?: false
+ }
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasicAnimationProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasicAnimationProvider.kt
new file mode 100644
index 00000000..b92e0ca1
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasicAnimationProvider.kt
@@ -0,0 +1,46 @@
+package com.petterp.floatingx.impl.provider
+
+import android.widget.FrameLayout
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+import com.petterp.floatingx.listener.provider.IFxAnimationProvider
+
+/**
+ *
+ * @author petterp
+ */
+class FxBasicAnimationProvider(override val helper: F) : IFxAnimationProvider {
+
+ override fun start(view: FrameLayout, obj: (() -> Unit)?) {
+ val fxAnimation = helper.fxAnimation ?: return
+ if (fxAnimation.fromJobIsRunning()) {
+ helper.fxLog.d("fxView -> Animation,startAnimation Executing, cancel this operation!")
+ return
+ }
+ helper.fxLog.d("fxView -> Animation,startAnimation Running.")
+ fxAnimation.setFromAnimatorListener(obj)
+ fxAnimation.fromStartAnimator(view)
+ }
+
+ override fun hide(view: FrameLayout, obj: (() -> Unit)?) {
+ val fxAnimation = helper.fxAnimation ?: return
+ if (helper.fxAnimation!!.endJobIsRunning()) {
+ helper.fxLog.d("fxView -> Animation,endAnimation Executing, cancel this operation!")
+ return
+ }
+ helper.fxLog.d("fxView -> Animation,endAnimation Running.")
+ fxAnimation.setEndAnimatorListener(obj)
+ fxAnimation.toEndAnimator(view)
+ }
+
+ override fun canRunAnimation(): Boolean {
+ return helper.enableAnimation && helper.fxAnimation != null
+ }
+
+ override fun canCancelAnimation(): Boolean {
+ return helper.enableAnimation && helper.fxAnimation != null
+ }
+
+ override fun reset() {
+ helper.fxAnimation?.cancelAnimation()
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasicConfigProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasicConfigProvider.kt
new file mode 100644
index 00000000..c6466f0d
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasicConfigProvider.kt
@@ -0,0 +1,98 @@
+package com.petterp.floatingx.impl.provider
+
+import com.petterp.floatingx.assist.FxAdsorbDirection
+import com.petterp.floatingx.assist.FxAnimation
+import com.petterp.floatingx.assist.FxDisplayMode
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+import com.petterp.floatingx.listener.IFxConfigStorage
+import com.petterp.floatingx.listener.IFxScrollListener
+import com.petterp.floatingx.listener.IFxViewLifecycle
+import com.petterp.floatingx.listener.control.IFxConfigControl
+import com.petterp.floatingx.listener.provider.IFxPlatformProvider
+import com.petterp.floatingx.view.IFxInternalView
+
+/**
+ * Fx基础配置更改 提供者
+ * @author petterp
+ */
+class FxBasicConfigProvider>(
+ private val helper: F,
+ private var p: P?
+) : IFxConfigControl {
+
+ private val internalView: IFxInternalView?
+ get() = p?.internalView
+
+ override fun setEnableClick(isEnable: Boolean) {
+ helper.enableClickListener = isEnable
+ }
+
+ override fun setEnableAnimation(isEnable: Boolean, animationImpl: FxAnimation) {
+ helper.enableAnimation = isEnable
+ helper.fxAnimation = animationImpl
+ }
+
+ override fun setEnableAnimation(isEnable: Boolean) {
+ helper.enableAnimation = isEnable
+ }
+
+ override fun setBorderMargin(t: Float, l: Float, b: Float, r: Float) {
+ helper.fxBorderMargin.apply {
+ this.t = t
+ this.l = l
+ this.b = b
+ this.r = r
+ }
+ internalView?.moveToEdge()
+ }
+
+ override fun setEnableEdgeAdsorption(isEnable: Boolean) {
+ helper.enableEdgeAdsorption = isEnable
+ internalView?.moveToEdge()
+ }
+
+ override fun setEdgeAdsorbDirection(direction: FxAdsorbDirection) {
+ helper.adsorbDirection = direction
+ internalView?.moveToEdge()
+ }
+
+ override fun setEdgeOffset(edgeOffset: Float) {
+ helper.edgeOffset = edgeOffset
+ internalView?.moveToEdge()
+ }
+
+ override fun setEnableEdgeRebound(isEnable: Boolean) {
+ helper.enableEdgeRebound = isEnable
+ internalView?.moveToEdge()
+ }
+
+ override fun setScrollListener(listener: IFxScrollListener) {
+ helper.iFxScrollListener = listener
+ }
+
+ override fun setViewLifecycleListener(listener: IFxViewLifecycle) {
+ helper.iFxViewLifecycle = listener
+ }
+
+ override fun setEnableSaveDirection(impl: IFxConfigStorage, isEnable: Boolean) {
+ helper.iFxConfigStorage = impl
+ helper.enableSaveDirection = isEnable
+ }
+
+ override fun setEnableSaveDirection(isEnable: Boolean) {
+ helper.enableSaveDirection = isEnable
+ }
+
+ override fun clearLocationStorage() {
+ helper.iFxConfigStorage?.clear()
+ }
+
+ override fun setEnableTouch(isEnable: Boolean) {
+ val mode = if (isEnable) FxDisplayMode.Normal else FxDisplayMode.ClickOnly
+ setDisplayMode(mode)
+ }
+
+ override fun setDisplayMode(mode: FxDisplayMode) {
+ helper.displayMode = mode
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasisControlImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasisControlImpl.kt
new file mode 100644
index 00000000..b12b05d4
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/FxBasisControlImpl.kt
@@ -0,0 +1,147 @@
+package com.petterp.floatingx.impl.provider
+
+import android.view.View
+import androidx.annotation.LayoutRes
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+import com.petterp.floatingx.listener.control.IFxConfigControl
+import com.petterp.floatingx.listener.control.IFxControl
+import com.petterp.floatingx.listener.provider.IFxAnimationProvider
+import com.petterp.floatingx.listener.provider.IFxContextProvider
+import com.petterp.floatingx.listener.provider.IFxHolderProvider
+import com.petterp.floatingx.listener.provider.IFxPlatformProvider
+import com.petterp.floatingx.util.INVALID_LAYOUT_ID
+import com.petterp.floatingx.view.IFxInternalView
+
+/**
+ * Fx基础控制器,用于协调各provider的分发
+ * */
+abstract class FxBasisControlImpl>(
+ protected val helper: F
+) : IFxControl {
+
+ protected lateinit var platformProvider: P
+ private lateinit var _configControl: IFxConfigControl
+ private lateinit var _animationProvider: IFxAnimationProvider
+ private val internalView: IFxInternalView?
+ get() = platformProvider.internalView
+
+ override val configControl: IFxConfigControl get() = _configControl
+ override fun getView() = internalView?.childView
+ override fun getViewHolder() = internalView?.viewHolder
+ override fun getManagerView() = internalView?.containerView
+
+ abstract fun createPlatformProvider(f: F): P
+ open fun createConfigProvider(f: F, p: P): IFxConfigControl = FxBasicConfigProvider(f, p)
+ open fun createAnimationProvider(f: F, p: P): IFxAnimationProvider = FxBasicAnimationProvider(f)
+
+ fun initProvider() {
+ platformProvider = createPlatformProvider(helper)
+ _animationProvider = createAnimationProvider(helper, platformProvider)
+ _configControl = createConfigProvider(helper, platformProvider)
+ }
+
+ override fun show() {
+ if (isShow()) return
+ if (!platformProvider.checkOrInit()) return
+ val fxView = getManagerView() ?: return
+ updateEnableStatus(true)
+ platformProvider.show()
+ if (_animationProvider.canRunAnimation()) {
+ _animationProvider.start(fxView)
+ }
+ }
+
+ override fun hide() {
+ if (!isShow()) return
+ val fxView = getManagerView() ?: return
+ updateEnableStatus(false)
+ if (_animationProvider.canCancelAnimation()) {
+ _animationProvider.hide(fxView) {
+ platformProvider.hide()
+ }
+ } else {
+ platformProvider.hide()
+ }
+ }
+
+ override fun cancel() {
+ val fxView = getManagerView() ?: return
+ if (isShow() && _animationProvider.canRunAnimation()) {
+ _animationProvider.hide(fxView) {
+ reset()
+ }
+ } else {
+ reset()
+ }
+ }
+
+ override fun isShow(): Boolean {
+ val interView = getManagerView() ?: return false
+ val isShow = platformProvider.isShow()
+ if (isShow != null) return isShow
+ return interView.isAttachedToWindow && interView.visibility == View.VISIBLE
+ }
+
+ override fun updateView(@LayoutRes resource: Int) {
+ check(resource != INVALID_LAYOUT_ID) { "resource cannot be INVALID_LAYOUT_ID!" }
+ helper.layoutView = null
+ helper.layoutId = resource
+ internalView?.updateView()
+ }
+
+ override fun updateView(view: View) {
+ helper.layoutId = INVALID_LAYOUT_ID
+ helper.layoutView = view
+ internalView?.updateView()
+ }
+
+ override fun updateView(provider: IFxContextProvider) {
+ updateView(provider.build(platformProvider.context))
+ }
+
+ override fun updateViewContent(provider: IFxHolderProvider) {
+ provider.apply(getViewHolder() ?: return)
+ }
+
+ override fun setClickListener(time: Long, clickListener: View.OnClickListener) {
+ helper.clickTime = time
+ helper.enableClickListener = true
+ helper.iFxClickListener = clickListener
+ }
+
+ override fun setClickListener(clickListener: View.OnClickListener) {
+ setClickListener(0, clickListener)
+ }
+
+ override fun move(x: Float, y: Float) {
+ move(x, y, true)
+ }
+
+ override fun moveByVector(x: Float, y: Float) {
+ moveByVector(x, y, true)
+ }
+
+ override fun move(x: Float, y: Float, useAnimation: Boolean) {
+ internalView?.moveLocation(x, y, useAnimation)
+ }
+
+ override fun moveByVector(x: Float, y: Float, useAnimation: Boolean) {
+ internalView?.moveLocationByVector(x, y, useAnimation)
+ }
+
+ override fun updateConfig(obj: IFxConfigControl.() -> Unit) {
+ obj.invoke(_configControl)
+ }
+
+ protected open fun reset() {
+ platformProvider.reset()
+ _animationProvider.reset()
+ helper.clear()
+ helper.fxLog.d("fxView-lifecycle-> code->cancelFx")
+ }
+
+ private fun updateEnableStatus(newStatus: Boolean) {
+ if (helper.enableFx == newStatus) return
+ helper.enableFx = newStatus
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/app/FxAppControlImp.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/app/FxAppControlImp.kt
new file mode 100644
index 00000000..ec2406fa
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/app/FxAppControlImp.kt
@@ -0,0 +1,51 @@
+package com.petterp.floatingx.impl.provider.app
+
+import android.app.Activity
+import android.app.Application
+import android.view.View
+import com.petterp.floatingx.FloatingX
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.impl.lifecycle.FxTempAppLifecycleImp
+import com.petterp.floatingx.impl.provider.FxBasisControlImpl
+import com.petterp.floatingx.listener.control.IFxAppControl
+import com.petterp.floatingx.util.decorView
+import com.petterp.floatingx.util.topActivity
+
+/** 全局控制器 */
+class FxAppControlImp(helper: FxAppHelper) : FxBasisControlImpl(helper), IFxAppControl {
+
+ override fun createPlatformProvider(f: FxAppHelper) =
+ FxAppPlatformProvider(f, FxTempAppLifecycleImp(f, this))
+
+ override fun getBindActivity(): Activity? {
+ val groupView = getManagerView()?.parent ?: return null
+ if (groupView === topActivity?.decorView) {
+ return topActivity
+ }
+ return null
+ }
+
+ override fun updateView(view: View) {
+ check(view.context is Application) {
+ "view.context != Application,The global floating window must use application as context!"
+ }
+ super.updateView(view)
+ }
+
+ @JvmSynthetic
+ internal fun reAttach(activity: Activity) {
+ if (!platformProvider.reAttach(activity)) return
+ show()
+ }
+
+ @JvmSynthetic
+ internal fun destroyToDetach(activity: Activity) {
+ platformProvider.destroyToDetach(activity)
+ }
+
+ override fun reset() {
+ super.reset()
+ if (!FloatingX.fxs.containsValue(this)) return
+ FloatingX.fxs.remove(helper.tag)
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/app/FxAppPlatformProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/app/FxAppPlatformProvider.kt
new file mode 100644
index 00000000..9cf6ba23
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/app/FxAppPlatformProvider.kt
@@ -0,0 +1,148 @@
+package com.petterp.floatingx.impl.provider.app
+
+import android.app.Activity
+import android.app.Application
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.view.OnApplyWindowInsetsListener
+import androidx.core.view.ViewCompat
+import com.petterp.floatingx.assist.FxScopeType
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.impl.lifecycle.FxTempAppLifecycleImp
+import com.petterp.floatingx.listener.provider.IFxPlatformProvider
+import com.petterp.floatingx.util.decorView
+import com.petterp.floatingx.util.topActivity
+import com.petterp.floatingx.view.FxDefaultContainerView
+import com.petterp.floatingx.view.IFxInternalView
+import java.lang.ref.WeakReference
+
+/**
+ * 免权限的浮窗提供者
+ * @author petterp
+ */
+class FxAppPlatformProvider(
+ override val helper: FxAppHelper,
+ private val proxyLifecycleImpl: FxTempAppLifecycleImp,
+) : IFxPlatformProvider, Application.ActivityLifecycleCallbacks by proxyLifecycleImpl {
+
+ private var isRegisterAppLifecycle = false
+ private var _internalView: FxDefaultContainerView? = null
+ private var _containerGroup: WeakReference? = null
+
+ private val windowsInsetsListener = OnApplyWindowInsetsListener { _, insets ->
+ val statusBar = insets.stableInsetTop
+ if (helper.statsBarHeight != statusBar) {
+ helper.fxLog.v("System--StatusBar---old-(${helper.statsBarHeight}),new-($statusBar))")
+ helper.statsBarHeight = statusBar
+ }
+ insets
+ }
+
+ init {
+ checkRegisterAppLifecycle()
+ }
+
+ private val containerGroupView: ViewGroup?
+ get() = _containerGroup?.get()
+
+ override val context: Context
+ get() = helper.context
+ override val internalView: IFxInternalView?
+ get() = _internalView
+
+ override fun checkOrInit(): Boolean {
+ val act = topActivity ?: return false
+ if (!helper.isCanInstall(act)) return false
+ if (_internalView == null) {
+ initWindowsInsetsListener()
+ helper.updateNavigationBar(act)
+ helper.updateStatsBar(act)
+ _internalView = FxDefaultContainerView(helper, helper.context)
+ _internalView?.initView()
+ checkRegisterAppLifecycle()
+ attach(act)
+ }
+ return true
+ }
+
+ override fun show() {
+ val fxView = _internalView ?: return
+ if (!ViewCompat.isAttachedToWindow(fxView)) {
+ fxView.visibility = View.VISIBLE
+ containerGroupView?.addView(fxView)
+ }
+ }
+
+ override fun hide() {
+ detach()
+ }
+
+ private fun attach(activity: Activity): Boolean {
+ val fxView = _internalView ?: return false
+ val decorView = activity.decorView ?: return false
+ if (containerGroupView === decorView) return false
+ if (ViewCompat.isAttachedToWindow(fxView)) containerGroupView?.removeView(fxView)
+ _containerGroup = WeakReference(decorView)
+ helper.fxLog.d("fxView-lifecycle-> onPostAttach")
+ helper.iFxViewLifecycle?.postAttach()
+ decorView.addView(fxView)
+ return true
+ }
+
+ fun reAttach(activity: Activity): Boolean {
+ val nContainer = activity.decorView ?: return false
+ if (_internalView == null) {
+ _containerGroup = WeakReference(nContainer)
+ return true
+ } else {
+ if (nContainer === containerGroupView) return false
+ containerGroupView?.removeView(_internalView)
+ nContainer.addView(_internalView)
+ _containerGroup = WeakReference(nContainer)
+ }
+ return false
+ }
+
+ fun destroyToDetach(activity: Activity): Boolean {
+ val fxView = _internalView ?: return false
+ val oldContainer = containerGroupView ?: return false
+ if (!ViewCompat.isAttachedToWindow(fxView)) return false
+ val nContainer = activity.decorView ?: return false
+ if (nContainer !== oldContainer) return false
+ oldContainer.removeView(_internalView)
+ return true
+ }
+
+ override fun reset() {
+ clearWindowsInsetsListener()
+ _internalView = null
+ _containerGroup?.clear()
+ _containerGroup = null
+ }
+
+ private fun detach() {
+ helper.fxLog.d("fxView-lifecycle-> onPostDetach")
+ _internalView?.visibility = View.GONE
+ containerGroupView?.removeView(_internalView)
+ }
+
+ private fun initWindowsInsetsListener() {
+ val fxView = _internalView ?: return
+ ViewCompat.setOnApplyWindowInsetsListener(fxView, windowsInsetsListener)
+ fxView.requestApplyInsets()
+ }
+
+ private fun checkRegisterAppLifecycle() {
+ if (!isRegisterAppLifecycle && helper.enableFx && helper.scope == FxScopeType.APP_ACTIVITY) {
+ isRegisterAppLifecycle = true
+ helper.context.unregisterActivityLifecycleCallbacks(this)
+ helper.context.registerActivityLifecycleCallbacks(this)
+ }
+ }
+
+ private fun clearWindowsInsetsListener() {
+ val managerView = _internalView ?: return
+ ViewCompat.setOnApplyWindowInsetsListener(managerView, null)
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/scope/FxScopeControl.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/scope/FxScopeControl.kt
new file mode 100644
index 00000000..ff5a981b
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/scope/FxScopeControl.kt
@@ -0,0 +1,26 @@
+package com.petterp.floatingx.impl.provider.scope
+
+import android.app.Application
+import android.view.View
+import android.view.ViewGroup
+import com.petterp.floatingx.assist.helper.FxScopeHelper
+import com.petterp.floatingx.impl.provider.FxBasisControlImpl
+import com.petterp.floatingx.listener.control.IFxScopeControl
+
+/** Fx普通View控制器 */
+class FxScopeControl(helper: FxScopeHelper) :
+ FxBasisControlImpl(helper), IFxScopeControl {
+
+ override fun createPlatformProvider(f: FxScopeHelper) = FxScopePlatFromProvider(f)
+
+ fun setContainerGroup(viewGroup: ViewGroup) {
+ platformProvider.setContainerGroup(viewGroup)
+ }
+
+ override fun updateView(view: View) {
+ check(view.context !is Application) {
+ "view = Application,Scope floating windows cannot use application-level views!"
+ }
+ super.updateView(view)
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/scope/FxScopePlatFromProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/scope/FxScopePlatFromProvider.kt
new file mode 100644
index 00000000..b479cfe2
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/scope/FxScopePlatFromProvider.kt
@@ -0,0 +1,58 @@
+package com.petterp.floatingx.impl.provider.scope
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import com.petterp.floatingx.assist.helper.FxScopeHelper
+import com.petterp.floatingx.listener.provider.IFxPlatformProvider
+import com.petterp.floatingx.view.IFxInternalView
+import com.petterp.floatingx.view.FxDefaultContainerView
+import java.lang.ref.WeakReference
+
+/**
+ * Fx局部控制器
+ * @author petterp
+ */
+class FxScopePlatFromProvider(
+ override val helper: FxScopeHelper,
+) : IFxPlatformProvider {
+
+ private var _internalView: FxDefaultContainerView? = null
+ private var _containerGroup: WeakReference? = null
+
+ private val containerGroupView: ViewGroup?
+ get() = _containerGroup?.get()
+ override val context: Context?
+ get() = containerGroupView?.context
+
+ override val internalView: IFxInternalView?
+ get() = _internalView
+
+ fun setContainerGroup(viewGroup: ViewGroup) {
+ _containerGroup = WeakReference(viewGroup)
+ }
+
+ override fun show() {
+ _internalView?.visibility = View.VISIBLE
+ }
+
+ override fun hide() {
+ _internalView?.visibility = View.GONE
+ }
+
+ override fun checkOrInit(): Boolean {
+ if (_internalView == null) {
+ val parentView = containerGroupView ?: return false
+ _internalView = FxDefaultContainerView(helper, parentView.context)
+ _internalView?.initView()
+ parentView.addView(_internalView)
+ }
+ return true
+ }
+
+ override fun reset() {
+ containerGroupView?.removeView(_internalView)
+ _containerGroup?.clear()
+ _containerGroup = null
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/system/FxSystemControlImp.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/system/FxSystemControlImp.kt
new file mode 100644
index 00000000..179cf096
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/system/FxSystemControlImp.kt
@@ -0,0 +1,18 @@
+package com.petterp.floatingx.impl.provider.system
+
+import android.app.Activity
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.impl.provider.FxBasisControlImpl
+import com.petterp.floatingx.listener.control.IFxAppControl
+
+/**
+ *
+ * @author petterp
+ */
+class FxSystemControlImp(helper: FxAppHelper) :
+ FxBasisControlImpl(helper), IFxAppControl {
+ override fun createPlatformProvider(f: FxAppHelper) = FxSystemPlatformProvider(helper)
+ override fun getBindActivity(): Activity? {
+ return null
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/provider/system/FxSystemPlatformProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/system/FxSystemPlatformProvider.kt
new file mode 100644
index 00000000..6565bd38
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/impl/provider/system/FxSystemPlatformProvider.kt
@@ -0,0 +1,57 @@
+package com.petterp.floatingx.impl.provider.system
+
+import android.content.Context
+import android.view.View
+import android.view.WindowManager
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.listener.provider.IFxPlatformProvider
+import com.petterp.floatingx.util.isVisibility
+import com.petterp.floatingx.view.IFxInternalView
+import com.petterp.floatingx.view.FxSystemContainerView
+
+/**
+ *
+ * @author petterp
+ */
+class FxSystemPlatformProvider(override val helper: FxAppHelper) :
+ IFxPlatformProvider {
+ private var _internalView: FxSystemContainerView? = null
+ private var wm: WindowManager? = null
+
+ override val context: Context
+ get() = helper.context
+ override val internalView: IFxInternalView?
+ get() = _internalView
+
+ override fun show() {
+ val internalView = _internalView ?: return
+ internalView.registerWM(wm ?: return)
+ internalView.isVisibility = true
+ }
+
+ override fun hide() {
+ val internalView = _internalView ?: return
+ // FIXME: 这里本来想直接remove,但是会引发LeakCanary的内存泄漏警告,故才用Gone
+ internalView.isVisibility = false
+ }
+
+ override fun isShow(): Boolean {
+ val internalView = _internalView ?: return false
+ return internalView.isAttachToWM && internalView.visibility == View.VISIBLE
+ }
+
+ override fun reset() {
+ val internalView = _internalView ?: return
+ internalView.isVisibility = false
+ wm?.removeViewImmediate(internalView)
+ }
+
+ override fun checkOrInit(): Boolean {
+ if (_internalView == null) {
+ wm = helper.context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ _internalView = FxSystemContainerView(helper, wm!!, context)
+ _internalView!!.initView()
+ }
+ return true
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxAppControl.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxAppControl.kt
index 4deda668..c63b8304 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxAppControl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxAppControl.kt
@@ -5,20 +5,6 @@ import android.app.Activity
/** App特有的控制方法 */
interface IFxAppControl : IFxControl {
- /**
- * 在当前activity中显示浮窗
- *
- * @param activity 当前要显示浮窗的activity
- *
- * 第一次调用该方法时,我们会插入一个AppLifecycle,用于监听activity的变化。当后续浮窗被cancel()时,我们会根据浮窗个数(=0),自动清空该lifecycle的绑定
- *
- * ps:尽管我们可以做到不传递activity,但是这种方式需要以性能作为牺牲,比如需要永久维护一个顶级activity与AppLifecycle监听器
- */
- fun show(activity: Activity)
-
- /** 从当前activity中移除 */
- fun detach(activity: Activity)
-
- /** 获得当前绑定的activity,不要手动保留此activity,以避免泄漏 */
+ /** 获得当前浮窗绑定的activity,不要手动保留此activity,以避免泄漏 */
fun getBindActivity(): Activity?
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxConfigControl.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxConfigControl.kt
index b418d960..2ecbc25d 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxConfigControl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxConfigControl.kt
@@ -5,7 +5,7 @@ import com.petterp.floatingx.assist.FxDisplayMode
import com.petterp.floatingx.listener.IFxConfigStorage
import com.petterp.floatingx.listener.IFxScrollListener
import com.petterp.floatingx.listener.IFxViewLifecycle
-import com.petterp.floatingx.util.FxAdsorbDirection
+import com.petterp.floatingx.assist.FxAdsorbDirection
/**
* 配置更改接口,使用此接口运行时更改配置层
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxControl.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxControl.kt
index d318af47..89137f92 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxControl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxControl.kt
@@ -1,10 +1,10 @@
package com.petterp.floatingx.listener.control
import android.view.View
+import android.widget.FrameLayout
import androidx.annotation.LayoutRes
import com.petterp.floatingx.listener.provider.IFxContextProvider
import com.petterp.floatingx.listener.provider.IFxHolderProvider
-import com.petterp.floatingx.view.FxManagerView
import com.petterp.floatingx.view.FxViewHolder
/** FloatingX 基础控制器 */
@@ -13,6 +13,8 @@ interface IFxControl {
/** 获取配置层控制器,以便运行时动态调整某些基础配置 */
val configControl: IFxConfigControl
+ fun show()
+
/** 隐藏悬浮窗-不会解绑app-lifecycle */
fun hide()
@@ -33,7 +35,7 @@ interface IFxControl {
fun getViewHolder(): FxViewHolder?
/** 获取浮窗管理器view,即浮窗底层容器 */
- fun getManagerView(): FxManagerView?
+ fun getManagerView(): FrameLayout?
/** 用于快速刷新视图内容 */
fun updateViewContent(provider: IFxHolderProvider)
@@ -85,4 +87,6 @@ interface IFxControl {
* @param useAnimation 是否使用动画
* */
fun moveByVector(x: Float, y: Float, useAnimation: Boolean)
+
+ fun updateConfig(obj: IFxConfigControl.() -> Unit)
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxScopeControl.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxScopeControl.kt
index 170c04e2..f4c34e15 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxScopeControl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxScopeControl.kt
@@ -1,7 +1,4 @@
package com.petterp.floatingx.listener.control
/** Fx局部控制器 */
-interface IFxScopeControl : IFxControl {
- /** 显示浮窗 */
- fun show()
-}
+interface IFxScopeControl : IFxControl
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxSystemControl.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxSystemControl.kt
new file mode 100644
index 00000000..ac282785
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/control/IFxSystemControl.kt
@@ -0,0 +1,9 @@
+package com.petterp.floatingx.listener.control
+
+/**
+ *
+ * @author petterp
+ */
+interface IFxSystemControl {
+ fun show()
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxAnimationProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxAnimationProvider.kt
new file mode 100644
index 00000000..099a3982
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxAnimationProvider.kt
@@ -0,0 +1,17 @@
+package com.petterp.floatingx.listener.provider
+
+import android.widget.FrameLayout
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+
+/**
+ *
+ * @author petterp
+ */
+interface IFxAnimationProvider : IFxBasicProvider {
+ fun start(view: FrameLayout, obj: (() -> Unit)? = null)
+
+ fun hide(view: FrameLayout, obj: (() -> Unit)? = null)
+
+ fun canRunAnimation(): Boolean
+ fun canCancelAnimation(): Boolean
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxBasicProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxBasicProvider.kt
new file mode 100644
index 00000000..a6f45208
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxBasicProvider.kt
@@ -0,0 +1,12 @@
+package com.petterp.floatingx.listener.provider
+
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+
+/**
+ * 基础fx提供者
+ * @author petterp
+ */
+interface IFxBasicProvider {
+ val helper: F
+ fun reset() {}
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxConfigProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxConfigProvider.kt
new file mode 100644
index 00000000..85efecfa
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxConfigProvider.kt
@@ -0,0 +1,9 @@
+package com.petterp.floatingx.listener.provider
+
+/**
+ *
+ * @author petterp
+ */
+interface IFxConfigProvider {
+ fun apply()
+}
\ No newline at end of file
diff --git a/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxPlatformProvider.kt b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxPlatformProvider.kt
new file mode 100644
index 00000000..5119b7a9
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/listener/provider/IFxPlatformProvider.kt
@@ -0,0 +1,14 @@
+package com.petterp.floatingx.listener.provider
+
+import android.content.Context
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+import com.petterp.floatingx.view.IFxInternalView
+
+interface IFxPlatformProvider : IFxBasicProvider {
+ val context: Context?
+ val internalView: IFxInternalView?
+ fun show()
+ fun hide()
+ fun isShow(): Boolean? = null
+ fun checkOrInit(): Boolean
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/FxExt.kt b/floatingx/src/main/java/com/petterp/floatingx/util/FxExt.kt
index b6c2368b..48a99f7f 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/util/FxExt.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/util/FxExt.kt
@@ -1,27 +1,60 @@
+@file:JvmName("_FxExt")
+
package com.petterp.floatingx.util
import android.app.Activity
-import android.content.Context
-import android.content.ContextWrapper
import android.view.MotionEvent
-import com.petterp.floatingx.assist.helper.ScopeHelper
+import android.view.View
+import android.widget.FrameLayout
+import com.petterp.floatingx.assist.helper.FxScopeHelper
+import com.petterp.floatingx.impl.lifecycle.FxAppLifecycleProvider
import java.lang.Exception
-@JvmSynthetic
internal const val FX_GRAVITY_TOP = 0x00000001
-@JvmSynthetic
internal const val FX_GRAVITY_CENTER = 0x00000002
-@JvmSynthetic
internal const val FX_GRAVITY_BOTTOM = 0x00000003
-internal const val TOUCH_CLICK_OFFSET = 2F
-internal const val TOUCH_TIME_THRESHOLD = 150L
internal const val INVALID_TOUCH_ID = -1
internal const val INVALID_LAYOUT_ID = 0
internal const val INVALID_TOUCH_IDX = -1
+internal const val TOUCH_TIME_THRESHOLD = 150L
internal const val DEFAULT_MOVE_ANIMATOR_DURATION = 200L
+internal const val FX_APP_DEFAULT_TAG = "FX_DEFAULT_TAG"
+
+internal const val FX_INSTALL_SCOPE_APP_TAG = "app"
+internal const val FX_INSTALL_SCOPE_SYSTEM_TAG = "system"
+internal const val FX_INSTALL_SCOPE_ACTIVITY_TAG = "activity"
+internal const val FX_INSTALL_SCOPE_FRAGMENT_TAG = "fragment"
+internal const val FX_INSTALL_SCOPE_VIEW_GROUP_TAG = "view"
+
+internal val topActivity: Activity?
+ get() = FxAppLifecycleProvider.getTopActivity()
+
+internal var View.isVisibility
+ set(value) {
+ visibility = if (value) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+ }
+ get() = visibility == View.VISIBLE
+
+internal val Activity.decorView: FrameLayout?
+ get() = try {
+ window.decorView as FrameLayout
+ } catch (_: Exception) {
+ null
+ }
+
+internal val Activity.contentView: FrameLayout?
+ get() = try {
+ window.decorView.findViewById(android.R.id.content)
+ } catch (_: Exception) {
+ null
+ }
/**
* 创建一个fx,自行初始化并控制插入位置
@@ -34,13 +67,11 @@ internal const val DEFAULT_MOVE_ANIMATOR_DURATION = 200L
*
* }
*/
-@JvmSynthetic
-inline fun createFx(crossinline obj: ScopeHelper.Builder.() -> T) =
+inline fun createFx(crossinline obj: FxScopeHelper.Builder.() -> T) =
lazy(LazyThreadSafetyMode.NONE) {
- ScopeHelper.Builder().run(obj)
+ FxScopeHelper.Builder().run(obj)
}
-@JvmSynthetic
internal inline fun lazyLoad(
mode: LazyThreadSafetyMode = LazyThreadSafetyMode.NONE,
crossinline obj: () -> T
@@ -49,37 +80,29 @@ internal inline fun lazyLoad(
obj()
}
-@JvmSynthetic
internal fun Float.coerceInFx(min: Float, max: Float): Float {
if (this < min) return min
if (this > max) return max
return this
}
+internal fun Int.coerceInFx(min: Int, max: Int): Int {
+ if (this < min) return min
+ if (this > max) return max
+ return this
+}
+
internal fun Float.withIn(min: Number, max: Number): Boolean {
return this in min.toFloat()..max.toFloat()
}
+internal fun Float.shr(count: Int): Float {
+ return this / count
+}
+
internal val MotionEvent.pointerId: Int
get() = try {
getPointerId(actionIndex)
} catch (_: Exception) {
INVALID_TOUCH_ID
}
-
-@JvmSynthetic
-internal fun Context.findActivity(): Activity? {
- return when (this) {
- is Activity -> {
- this
- }
-
- is ContextWrapper -> {
- baseContext.findActivity()
- }
-
- else -> {
- null
- }
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/FxLog.kt b/floatingx/src/main/java/com/petterp/floatingx/util/FxLog.kt
index 786829f2..ac1cddb8 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/util/FxLog.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/util/FxLog.kt
@@ -5,24 +5,27 @@ import android.util.Log
/**
* Fx日志查看器,开启之后,将查看到Fx整个运行轨迹
* */
-class FxLog private constructor(private val tag: String) {
+class FxLog private constructor(private val enable: Boolean, private val tag: String) {
companion object {
private var TAG = "FloatingX"
- fun builder(tag: String) =
- FxLog("$TAG-$tag")
+ fun builder(enable: Boolean, tag: String) =
+ FxLog(enable, "$TAG-$tag")
}
fun d(message: String) {
+ if (!enable) return
Log.d(tag, message)
}
fun v(message: String) {
+ if (!enable) return
Log.v(tag, message)
}
fun e(message: String) {
+ if (!enable) return
Log.e(tag, message)
}
}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/FxScopeEnum.kt b/floatingx/src/main/java/com/petterp/floatingx/util/FxScopeEnum.kt
deleted file mode 100644
index 2adbc216..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/util/FxScopeEnum.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.petterp.floatingx.util
-
-/** Fx插入的不同位置 */
-enum class FxScopeEnum(val tag: String) {
- APP_SCOPE("app"),
- ACTIVITY_SCOPE("activity"),
- FRAGMENT_SCOPE("fragment"),
- VIEW_GROUP_SCOPE("view"),
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/FxScreenExt.kt b/floatingx/src/main/java/com/petterp/floatingx/util/FxScreenExt.kt
index 46ffddec..32e06b2f 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/util/FxScreenExt.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/util/FxScreenExt.kt
@@ -1,3 +1,5 @@
+@file:JvmName("_FxScreenExt")
+
package com.petterp.floatingx.util
import android.annotation.SuppressLint
@@ -37,6 +39,15 @@ internal val Context.screenHeight: Int
return dm.heightPixels
}
+internal val Context.screenWidth: Int
+ get() {
+ val wm = getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ val display = wm.defaultDisplay
+ val dm = DisplayMetrics()
+ display.getMetrics(dm)
+ return dm.widthPixels
+ }
+
/** 状态栏高度,直接使用AppContext测量,部分情况会不准确 */
internal val Activity.statusBarHeight: Int
get() {
diff --git a/floatingx/src/main/java/com/petterp/floatingx/impl/FxScrollImpl.kt b/floatingx/src/main/java/com/petterp/floatingx/util/FxScrollImpl.kt
similarity index 91%
rename from floatingx/src/main/java/com/petterp/floatingx/impl/FxScrollImpl.kt
rename to floatingx/src/main/java/com/petterp/floatingx/util/FxScrollImpl.kt
index 4e12d16b..1aace662 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/impl/FxScrollImpl.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/util/FxScrollImpl.kt
@@ -1,4 +1,4 @@
-package com.petterp.floatingx.impl
+package com.petterp.floatingx.util
import android.view.MotionEvent
import com.petterp.floatingx.listener.IFxScrollListener
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/FxUiExt.kt b/floatingx/src/main/java/com/petterp/floatingx/util/FxUiExt.kt
deleted file mode 100644
index 0939b65e..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/util/FxUiExt.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.petterp.floatingx.util
-
-import android.app.Activity
-import android.widget.FrameLayout
-import com.petterp.floatingx.impl.lifecycle.FxLifecycleCallbackImpl
-
-/** App级当前设置了tag的栈顶Activity */
-internal val topActivity: Activity?
- get() = FxLifecycleCallbackImpl.getTopActivity()
-
-internal val Activity.decorView: FrameLayout?
- get() = try {
- window.decorView as FrameLayout
- } catch (_: Exception) {
- null
- }
-
-internal val Activity.contentView: FrameLayout?
- get() = try {
- window.decorView.findViewById(android.R.id.content)
- } catch (_: Exception) {
- null
- }
diff --git a/floatingx/src/main/java/com/petterp/floatingx/util/SimpleAnimatorListener.kt b/floatingx/src/main/java/com/petterp/floatingx/util/SimpleAnimatorListener.kt
new file mode 100644
index 00000000..6e9d5e03
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/util/SimpleAnimatorListener.kt
@@ -0,0 +1,28 @@
+package com.petterp.floatingx.util
+
+import android.animation.Animator
+import android.animation.Animator.AnimatorListener
+
+/**
+ *
+ * @author petterp
+ */
+class SimpleAnimatorListener(
+ var start: (() -> Unit)? = null,
+ var end: (() -> Unit)? = null
+) : AnimatorListener {
+ override fun onAnimationStart(animation: Animator?) {
+ start?.invoke()
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ end?.invoke()
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ end?.invoke()
+ }
+
+ override fun onAnimationRepeat(animation: Animator?) {
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxClickHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/FxClickHelper.kt
deleted file mode 100644
index 76b3c2b0..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/view/FxClickHelper.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.petterp.floatingx.view
-
-import androidx.annotation.Keep
-import com.petterp.floatingx.assist.helper.BasisHelper
-import com.petterp.floatingx.util.TOUCH_CLICK_OFFSET
-import com.petterp.floatingx.util.TOUCH_TIME_THRESHOLD
-import kotlin.math.abs
-
-/**
- *
- * @author petterp
- */
-class FxClickHelper {
- private var initX = 0f
- private var initY = 0f
- private var isClickEvent = false
- private var clickEnable = true
- private var mLastTouchDownTime = 0L
- private lateinit var helper: BasisHelper
-
- fun initConfig(helper: BasisHelper) {
- reset()
- this.helper = helper
- }
-
- fun initDown(x: Float, y: Float) {
- if (!helper.enableClickListener || helper.iFxClickListener == null) return
- this.initX = x
- this.initY = y
- isClickEvent = true
- mLastTouchDownTime = System.currentTimeMillis()
- }
-
- fun checkClickEvent(x: Float, y: Float) {
- if (!isClickEvent) return
- isClickEvent = abs(x - initX) < TOUCH_CLICK_OFFSET &&
- abs(y - initY) < TOUCH_CLICK_OFFSET
- }
-
- @Keep
- fun performClick(view: FxManagerView) {
- if (!isClickEffective()) return
- helper.iFxClickListener?.onClick(view)
- if (helper.clickTime > 0) {
- clickEnable = false
- view.postDelayed({ clickEnable = true }, helper.clickTime)
- }
- helper.fxLog?.d("fxView -> click")
- reset()
- }
-
- private fun reset() {
- initX = 0f
- initY = 0f
- isClickEvent = false
- mLastTouchDownTime = 0L
- }
-
- private fun isClickEffective(): Boolean {
- return isClickEvent && clickEnable && helper.enableClickListener &&
- helper.iFxClickListener != null &&
- System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxDefaultContainerView.kt b/floatingx/src/main/java/com/petterp/floatingx/view/FxDefaultContainerView.kt
new file mode 100644
index 00000000..a5605e26
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/FxDefaultContainerView.kt
@@ -0,0 +1,72 @@
+package com.petterp.floatingx.view
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Color
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.ViewGroup
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+import com.petterp.floatingx.view.basic.FxBasicParentView
+
+/**
+ * FxDefault View
+ * @author petterp
+ */
+@SuppressLint("ViewConstructor")
+class FxDefaultContainerView(helper: FxBasisHelper, context: Context, attrs: AttributeSet? = null) :
+ FxBasicParentView(helper, context, attrs) {
+
+ private var downTouchX = 0f
+ private var downTouchY = 0f
+
+ override fun initView() {
+ super.initView()
+ isClickable = true
+ initLayoutParams()
+ installChildView()
+ setBackgroundColor(Color.TRANSPARENT)
+ }
+
+ private fun initLayoutParams() {
+ val lp = helper.layoutParams ?: LayoutParams(
+ LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT,
+ ).apply {
+ gravity = Gravity.START or Gravity.TOP
+ }
+ layoutParams = lp
+ }
+
+ override fun currentX(): Float = x
+
+ override fun currentY(): Float = y
+
+ override fun updateXY(x: Float, y: Float) {
+ this.x = x
+ this.y = y
+ }
+
+ override fun parentSize(): Pair? {
+ val parentGroup = (parent as? ViewGroup) ?: return null
+ return parentGroup.width to parentGroup.height
+ }
+
+ override fun onTouchDown(event: MotionEvent) {
+ downTouchX = event.x
+ downTouchY = event.y
+ }
+
+ // 计算方式的不同,故与system逻辑有区别
+ override fun onTouchMove(event: MotionEvent) {
+ val x = x.minus(downTouchX).plus(event.x)
+ val y = y.minus(downTouchY).plus(event.y)
+ safeUpdateXY(x, y)
+ }
+
+ override fun onTouchCancel(event: MotionEvent) {
+ downTouchX = 0f
+ downTouchY = 0f
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxLocationHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/FxLocationHelper.kt
deleted file mode 100644
index 5b1602d0..00000000
--- a/floatingx/src/main/java/com/petterp/floatingx/view/FxLocationHelper.kt
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.petterp.floatingx.view
-
-import android.content.res.Configuration
-import com.petterp.floatingx.assist.helper.BasisHelper
-import com.petterp.floatingx.util.coerceInFx
-
-/**
- * Fx location restore helper,Used to restore the location of the floating window after the screen is rotated
- * @author petterp
- */
-class FxLocationHelper {
- private lateinit var config: BasisHelper
- private var screenW = 0
- private var screenH = 0
- private var x: Float = 0f
- private var y: Float = 0f
- private var isNearestLeft = false
- private var screenChanged: Boolean = false
- private var isInitLocation = true
-
- fun initConfig(config: BasisHelper) {
- this.config = config
- }
-
- /**
- * Whether to restore the position
- * */
- fun isRestoreLocation() = screenChanged
-
- fun isInitLocation(): Boolean {
- if (isInitLocation) {
- isInitLocation = false
- return true
- }
- return false
- }
-
- /**
- * save location info
- * */
- fun saveLocation(
- x: Float,
- y: Float,
- configHelper: FxViewConfigHelper,
- ): FxLocationHelper {
- this.x = x
- this.y = y
- isNearestLeft = configHelper.isNearestLeft(x)
- return this
- }
-
- /**
- * update screen size config
- * @return Whether the screen rotation has occurred
- * */
- fun updateConfig(config: Configuration): Boolean {
- val isChangedScreen =
- if (config.screenWidthDp != screenW || config.screenHeightDp != screenH) {
- this.screenW = config.screenWidthDp
- this.screenH = config.screenHeightDp
- true
- } else {
- false
- }
- screenChanged = isChangedScreen
- return screenChanged
- }
-
- /** get location config */
- fun getLocation(viewConfig: FxViewConfigHelper): Pair {
- val newX = getX(viewConfig.minWBoundary, viewConfig.minWBoundary)
- val newY = getY(viewConfig.minHBoundary, viewConfig.maxHBoundary)
- this.screenChanged = false
- return newX to newY
- }
-
- private fun getX(min: Float, max: Float): Float {
- return if (config.enableEdgeAdsorption) {
- if (isNearestLeft) min else max
- } else {
- x.coerceInFx(min, max)
- }
- }
-
- private fun getY(min: Float, max: Float): Float {
- return y.coerceInFx(min, max)
- }
-}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxSystemContainerView.kt b/floatingx/src/main/java/com/petterp/floatingx/view/FxSystemContainerView.kt
new file mode 100644
index 00000000..9b8fed1f
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/FxSystemContainerView.kt
@@ -0,0 +1,106 @@
+package com.petterp.floatingx.view
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Build
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.WindowManager
+import com.petterp.floatingx.assist.helper.FxAppHelper
+import com.petterp.floatingx.util.screenHeight
+import com.petterp.floatingx.util.screenWidth
+import com.petterp.floatingx.view.basic.FxBasicParentView
+
+/** 基础悬浮窗View */
+@SuppressLint("ViewConstructor")
+class FxSystemContainerView @JvmOverloads constructor(
+ override val helper: FxAppHelper,
+ private val wm: WindowManager,
+ context: Context,
+ attrs: AttributeSet? = null,
+) : FxBasicParentView(helper, context, attrs) {
+
+ private lateinit var wl: WindowManager.LayoutParams
+
+ private var downTouchX = 0f
+ private var downTouchY = 0f
+
+ val isAttachToWM: Boolean
+ get() = windowToken != null
+
+ override fun initView() {
+ super.initView()
+ installChildView() ?: return
+ initWLParams()
+ }
+
+ internal fun registerWM(wm: WindowManager) {
+ if (isAttachToWM) return
+ wm.addView(this, wl)
+ }
+
+ override fun currentX(): Float {
+ return wl.x.toFloat()
+ }
+
+ override fun currentY(): Float {
+ return wl.y.toFloat()
+ }
+
+ override fun preCheckPointerDownTouch(event: MotionEvent): Boolean {
+ // 当前屏幕存在手指时,check当前手势是否真的在浮窗之上
+ val x = event.rawX
+ val y = event.rawY
+ val location = IntArray(2)
+ getLocationOnScreen(location)
+ val left = location[0]
+ val top = location[1]
+ val right = left + this.width
+ val bottom = top + this.height
+ return x >= left && x <= right && y >= top && y <= bottom
+ }
+
+ override fun onTouchDown(event: MotionEvent) {
+ downTouchX = wl.x.minus(event.rawX)
+ downTouchY = wl.y.minus(event.rawY)
+ }
+
+ override fun onTouchMove(event: MotionEvent) {
+ val x = downTouchX.plus(event.rawX)
+ val y = downTouchY.plus(event.rawY)
+ safeUpdateXY(x, y)
+ }
+
+ override fun onTouchCancel(event: MotionEvent) {
+ downTouchX = 0f
+ downTouchY = 0f
+ }
+
+ override fun updateXY(x: Float, y: Float) {
+ wl.x = x.toInt()
+ wl.y = y.toInt()
+ wm.updateViewLayout(this, wl)
+ }
+
+ override fun parentSize(): Pair {
+ return helper.context.screenWidth to helper.context.screenHeight
+ }
+
+ private fun initWLParams() {
+ wl = WindowManager.LayoutParams().apply {
+ width = WindowManager.LayoutParams.WRAP_CONTENT
+ height = WindowManager.LayoutParams.WRAP_CONTENT
+ format = PixelFormat.RGBA_8888
+ gravity = Gravity.TOP or Gravity.START
+ flags =
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ type = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ } else {
+ WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
+ }
+ }
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxViewHolder.kt b/floatingx/src/main/java/com/petterp/floatingx/view/FxViewHolder.kt
index 26150452..622fb4fe 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/view/FxViewHolder.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/FxViewHolder.kt
@@ -31,7 +31,9 @@ class FxViewHolder(private val itemView: View?) {
views.put(viewId, it)
it
}
- } else view as? T
+ } else {
+ view as? T
+ }
}
fun setOnClickListener(@IdRes viewId: Int, listener: OnClickListener): FxViewHolder {
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/IFxInternalView.kt b/floatingx/src/main/java/com/petterp/floatingx/view/IFxInternalView.kt
new file mode 100644
index 00000000..e426b043
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/IFxInternalView.kt
@@ -0,0 +1,25 @@
+package com.petterp.floatingx.view
+
+import android.view.View
+import android.widget.FrameLayout
+
+/**
+ *
+ * @author petterp
+ */
+interface IFxInternalView {
+
+ val childView: View?
+
+ val containerView: FrameLayout
+
+ val viewHolder: FxViewHolder?
+
+ fun moveLocation(x: Float, y: Float, useAnimation: Boolean = true)
+
+ fun moveLocationByVector(x: Float, y: Float, useAnimation: Boolean = true)
+
+ fun moveToEdge()
+
+ fun updateView()
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxBasicParentView.kt b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxBasicParentView.kt
new file mode 100644
index 00000000..9f4f4f94
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxBasicParentView.kt
@@ -0,0 +1,139 @@
+package com.petterp.floatingx.view.basic
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+import com.petterp.floatingx.util.INVALID_LAYOUT_ID
+import com.petterp.floatingx.view.FxViewHolder
+import com.petterp.floatingx.view.IFxInternalView
+
+/**
+ * @author petterp
+ */
+abstract class FxBasicParentView @JvmOverloads constructor(
+ open val helper: FxBasisHelper,
+ context: Context,
+ attrs: AttributeSet? = null
+) : FrameLayout(context, attrs), IFxInternalView {
+ private var isInitLayout = true
+ private var _childView: View? = null
+ private var _viewHolder: FxViewHolder? = null
+ private val touchHelper = FxViewTouchHelper()
+ private val animateHelper = FxViewAnimationHelper()
+ private val locationHelper = FxViewLocationHelper()
+
+ abstract fun currentX(): Float
+ abstract fun currentY(): Float
+ abstract fun updateXY(x: Float, y: Float)
+ abstract fun parentSize(): Pair?
+
+ abstract fun onTouchDown(event: MotionEvent)
+ abstract fun onTouchMove(event: MotionEvent)
+ abstract fun onTouchCancel(event: MotionEvent)
+ open fun preCheckPointerDownTouch(event: MotionEvent): Boolean = true
+ open fun onLayoutInit() {}
+
+ override val childView: View? get() = _childView
+ override val containerView: FrameLayout get() = this
+ override val viewHolder: FxViewHolder? get() = _viewHolder
+
+ open fun initView() {
+ touchHelper.initConfig(this)
+ animateHelper.initConfig(this)
+ locationHelper.initConfig(this)
+ }
+
+ override fun moveToEdge() {
+ val (x, y) = locationHelper.getDefaultEdgeXY() ?: return
+ moveLocation(x, y, true)
+ }
+
+ override fun moveLocation(x: Float, y: Float, useAnimation: Boolean) {
+ moveToXY(x, y, useAnimation)
+ }
+
+ override fun moveLocationByVector(x: Float, y: Float, useAnimation: Boolean) {
+ moveToXY(x + currentX(), y + currentY(), useAnimation)
+ }
+
+ override fun updateView() {
+ helper.fxLog.d("fxView -> updateView")
+ locationHelper.updateLocationStatus()
+ removeView(_childView)
+ installChildView()
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ locationHelper.onSizeChanged()
+ }
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ checkOrInitLayout()
+ }
+
+ override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
+ return touchHelper.interceptTouchEvent(event) || super.onInterceptTouchEvent(event)
+ }
+
+ protected fun safeUpdateXY(x: Float, y: Float) {
+ val safeX = locationHelper.safeX(x, true)
+ val safeY = locationHelper.safeY(y, true)
+ updateXY(safeX, safeY)
+ }
+
+ protected fun installChildView(): View? {
+ _childView = inflateLayoutView() ?: inflateLayoutId()
+ if (_childView != null) _viewHolder = FxViewHolder(_childView)
+ return _childView
+ }
+
+ private fun inflateLayoutView(): View? {
+ val view = helper.layoutView ?: return null
+ helper.fxLog.d("fxView -> init, way:[layoutView]")
+ val lp = view.layoutParams ?: LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ )
+ addView(view, lp)
+ return view
+ }
+
+ private fun inflateLayoutId(): View? {
+ if (helper.layoutId == INVALID_LAYOUT_ID) return null
+ helper.fxLog.d("fxView -> init, way:[layoutId]")
+ val view = LayoutInflater.from(context).inflate(helper.layoutId, this, false)
+ addView(view)
+ return view
+ }
+
+ private fun moveToXY(x: Float, y: Float, useAnimation: Boolean) {
+ val endX = locationHelper.safeX(x)
+ val endY = locationHelper.safeY(y)
+ if (currentX() == endX && currentY() == endY) return
+ internalMoveToXY(useAnimation, endX, endY)
+ locationHelper.checkOrSaveLocation(endX, endY)
+ helper.fxLog.d("fxView -> moveToXY: start(${currentX()},${currentY()}),end($endX,$endY)")
+ }
+
+ private fun internalMoveToXY(useAnimation: Boolean, endX: Float, endY: Float) {
+ if (useAnimation) {
+ animateHelper.start(endX, endY)
+ } else {
+ updateXY(endX, endY)
+ }
+ }
+
+ private fun checkOrInitLayout() {
+ if (!isInitLayout) return
+ isInitLayout = false
+ locationHelper.initLayout(this)
+ onLayoutInit()
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxBasicViewHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxBasicViewHelper.kt
new file mode 100644
index 00000000..cf9f6f1e
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxBasicViewHelper.kt
@@ -0,0 +1,17 @@
+package com.petterp.floatingx.view.basic
+
+import com.petterp.floatingx.assist.helper.FxBasisHelper
+
+/**
+ * 基础类的辅助助手,用于分发基础逻辑
+ * @author petterp
+ */
+abstract class FxBasicViewHelper {
+ protected var basicView: FxBasicParentView? = null
+ protected lateinit var config: FxBasisHelper
+
+ open fun initConfig(parentView: FxBasicParentView) {
+ this.basicView = parentView
+ this.config = parentView.helper
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewAnimationHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewAnimationHelper.kt
new file mode 100644
index 00000000..6b017677
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewAnimationHelper.kt
@@ -0,0 +1,59 @@
+package com.petterp.floatingx.view.basic
+
+import android.animation.ValueAnimator
+import com.petterp.floatingx.util.DEFAULT_MOVE_ANIMATOR_DURATION
+
+/**
+ * FxView基础辅助类
+ * @author petterp
+ */
+class FxViewAnimationHelper : FxBasicViewHelper() {
+ private var valueAnimator: ValueAnimator? = null
+ private var animateListener: IFxViewAnimate? = null
+ private var startX: Float = 0f
+ private var startY: Float = 0f
+ private var endX: Float = 0f
+ private var endY: Float = 0f
+
+ fun setListener(iFxViewAnimate: IFxViewAnimate) {
+ this.animateListener = iFxViewAnimate
+ }
+
+ fun start(endX: Float, endY: Float) {
+ val startX = basicView?.currentX() ?: 0f
+ val startY = basicView?.currentY() ?: 0f
+ if (startX == endX && startY == endY) return
+ this.startX = startX
+ this.startY = startY
+ this.endX = endX
+ this.endY = endY
+ checkOrInitAnimator()
+ if (valueAnimator?.isRunning == true) valueAnimator?.cancel()
+ valueAnimator?.start()
+ }
+
+ private fun checkOrInitAnimator() {
+ if (valueAnimator == null) {
+ valueAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = DEFAULT_MOVE_ANIMATOR_DURATION
+ addUpdateListener {
+ val fraction = it.animatedValue as Float
+ val x = calculationNumber(startX, endX, fraction)
+ val y = calculationNumber(startY, endY, fraction)
+ basicView?.updateXY(x, y)
+ }
+ }
+ }
+ }
+
+ private fun calculationNumber(start: Float, end: Float, fraction: Float): Float {
+ val currentX = if (start == end) {
+ start
+ } else {
+ start + (end - start) * fraction
+ }
+ return currentX
+ }
+}
+
+typealias IFxViewAnimate = (x: Float, y: Float) -> Unit
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewBoundaryConfig.kt b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewBoundaryConfig.kt
new file mode 100644
index 00000000..cb2a1962
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewBoundaryConfig.kt
@@ -0,0 +1,20 @@
+package com.petterp.floatingx.view.basic
+
+/**
+ * FxView 边界配置
+ * @author petterp
+ */
+class FxViewBoundaryConfig(
+ var minW: Float = 0f,
+ var maxW: Float = 0f,
+ var minH: Float = 0f,
+ var maxH: Float = 0f
+) {
+ fun copy(other: FxViewBoundaryConfig): FxViewBoundaryConfig {
+ this.minW = other.minW
+ this.maxW = other.maxW
+ this.minH = other.minH
+ this.maxH = other.maxH
+ return this
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewLocationHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewLocationHelper.kt
new file mode 100644
index 00000000..81e6a1fc
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewLocationHelper.kt
@@ -0,0 +1,184 @@
+package com.petterp.floatingx.view.basic
+
+import com.petterp.floatingx.assist.FxAdsorbDirection
+import com.petterp.floatingx.assist.FxGravity
+import com.petterp.floatingx.util.coerceInFx
+import com.petterp.floatingx.util.shr
+
+/**
+ * Fx location restore helper,Used to restore the location of the floating window after the screen is rotated
+ * @author petterp
+ */
+class FxViewLocationHelper : FxBasicViewHelper() {
+ private var screenChanged: Boolean = false
+ private var isInitLocation = true
+
+ private var needUpdateLocation = false
+ private var parentW = 0f
+ private var parentH = 0f
+ private var viewW = 0f
+ private var viewH = 0f
+
+ private val moveIngBoundary = FxViewBoundaryConfig()
+ private val moveBoundary = FxViewBoundaryConfig()
+
+ private val x: Float
+ get() = basicView?.currentX() ?: 0f
+ private val y: Float
+ get() = basicView?.currentY() ?: 0f
+
+ fun isRestoreLocation() = screenChanged
+
+ fun isInitLocation(): Boolean {
+ if (isInitLocation) {
+ isInitLocation = false
+ return true
+ }
+ return false
+ }
+
+ fun updateLocationStatus() {
+ needUpdateLocation = true
+ }
+
+ fun onSizeChanged() {
+ updateViewSize()
+ if (needUpdateLocation) {
+ basicView?.moveToEdge()
+ needUpdateLocation = false
+ config.fxLog.d("fxView -> updateLocation")
+ }
+ }
+
+ fun initLayout(view: FxBasicParentView) {
+ val hasHistory = config.enableSaveDirection && config.iFxConfigStorage?.hasConfig() == true
+ val (defaultX, defaultY) = if (hasHistory) {
+ getHistoryXY()
+ } else {
+ getDefaultXY(parentW, parentH, viewW, viewH)
+ }
+ view.updateXY(safeX(defaultX), safeY(defaultY))
+ val from = if (hasHistory) "history_location" else "default_location"
+ config.fxLog.d("fxView -> initLocation: x:$defaultX,y:$defaultY,from:$from")
+ }
+
+ fun getDefaultEdgeXY(): Pair? {
+ return if (config.enableEdgeAdsorption) {
+ if (config.adsorbDirection == FxAdsorbDirection.LEFT_OR_RIGHT) {
+ val moveX = if (isNearestLeft(x)) moveBoundary.minW else moveBoundary.maxW
+ val moveY = y
+ moveX to moveY
+ } else {
+ val moveX = x
+ val moveY = if (isNearestTop(y)) moveBoundary.minH else moveBoundary.maxH
+ moveX to moveY
+ }
+ } else if (config.enableEdgeRebound) {
+ x to y
+ } else {
+ null
+ }
+ }
+
+ private fun updateViewSize() {
+ val view = basicView ?: return
+ val (pW, pH) = view.parentSize() ?: return
+ val viewH = view.height.toFloat()
+ val viewW = view.width.toFloat()
+ this.parentW = pW.toFloat()
+ this.parentH = pH.toFloat()
+ this.viewW = viewW
+ this.viewH = viewH
+ updateBoundary()
+ config.fxLog.d("fxView -> updateViewSize: parentW:$parentW,parentH:$parentH,viewW:$viewW,viewH:$viewH")
+ }
+
+ fun safeX(x: Float, isMoveIng: Boolean = false): Float {
+ val enableBound = isMoveIng && config.enableEdgeRebound
+ val minW = if (enableBound) moveIngBoundary.minW else moveBoundary.minW
+ val maxW = if (enableBound) moveIngBoundary.maxW else moveBoundary.maxW
+ return x.coerceInFx(minW, maxW)
+ }
+
+ fun safeY(y: Float, isMoveIng: Boolean = false): Float {
+ val enableBound = isMoveIng && config.enableEdgeRebound
+ val minH = if (enableBound) moveIngBoundary.minH else moveBoundary.minH
+ val maxH = if (enableBound) moveIngBoundary.maxH else moveBoundary.maxH
+ return y.coerceInFx(minH, maxH)
+ }
+
+ fun checkOrSaveLocation(x: Float, y: Float) {
+ if (config.iFxConfigStorage == null || !config.enableSaveDirection) return
+ config.iFxConfigStorage!!.update(x, y)
+ config.fxLog.d("saveLocation -> x:$x,y:$y")
+ }
+
+ private fun isNearestLeft(x: Float): Boolean {
+ val middle = parentW / 2
+ return x < middle
+ }
+
+ private fun isNearestTop(y: Float): Boolean {
+ val middle = parentH / 2
+ return y < middle
+ }
+
+ private fun getHistoryXY(): Pair {
+ return config.run {
+ val configX = iFxConfigStorage?.getX() ?: 0f
+ val configY = iFxConfigStorage?.getY() ?: 0f
+ configX to configY
+ }
+ }
+
+ private fun getDefaultXY(
+ width: Float,
+ height: Float,
+ viewW: Float,
+ viewH: Float
+ ): Pair {
+ return config.run {
+ val l = offsetX + safeEdgeOffSet + fxBorderMargin.l
+ val r = offsetX + safeEdgeOffSet + fxBorderMargin.r
+ val b = offsetY + safeEdgeOffSet + fxBorderMargin.b
+ val t = offsetY + safeEdgeOffSet + fxBorderMargin.t
+ when (gravity) {
+ FxGravity.DEFAULT,
+ FxGravity.LEFT_OR_TOP -> l to t
+
+ FxGravity.LEFT_OR_CENTER -> l to (height - viewH).shr(1)
+
+ FxGravity.LEFT_OR_BOTTOM -> 0f to height - viewH - b
+
+ FxGravity.RIGHT_OR_TOP -> width - viewW - r to t
+
+ FxGravity.RIGHT_OR_CENTER -> width - viewW - r to (height - viewH).shr(1)
+
+ FxGravity.RIGHT_OR_BOTTOM -> width - viewW - r to height - viewH - b
+
+ FxGravity.TOP_OR_CENTER -> (width - viewW).shr(1) to t
+
+ FxGravity.BOTTOM_OR_CENTER -> (width - viewW).shr(1) to height - viewH - b
+
+ else -> (width - viewW).shr(1) to (height - viewH).shr(1)
+ }
+ }
+ }
+
+ private fun updateBoundary() {
+ config.apply {
+ moveIngBoundary.apply {
+ minW = 0f
+ maxW = parentW - viewW
+ minH = statsBarHeight.toFloat()
+ maxH = parentH - viewH - navigationBarHeight
+ }
+ moveBoundary.copy(moveIngBoundary).apply {
+ minW += fxBorderMargin.l + edgeOffset
+ maxW -= fxBorderMargin.r + edgeOffset
+ minH += fxBorderMargin.t + edgeOffset
+ maxH -= fxBorderMargin.b + edgeOffset
+ }
+ }
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewTouchHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewTouchHelper.kt
new file mode 100644
index 00000000..1b000fb2
--- /dev/null
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/basic/FxViewTouchHelper.kt
@@ -0,0 +1,156 @@
+package com.petterp.floatingx.view.basic
+
+import android.annotation.SuppressLint
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewConfiguration
+import androidx.annotation.Keep
+import com.petterp.floatingx.util.INVALID_TOUCH_ID
+import com.petterp.floatingx.util.TOUCH_TIME_THRESHOLD
+import com.petterp.floatingx.util.pointerId
+import kotlin.math.abs
+
+/**
+ * 手势事件辅助类,处理点击事件
+ * @author petterp
+ */
+class FxViewTouchHelper : FxBasicViewHelper() {
+ private var initX = 0f
+ private var initY = 0f
+ private var scaledTouchSlop = 0F
+ private var isClickEvent = false
+ private var clickEnable = true
+ private var mLastTouchDownTime = 0L
+ private var touchDownId = INVALID_TOUCH_ID
+
+ @SuppressLint("ClickableViewAccessibility")
+ override fun initConfig(parentView: FxBasicParentView) {
+ super.initConfig(parentView)
+ reset()
+ scaledTouchSlop = ViewConfiguration.get(parentView.context).scaledTouchSlop.toFloat()
+ parentView.setOnTouchListener { _, event ->
+ when (event.actionMasked) {
+ MotionEvent.ACTION_DOWN -> initTouchDown(event)
+ MotionEvent.ACTION_MOVE -> touchToMove(event)
+ MotionEvent.ACTION_POINTER_DOWN -> touchToPointerDown(event)
+ MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL,
+ MotionEvent.ACTION_POINTER_UP -> touchCancel(event)
+ }
+ false
+ }
+ }
+
+ fun interceptTouchEvent(event: MotionEvent): Boolean {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ if (hasMainPointerId()) return false
+ initTouchDown(event)
+ }
+
+ MotionEvent.ACTION_MOVE -> {
+ if (!isCurrentPointerId(event)) return false
+ return abs(event.x - initX) >= scaledTouchSlop ||
+ abs(event.y - initY) >= scaledTouchSlop
+ }
+ }
+ return false
+ }
+
+ @Keep
+ fun touchCancel(view: View) {
+ if (isClickEffective()) {
+ config.iFxClickListener?.onClick(view)
+ if (config.clickTime > 0) {
+ clickEnable = false
+ view.postDelayed({ clickEnable = true }, config.clickTime)
+ } else {
+ clickEnable = true
+ }
+ config.fxLog.d("fxView -> click")
+ }
+ reset()
+ }
+
+ private fun initTouchDown(event: MotionEvent) {
+ if (hasMainPointerId()) return
+ initClickConfig(event)
+ touchDownId = event.pointerId
+ basicView?.onTouchDown(event)
+ config.fxLog.d("fxView -> initDownTouch,mainTouchId:$touchDownId")
+ }
+
+ private fun initClickConfig(event: MotionEvent) {
+ this.initX = event.rawX
+ this.initY = event.rawY
+ if (!config.enableClickListener || config.iFxClickListener == null) return
+ isClickEvent = true
+ mLastTouchDownTime = System.currentTimeMillis()
+ }
+
+ private fun touchToPointerDown(event: MotionEvent) {
+ if (hasMainPointerId()) {
+ config.fxLog.d("fxView -> touchToPointerDown: currentId:${event.pointerId}, mainTouchId:$touchDownId exist,return")
+ return
+ }
+ // Before the event starts, check first
+ if (basicView?.preCheckPointerDownTouch(event) != true) {
+ config.fxLog.d("fxView -> touchToPointerDown: current touch location error,return")
+ return
+ }
+ initTouchDown(event)
+ }
+
+ private fun touchToMove(event: MotionEvent) {
+ if (!isCurrentPointerId(event)) return
+ checkClickState(event)
+ basicView?.onTouchMove(event)
+ config.fxLog.v("fxView -> touchMove,rawX:${event.rawX},rawY:${event.rawY}")
+ }
+
+ private fun touchCancel(event: MotionEvent) {
+ if (!isCurrentPointerId(event)) return
+ performClick()
+ reset()
+ basicView?.moveToEdge()
+ basicView?.onTouchCancel(event)
+ config.fxLog.d("fxView -> mainTouchUp")
+ }
+
+ private fun performClick() {
+ if (isClickEffective()) {
+ clickEnable = false
+ config.iFxClickListener?.onClick(basicView)
+ basicView?.postDelayed({
+ clickEnable = true
+ }, 1000)
+ }
+ }
+
+ private fun checkClickState(event: MotionEvent) {
+ if (!isClickEvent) return
+ isClickEvent = abs(event.rawX - initX) < scaledTouchSlop &&
+ abs(event.rawY - initY) < scaledTouchSlop
+ }
+
+ private fun isCurrentPointerId(ev: MotionEvent): Boolean {
+ if (touchDownId == INVALID_TOUCH_ID) return false
+ return ev.pointerId == touchDownId
+ }
+
+ private fun reset() {
+ initX = 0f
+ initY = 0f
+ isClickEvent = false
+ mLastTouchDownTime = 0L
+ touchDownId = INVALID_TOUCH_ID
+ }
+
+ private fun hasMainPointerId() = touchDownId != INVALID_TOUCH_ID
+
+ private fun isClickEffective(): Boolean {
+ // 当前是点击事件&&点击事件目前可启用&&回调存在&&点击时间小于阈值
+ return isClickEvent && clickEnable && config.enableClickListener &&
+ config.iFxClickListener != null &&
+ System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD
+ }
+}
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxManagerView.kt b/floatingx/src/main/java/com/petterp/floatingx/view/default/FxDefaultContainerView.kt
similarity index 70%
rename from floatingx/src/main/java/com/petterp/floatingx/view/FxManagerView.kt
rename to floatingx/src/main/java/com/petterp/floatingx/view/default/FxDefaultContainerView.kt
index 7d0f3a2b..53f96cfe 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/view/FxManagerView.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/default/FxDefaultContainerView.kt
@@ -1,17 +1,17 @@
-package com.petterp.floatingx.view
+package com.petterp.floatingx.view.default
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Color
import android.util.AttributeSet
+import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import com.petterp.floatingx.assist.FxDisplayMode
-import com.petterp.floatingx.assist.FxGravity
-import com.petterp.floatingx.assist.helper.BasisHelper
+import com.petterp.floatingx.assist.helper.FxBasisHelper
import com.petterp.floatingx.util.DEFAULT_MOVE_ANIMATOR_DURATION
import com.petterp.floatingx.util.FX_GRAVITY_BOTTOM
import com.petterp.floatingx.util.FX_GRAVITY_TOP
@@ -19,24 +19,37 @@ import com.petterp.floatingx.util.INVALID_LAYOUT_ID
import com.petterp.floatingx.util.INVALID_TOUCH_ID
import com.petterp.floatingx.util.pointerId
import com.petterp.floatingx.util.withIn
+import com.petterp.floatingx.view.FxViewHolder
+import com.petterp.floatingx.view.IFxInternalView
+import com.petterp.floatingx.view.basic.FxViewLocationHelper
+import com.petterp.floatingx.view.basic.FxViewTouchHelper
/** 基础悬浮窗View */
@SuppressLint("ViewConstructor")
-class FxManagerView @JvmOverloads constructor(
+class FxDefaultContainerView @JvmOverloads constructor(
context: Context,
- attrs: AttributeSet? = null,
-) : FrameLayout(context, attrs), View.OnLayoutChangeListener {
+ attrs: AttributeSet? = null
+) : FrameLayout(context, attrs), View.OnLayoutChangeListener, IFxInternalView {
- private lateinit var helper: BasisHelper
- private val clickHelper = FxClickHelper()
- private val restoreHelper = FxLocationHelper()
+ private lateinit var helper: FxBasisHelper
+ private val clickHelper = FxViewTouchHelper()
+ private val locationHelper = FxViewLocationHelper()
private val configHelper = FxViewConfigHelper()
+ private var _viewHolder: FxViewHolder? = null
private var _childFxView: View? = null
- val childFxView: View? get() = _childFxView
+ override val childView: View?
+ get() = _childFxView
+ override val containerView: FrameLayout
+ get() = this
+ override val viewHolder: FxViewHolder?
+ get() {
+ if (_viewHolder == null) _viewHolder = FxViewHolder(this)
+ return _viewHolder
+ }
@JvmSynthetic
- internal fun init(config: BasisHelper): FxManagerView {
+ internal fun init(config: FxBasisHelper): FxDefaultContainerView {
this.helper = config
initView()
return this
@@ -44,8 +57,8 @@ class FxManagerView @JvmOverloads constructor(
private fun initView() {
_childFxView = inflateLayoutView() ?: inflateLayoutId()
- clickHelper.initConfig(helper)
- restoreHelper.initConfig(helper)
+// clickHelper.initConfig(context, helper)
+// locationHelper.initConfig(this)
configHelper.initConfig(context, helper)
checkNotNull(_childFxView) { "initFxView -> Error,check your layoutId or layoutView." }
initLocation()
@@ -56,7 +69,7 @@ class FxManagerView @JvmOverloads constructor(
private fun inflateLayoutView(): View? {
val view = helper.layoutView ?: return null
- helper.fxLog?.d("fxView-->init, way:[layoutView]")
+ helper.fxLog.d("fxView-->init, way:[layoutView]")
val lp = view.layoutParams ?: LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -67,8 +80,10 @@ class FxManagerView @JvmOverloads constructor(
private fun inflateLayoutId(): View? {
if (helper.layoutId == INVALID_LAYOUT_ID) return null
- helper.fxLog?.d("fxView-->init, way:[layoutId]")
- return inflate(context, helper.layoutId, this)
+ helper.fxLog.d("fxView-->init, way:[layoutId]")
+ val view = LayoutInflater.from(context).inflate(helper.layoutId, this, false)
+ addView(view)
+ return view
}
private fun initLocation() {
@@ -91,13 +106,13 @@ class FxManagerView @JvmOverloads constructor(
}
if (initX != -1F) x = initX
if (initY != -1F) y = initY
- helper.fxLog?.d("fxView->initLocation,isHasConfig-($hasConfig),defaultX-($initX),defaultY-($initY)")
+ helper.fxLog.d("fxView -> initLocation,isHasConfig-($hasConfig),defaultX-($initX),defaultY-($initY)")
}
private fun initDefaultXY(): Pair {
// 非辅助定位&&非默认位置,此时x,y不可信
if (!helper.enableAssistLocation && !helper.gravity.isDefault()) {
- helper.fxLog?.e(
+ helper.fxLog.e(
"fxView--默认坐标可能初始化异常,如果显示位置异常,请检查您的gravity是否为默认配置,当前gravity:${helper.gravity}。\n" +
"如果您要配置gravity,建议您启用辅助定位setEnableAssistDirection(),此方法将更便于定位。",
)
@@ -121,12 +136,12 @@ class FxManagerView @JvmOverloads constructor(
when (ev.actionMasked) {
MotionEvent.ACTION_DOWN -> {
initTouchDown(ev)
- helper.fxLog?.d("fxView---onInterceptTouchEvent-[down]")
+ helper.fxLog.d("fxView---onInterceptTouchEvent-[down]")
}
MotionEvent.ACTION_MOVE -> {
intercepted = configHelper.checkInterceptedEvent(ev)
- helper.fxLog?.d("fxView---onInterceptTouchEvent-[move], interceptedTouch-$intercepted")
+ helper.fxLog.d("fxView---onInterceptTouchEvent-[move], interceptedTouch-$intercepted")
}
}
return intercepted
@@ -149,32 +164,32 @@ class FxManagerView @JvmOverloads constructor(
super.onAttachedToWindow()
helper.iFxViewLifecycle?.attach()
(parent as? ViewGroup)?.addOnLayoutChangeListener(this)
- helper.fxLog?.d("fxView-lifecycle-> onAttachedToWindow")
+ helper.fxLog.d("fxView-lifecycle-> onAttachedToWindow")
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
helper.iFxViewLifecycle?.detached()
(parent as? ViewGroup)?.removeOnLayoutChangeListener(this)
- helper.fxLog?.d("fxView-lifecycle-> onDetachedFromWindow")
+ helper.fxLog.d("fxView-lifecycle-> onDetachedFromWindow")
}
override fun onWindowVisibilityChanged(visibility: Int) {
super.onWindowVisibilityChanged(visibility)
helper.iFxViewLifecycle?.windowsVisibility(visibility)
- helper.fxLog?.d("fxView-lifecycle-> onWindowVisibilityChanged")
+ helper.fxLog.d("fxView-lifecycle-> onWindowVisibilityChanged")
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
- helper.fxLog?.d("fxView--lifecycle-> onConfigurationChanged--->")
+ helper.fxLog.d("fxView--lifecycle-> onConfigurationChanged--->")
// use the configuration in Configuration first
- val isScreenChanged = restoreHelper.updateConfig(newConfig)
- if (!isScreenChanged) return
+// val isScreenChanged = locationHelper.updateConfig(newConfig)
+// if (!isScreenChanged) return
val x = x
val y = y
- restoreHelper.saveLocation(x, y, configHelper)
- helper.fxLog?.d("fxView--lifecycle-> saveLocation:[x:$x,y:$y]")
+// locationHelper.saveLocation(x, y, configHelper)
+ helper.fxLog.d("fxView--lifecycle-> saveLocation:[x:$x,y:$y]")
}
override fun setOnClickListener(l: OnClickListener?) {
@@ -184,7 +199,7 @@ class FxManagerView @JvmOverloads constructor(
private fun initTouchDown(ev: MotionEvent) {
if (configHelper.hasMainPointerId()) return
- clickHelper.initDown(x, y)
+// clickHelper.initDown(ev)
configHelper.initTouchDown(ev)
configHelper.updateWidgetSize(this)
configHelper.updateBoundary(true)
@@ -192,8 +207,7 @@ class FxManagerView @JvmOverloads constructor(
helper.iFxScrollListener?.down()
}
- @JvmSynthetic
- internal fun moveLocation(x: Float, y: Float, useAnimation: Boolean = true) {
+ override fun moveLocation(x: Float, y: Float, useAnimation: Boolean) {
val newX = configHelper.safeX(x)
val newY = configHelper.safeY(y)
if (useAnimation) {
@@ -204,22 +218,23 @@ class FxManagerView @JvmOverloads constructor(
}
}
- @JvmSynthetic
- internal fun moveLocationByVector(x: Float, y: Float, useAnimation: Boolean = true) {
+ override fun moveLocationByVector(x: Float, y: Float, useAnimation: Boolean) {
val currentX = this.x.plus(x)
val currentY = this.y.plus(y)
moveLocation(currentX, currentY, useAnimation)
}
- @JvmSynthetic
- internal fun restoreLocation(x: Float, y: Float) {
- (layoutParams as LayoutParams).gravity = FxGravity.DEFAULT.value
- this.x = x
- this.y = y
+ override fun updateView() {
+ removeView(_childFxView)
+ _childFxView = if (helper.layoutView != null) {
+ inflateLayoutView()
+ } else {
+ inflateLayoutId()
+ }
}
- @JvmSynthetic
- internal fun moveToEdge() {
+ override fun moveToEdge() {
+ // TODO: 这里看着有点bug,如果没开启吸附,位置就不保存了
configHelper.updateBoundary(false)
configHelper.getAdsorbDirectionLocation(x, y)?.let { (x, y) ->
moveToLocation(x, y)
@@ -234,26 +249,26 @@ class FxManagerView @JvmOverloads constructor(
private fun moveToLocation(moveX: Float, moveY: Float) {
if (moveX == x && moveY == y) return
- helper.fxLog?.d("fxView-->moveToEdge---x-($x),y-($y) -> moveX-($moveX),moveY-($moveY)")
+ helper.fxLog.d("fxView-->moveToEdge---x-($x),y-($y) -> moveX-($moveX),moveY-($moveY)")
animate().x(moveX).y(moveY).setDuration(DEFAULT_MOVE_ANIMATOR_DURATION).start()
}
private fun saveLocationToStorage(moveX: Float, moveY: Float) {
if (!helper.enableSaveDirection) return
if (helper.iFxConfigStorage == null) {
- helper.fxLog?.e("fxView-->saveDirection---iFxConfigStorageImpl does not exist, save failed!")
+ helper.fxLog.e("fxView-->saveDirection---iFxConfigStorageImpl does not exist, save failed!")
return
}
helper.iFxConfigStorage?.update(moveX, moveY)
- helper.fxLog?.d("fxView-->saveDirection---x-($moveX),y-($moveY)")
+ helper.fxLog.d("fxView-->saveDirection---x-($moveX),y-($moveY)")
}
private fun restoreLocation() {
- val (x, y) = restoreHelper.getLocation(configHelper)
- this.x = x
- this.y = y
+// val (x, y) = locationHelper.getLocation(configHelper)
+// this.x = x
+// this.y = y
saveLocationToStorage(x, y)
- helper.fxLog?.d("fxView--lifecycle-> restoreLocation:[x:$x,y:$y]")
+ helper.fxLog.d("fxView--lifecycle-> restoreLocation:[x:$x,y:$y]")
}
private fun touchToMove(event: MotionEvent) {
@@ -266,12 +281,12 @@ class FxManagerView @JvmOverloads constructor(
if (configHelper.isCurrentPointerId(event)) {
touchToCancel()
} else {
- helper.fxLog?.d("fxView---onTouchEvent--ACTION_POINTER_UP---id:${event.pointerId}->")
+ helper.fxLog.d("fxView---onTouchEvent--ACTION_POINTER_UP---id:${event.pointerId}->")
}
}
private fun touchToPointerDown(event: MotionEvent) {
- helper.fxLog?.d("fxView---onTouchEvent--touchToPointerDown--id:${event.getPointerId(event.actionIndex)}->")
+ helper.fxLog.d("fxView---onTouchEvent--touchToPointerDown--id:${event.getPointerId(event.actionIndex)}->")
if (configHelper.hasMainPointerId()) return
// Here you can realize the multi-finger cooperative pulling 😆
if (event.x.withIn(0, width) && event.y.withIn(0, height)) {
@@ -283,18 +298,18 @@ class FxManagerView @JvmOverloads constructor(
moveToEdge()
helper.iFxScrollListener?.up()
configHelper.touchDownId = INVALID_TOUCH_ID
- clickHelper.performClick(this)
- helper.fxLog?.d("fxView---onTouchEvent---MainTouchCancel->")
+ clickHelper.touchCancel(this)
+ helper.fxLog.d("fxView---onTouchEvent---MainTouchCancel->")
}
private fun refreshLocation(w: Int, h: Int) {
if (!configHelper.updateWidgetSize(w, h, this)) return
// 初始化位置时,我们进行一次位置校准,避免浮窗位置异常
- if (restoreHelper.isInitLocation()) {
+ if (locationHelper.isInitLocation()) {
checkOrFixLocation()
return
}
- if (restoreHelper.isRestoreLocation()) {
+ if (locationHelper.isRestoreLocation()) {
restoreLocation()
} else {
moveToEdge()
@@ -312,9 +327,9 @@ class FxManagerView @JvmOverloads constructor(
val disY = configHelper.safeY(y, event)
x = disX
y = disY
- clickHelper.checkClickEvent(disX, disY)
+// clickHelper.touchMove(event)
helper.iFxScrollListener?.dragIng(event, disX, disY)
- helper.fxLog?.v("fxView---scrollListener--drag-event--x($disX)-y($disY)")
+ helper.fxLog.v("fxView---scrollListener--drag-event--x($disX)-y($disY)")
}
override fun onLayoutChange(
diff --git a/floatingx/src/main/java/com/petterp/floatingx/view/FxViewConfigHelper.kt b/floatingx/src/main/java/com/petterp/floatingx/view/default/FxViewConfigHelper.kt
similarity index 88%
rename from floatingx/src/main/java/com/petterp/floatingx/view/FxViewConfigHelper.kt
rename to floatingx/src/main/java/com/petterp/floatingx/view/default/FxViewConfigHelper.kt
index 1f3a4b03..78b108f6 100644
--- a/floatingx/src/main/java/com/petterp/floatingx/view/FxViewConfigHelper.kt
+++ b/floatingx/src/main/java/com/petterp/floatingx/view/default/FxViewConfigHelper.kt
@@ -1,11 +1,11 @@
-package com.petterp.floatingx.view
+package com.petterp.floatingx.view.default
import android.content.Context
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.ViewGroup
-import com.petterp.floatingx.assist.helper.BasisHelper
-import com.petterp.floatingx.util.FxAdsorbDirection
+import com.petterp.floatingx.assist.FxAdsorbDirection
+import com.petterp.floatingx.assist.helper.FxBasisHelper
import com.petterp.floatingx.util.INVALID_TOUCH_ID
import com.petterp.floatingx.util.coerceInFx
import com.petterp.floatingx.util.pointerId
@@ -16,7 +16,7 @@ class FxViewConfigHelper {
private var downTouchY = 0f
private var mParentWidth = 0f
private var mParentHeight = 0f
- private lateinit var helper: BasisHelper
+ private lateinit var helper: FxBasisHelper
private var scaledTouchSlop = 0
var minHBoundary = 0f
@@ -26,7 +26,7 @@ class FxViewConfigHelper {
var touchDownId = INVALID_TOUCH_ID
- fun initConfig(context: Context, helper: BasisHelper) {
+ fun initConfig(context: Context, helper: FxBasisHelper) {
this.helper = helper
scaledTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
}
@@ -50,7 +50,7 @@ class FxViewConfigHelper {
touchDownId = ev.pointerId
downTouchX = ev.x
downTouchY = ev.y
- helper.fxLog?.d("fxView---newTouchDown:$touchDownId")
+ helper.fxLog.d("fxView---newTouchDown:$touchDownId")
}
fun updateWidgetSize(view: ViewGroup): Boolean {
@@ -63,7 +63,7 @@ class FxViewConfigHelper {
val parentWidth = (parentW - view.width).toFloat()
val parentHeight = (parentH - view.height).toFloat()
if (mParentHeight != parentHeight || mParentWidth != parentWidth) {
- helper.fxLog?.d("fxView->updateContainerSize: oldW-($mParentWidth),oldH-($mParentHeight),newW-($parentWidth),newH-($parentHeight)")
+ helper.fxLog.d("fxView -> updateContainerSize: oldW-($mParentWidth),oldH-($mParentHeight),newW-($parentWidth),newH-($parentHeight)")
mParentWidth = parentWidth
mParentHeight = parentHeight
updateBoundary(false)
@@ -72,12 +72,12 @@ class FxViewConfigHelper {
return false
}
- fun isNearestLeft(x: Float): Boolean {
+ private fun isNearestLeft(x: Float): Boolean {
val middle = mParentWidth / 2
return x < middle
}
- fun isNearestTop(y: Float): Boolean {
+ private fun isNearestTop(y: Float): Boolean {
val middle = mParentHeight / 2
return y < middle
}