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 }