diff --git a/app/src/androidTest/java/roboyard/eclabs/achievements/AchievementManagerTest.java b/app/src/androidTest/java/roboyard/eclabs/achievements/AchievementManagerTest.java index 82762ae2..4cf22aa2 100644 --- a/app/src/androidTest/java/roboyard/eclabs/achievements/AchievementManagerTest.java +++ b/app/src/androidTest/java/roboyard/eclabs/achievements/AchievementManagerTest.java @@ -16,6 +16,7 @@ import org.junit.Test; import org.junit.runner.RunWith; +import java.lang.reflect.Field; import java.util.List; /** @@ -104,6 +105,24 @@ public void testUnlockUnknownAchievement() { assertFalse("Unknown achievement unlock should return false", result); } + /** + * Test that setUiNotifier keeps a strong reference until explicitly cleared. + */ + @Test + public void testSetUiNotifierStoresStrongReference() throws Exception { + AchievementManager.UiNotifier notifier = message -> {}; + + achievementManager.setUiNotifier(notifier); + + Field uiNotifierField = AchievementManager.class.getDeclaredField("uiNotifier"); + uiNotifierField.setAccessible(true); + assertSame("UiNotifier should be stored strongly", notifier, uiNotifierField.get(achievementManager)); + + achievementManager.setUiNotifier(null); + + assertNull("UiNotifier should clear when set to null", uiNotifierField.get(achievementManager)); + } + // ==================== PROGRESSION ACHIEVEMENTS TESTS ==================== /** diff --git a/app/src/main/java/roboyard/logic/achievements/AchievementManager.kt b/app/src/main/java/roboyard/logic/achievements/AchievementManager.kt index bce2da20..99d312fb 100644 --- a/app/src/main/java/roboyard/logic/achievements/AchievementManager.kt +++ b/app/src/main/java/roboyard/logic/achievements/AchievementManager.kt @@ -44,7 +44,7 @@ class AchievementManager private constructor(context: Context) { private val achievements: MutableMap? private var unlockListener: AchievementUnlockListener? = null private var currentActivity: WeakReference? = null - private var uiNotifier: WeakReference? = null + private var uiNotifier: UiNotifier? = null fun interface UiNotifier { fun showMessage(message: String) @@ -84,9 +84,9 @@ class AchievementManager private constructor(context: Context) { fun setCurrentActivity(activity: Activity?) { this.currentActivity = WeakReference(activity) // Create Android UiNotifier adapter when activity is set - if (activity != null) { + this.uiNotifier = if (activity != null) { val ctx = this.context - this.uiNotifier = WeakReference(UiNotifier { message -> + UiNotifier { message -> Handler(Looper.getMainLooper()).post { try { Toast.makeText(ctx, message, Toast.LENGTH_LONG).show() @@ -94,7 +94,9 @@ class AchievementManager private constructor(context: Context) { e(e, "[UPDATE_NUDGE] Failed to show toast") } } - }) + } + } else { + null } // Show any pending update nudge now that we have an activity if (activity != null && pendingNudgeVersion != null) { @@ -108,7 +110,7 @@ class AchievementManager private constructor(context: Context) { * Use this on non-Android platforms (KMP/iOS) instead of setCurrentActivity. */ fun setUiNotifier(notifier: UiNotifier?) { - this.uiNotifier = if (notifier != null) WeakReference(notifier) else null + this.uiNotifier = notifier } /** @@ -135,7 +137,7 @@ class AchievementManager private constructor(context: Context) { private fun showUpdateNudgeInternal(version: String?) { val message = context.getString(R.string.update_available_nudge, version) - uiNotifier?.get()?.showMessage(message) ?: d("[UPDATE_NUDGE] No UiNotifier available, cannot show nudge: version=%s", version) + uiNotifier?.showMessage(message) ?: d("[UPDATE_NUDGE] No UiNotifier available, cannot show nudge: version=%s", version) } /**