Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions app/src/main/java/com/petterp/floatingx/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import androidx.cardview.widget.CardView
import com.petterp.floatingx.FloatingX
import com.petterp.floatingx.app.kotlin.FxSystemSimple
import com.petterp.floatingx.app.simple.FxAnimationImpl
import com.petterp.floatingx.app.test.EdgeCaseTestActivity
import com.petterp.floatingx.app.test.MultipleFxActivity
import com.petterp.floatingx.app.test.SystemActivity
import com.petterp.floatingx.app.test.TestLifecycleActivity
import com.petterp.floatingx.util.createFx

class MainActivity : AppCompatActivity() {
Expand Down Expand Up @@ -110,6 +112,12 @@ class MainActivity : AppCompatActivity() {
addItemView("进入system浮窗测试页面") {
SystemActivity::class.java.start(this@MainActivity)
}
addItemView("测试生命周期时序问题修复") {
TestLifecycleActivity::class.java.start(this@MainActivity)
}
addItemView("测试边界情况") {
EdgeCaseTestActivity::class.java.start(this@MainActivity)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package com.petterp.floatingx.app.test

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.petterp.floatingx.FloatingX
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.R

/**
* Comprehensive test for edge cases in the lifecycle timing fix
*/
class EdgeCaseTestActivity : AppCompatActivity() {

companion object {
const val EDGE_TEST_TAG = "edge_test"
}

private val handler = Handler(Looper.getMainLooper())

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

createLinearLayoutToParent {
addNestedScrollView {
addLinearLayout {
addItemView("Test rapid operations") {
testRapidOperations()
}
addItemView("Test duplicate show/hide") {
testDuplicateOperations()
}
addItemView("Test operations during initialization") {
testOperationsDuringInit()
}
addItemView("Test cancel with pending operations") {
testCancelWithPending()
}
addItemView("Test reinstall with operations") {
testReinstallWithOperations()
}
addItemView("Clean up all") {
cleanUp()
}
}
}
}
}

private fun testRapidOperations() {
// Install and immediately call multiple operations
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(EDGE_TEST_TAG)
setEnableLog(true, "edge_test")
}

// Rapid fire operations - these should all be queued and executed properly
FloatingX.control(EDGE_TEST_TAG).move(100f, 100f)
FloatingX.control(EDGE_TEST_TAG).show()
FloatingX.control(EDGE_TEST_TAG).move(200f, 200f)
FloatingX.control(EDGE_TEST_TAG).hide()
FloatingX.control(EDGE_TEST_TAG).move(300f, 300f)
FloatingX.control(EDGE_TEST_TAG).show()

Toast.makeText(this, "Rapid operations queued", Toast.LENGTH_SHORT).show()
}

private fun testDuplicateOperations() {
if (!FloatingX.isInstalled(EDGE_TEST_TAG)) {
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(EDGE_TEST_TAG)
}
}

// Multiple show calls - should not cause issues
FloatingX.control(EDGE_TEST_TAG).show()
FloatingX.control(EDGE_TEST_TAG).show()
FloatingX.control(EDGE_TEST_TAG).show()

// Multiple hide calls - should not cause issues
handler.postDelayed({
FloatingX.control(EDGE_TEST_TAG).hide()
FloatingX.control(EDGE_TEST_TAG).hide()
FloatingX.control(EDGE_TEST_TAG).hide()
}, 1000)

Toast.makeText(this, "Duplicate operations test", Toast.LENGTH_SHORT).show()
}

private fun testOperationsDuringInit() {
// Reinstall to trigger initialization
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(EDGE_TEST_TAG + "_init")
}

// Call operations in rapid succession during potential initialization
for (i in 1..5) {
handler.postDelayed({
FloatingX.control(EDGE_TEST_TAG + "_init").move(i * 50f, i * 50f)
if (i % 2 == 0) {
FloatingX.control(EDGE_TEST_TAG + "_init").show()
} else {
FloatingX.control(EDGE_TEST_TAG + "_init").hide()
}
}, i * 50L)
}

Toast.makeText(this, "Operations during init test", Toast.LENGTH_SHORT).show()
}

private fun testCancelWithPending() {
// Install and queue operations
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(EDGE_TEST_TAG + "_cancel")
}

// Queue some operations
FloatingX.control(EDGE_TEST_TAG + "_cancel").move(400f, 400f)
FloatingX.control(EDGE_TEST_TAG + "_cancel").show()

// Cancel immediately - pending operations should be cleared
FloatingX.control(EDGE_TEST_TAG + "_cancel").cancel()

Toast.makeText(this, "Cancel with pending operations", Toast.LENGTH_SHORT).show()
}

private fun testReinstallWithOperations() {
val tag = EDGE_TEST_TAG + "_reinstall"

// Install
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(tag)
}

// Queue operations
FloatingX.control(tag).move(500f, 500f)
FloatingX.control(tag).show()

// Reinstall (should cancel previous and start fresh)
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(tag)
}

// New operations
FloatingX.control(tag).move(600f, 600f)
FloatingX.control(tag).show()

Toast.makeText(this, "Reinstall with operations", Toast.LENGTH_SHORT).show()
}

private fun cleanUp() {
FloatingX.uninstallAll()
Toast.makeText(this, "All floating windows uninstalled", Toast.LENGTH_SHORT).show()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package com.petterp.floatingx.app.test

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.petterp.floatingx.FloatingX
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.R

/**
* Test activity to reproduce and verify the fix for the lifecycle timing issue
* This simulates the problem described in the issue where FloatingX.control()
* operations in onCreate() don't execute properly
*/
class TestLifecycleActivity : AppCompatActivity() {

companion object {
const val TEST_TAG = "lifecycle_test"
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

createLinearLayoutToParent {
addNestedScrollView {
addLinearLayout {
addItemView("Install FloatingX") {
installFloatingX()
}
addItemView("Test onCreate() operations (BEFORE fix: ignored)") {
testOnCreateOperations()
}
addItemView("Show Menu 1 (move then show)") {
showMenu1()
}
addItemView("Hide Menu 1 (move then hide)") {
hideMenu1()
}
addItemView("Test immediate operations") {
testImmediateOperations()
}
addItemView("Test updateViewContent() in onCreate()") {
testUpdateViewContentInOnCreate()
}
addItemView("Cancel FloatingX") {
FloatingX.controlOrNull(TEST_TAG)?.cancel()
}
}
}
}
}

private fun installFloatingX() {
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(TEST_TAG)
setEnableLog(true, "lifecycle_test")
}
Toast.makeText(this, "FloatingX installed", Toast.LENGTH_SHORT).show()
}

private fun testOnCreateOperations() {
// Simulate operations being called immediately in onCreate
// Before fix: these would be ignored if internal view isn't ready
// After fix: these get queued and executed when ready

if (!FloatingX.isInstalled(TEST_TAG)) {
installFloatingX()
}

// Line equivalent to 202: move operation
FloatingX.control(TEST_TAG).move(700f, 700f)

// Line equivalent to 203: hide operation
FloatingX.control(TEST_TAG).hide()

Toast.makeText(this, "onCreate operations executed", Toast.LENGTH_SHORT).show()
}

private fun showMenu1() {
if (!FloatingX.isInstalled(TEST_TAG)) {
installFloatingX()
}

// This should now work properly: move first, then show
// Before fix: move would be ignored on first call
FloatingX.control(TEST_TAG).move(150f, 100f)
FloatingX.control(TEST_TAG).show()

Toast.makeText(this, "showMenu1 executed (move + show)", Toast.LENGTH_SHORT).show()
}

private fun hideMenu1() {
if (!FloatingX.isInstalled(TEST_TAG)) {
installFloatingX()
}

// This should now show the move animation before hiding
// Before fix: would hide immediately without move animation
FloatingX.control(TEST_TAG).move(300f, 300f)
FloatingX.control(TEST_TAG).hide()

Toast.makeText(this, "hideMenu1 executed (move + hide)", Toast.LENGTH_SHORT).show()
}

private fun testImmediateOperations() {
// Test calling operations immediately after install
FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(TEST_TAG + "_immediate")
setEnableLog(true, "immediate_test")
}

// These should work with queuing
FloatingX.control(TEST_TAG + "_immediate").move(400f, 200f)
FloatingX.control(TEST_TAG + "_immediate").show()

Toast.makeText(this, "Immediate operations executed", Toast.LENGTH_SHORT).show()
}

private fun testUpdateViewContentInOnCreate() {
// Test updateViewContent() being called before the floating window is ready
// This simulates the scenario mentioned in the comment

FloatingX.install {
setContext(applicationContext)
setLayout(R.layout.item_floating)
setTag(TEST_TAG + "_content")
setEnableLog(true, "content_test")
}

// Before fix: this would be ignored if viewHolder isn't ready
// After fix: this gets queued and executed when ready
FloatingX.control(TEST_TAG + "_content").updateViewContent { holder ->
holder.setText(R.id.tvItemFx, "Updated in onCreate()!")
}

// Show the floating window to see the result
FloatingX.control(TEST_TAG + "_content").show()

Toast.makeText(this, "updateViewContent() in onCreate() executed", Toast.LENGTH_SHORT).show()
}
}
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ buildscript {
plugins {
alias(libs.plugins.vanniketch.maven.publish) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.android.lirary) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
}
Loading