Reviews onDispose calls to make sure the onDispose is disposing the data from the previous composable, since it can happen AFTER a new composition already took place. So the references must match the old composition.
This commit is contained in:
+15
-7
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.vitorpamplona.amethyst.service.playback.composable
|
package com.vitorpamplona.amethyst.service.playback.composable
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -67,8 +68,19 @@ fun ControlWhenPlayerIsActive(
|
|||||||
|
|
||||||
// Keeps the screen on while playing and viewing videos.
|
// Keeps the screen on while playing and viewing videos.
|
||||||
DisposableEffect(key1 = controller, key2 = view) {
|
DisposableEffect(key1 = controller, key2 = view) {
|
||||||
val listener =
|
val listener = PlayerEventListener(view)
|
||||||
object : Player.Listener {
|
|
||||||
|
controller.addListener(listener)
|
||||||
|
onDispose {
|
||||||
|
controller.removeListener(listener)
|
||||||
|
listener.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PlayerEventListener(
|
||||||
|
val view: View,
|
||||||
|
) : Player.Listener {
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
// doesn't consider the mutex because the screen can turn off if the video
|
// doesn't consider the mutex because the screen can turn off if the video
|
||||||
// being played in the mutex is not visible.
|
// being played in the mutex is not visible.
|
||||||
@@ -76,14 +88,10 @@ fun ControlWhenPlayerIsActive(
|
|||||||
view.keepScreenOn = isPlaying
|
view.keepScreenOn = isPlaying
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
controller.addListener(listener)
|
fun destroy() {
|
||||||
onDispose {
|
|
||||||
if (view.keepScreenOn) {
|
if (view.keepScreenOn) {
|
||||||
view.keepScreenOn = false
|
view.keepScreenOn = false
|
||||||
}
|
}
|
||||||
controller.removeListener(listener)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -54,6 +54,10 @@ fun GetVideoController(
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
// Prepares a VideoPlayer from the foreground service.
|
// Prepares a VideoPlayer from the foreground service.
|
||||||
|
//
|
||||||
|
// TODO: Review this code because a new Disposable Effect can run
|
||||||
|
// before the onDispose of the previous composable and the onDispose
|
||||||
|
// sometimes affects the new variables, not the old ones.
|
||||||
DisposableEffect(key1 = mediaItem.src.videoUri) {
|
DisposableEffect(key1 = mediaItem.src.videoUri) {
|
||||||
// If it is not null, the user might have come back from a playing video, like clicking on
|
// If it is not null, the user might have come back from a playing video, like clicking on
|
||||||
// the notification of the video player.
|
// the notification of the video player.
|
||||||
|
|||||||
+7
-3
@@ -56,8 +56,11 @@ fun Waveform(
|
|||||||
|
|
||||||
val restartFlow = remember { mutableIntStateOf(0) }
|
val restartFlow = remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
|
val myController = mediaControllerState.controller
|
||||||
|
|
||||||
// Keeps the screen on while playing and viewing videos.
|
// Keeps the screen on while playing and viewing videos.
|
||||||
DisposableEffect(key1 = mediaControllerState.controller) {
|
if (myController != null) {
|
||||||
|
DisposableEffect(key1 = myController) {
|
||||||
val listener =
|
val listener =
|
||||||
object : Player.Listener {
|
object : Player.Listener {
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
@@ -69,8 +72,9 @@ fun Waveform(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaControllerState.controller?.addListener(listener)
|
myController.addListener(listener)
|
||||||
onDispose { mediaControllerState.controller?.removeListener(listener) }
|
onDispose { myController.removeListener(listener) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = restartFlow.intValue) {
|
LaunchedEffect(key1 = restartFlow.intValue) {
|
||||||
|
|||||||
+4
-2
@@ -39,7 +39,7 @@ object BackgroundMedia {
|
|||||||
fun removeBackgroundControllerAndReleaseIt() {
|
fun removeBackgroundControllerAndReleaseIt() {
|
||||||
bgInstance.value?.let {
|
bgInstance.value?.let {
|
||||||
PlaybackServiceClient.removeController(it)
|
PlaybackServiceClient.removeController(it)
|
||||||
clearBackground()
|
bgInstance.tryEmit(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +47,9 @@ object BackgroundMedia {
|
|||||||
bgInstance.tryEmit(mediaControllerState)
|
bgInstance.tryEmit(mediaControllerState)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearBackground() {
|
fun clearBackground(mediaControllerState: MediaControllerState) {
|
||||||
|
if (bgInstance.value == mediaControllerState) {
|
||||||
bgInstance.tryEmit(null)
|
bgInstance.tryEmit(null)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -58,9 +58,8 @@ fun rememberIsInPipMode(): Boolean {
|
|||||||
Consumer<PictureInPictureModeChangedInfo> { info ->
|
Consumer<PictureInPictureModeChangedInfo> { info ->
|
||||||
pipMode = info.isInPictureInPictureMode
|
pipMode = info.isInPictureInPictureMode
|
||||||
}
|
}
|
||||||
activity.addOnPictureInPictureModeChangedListener(
|
|
||||||
observer,
|
activity.addOnPictureInPictureModeChangedListener(observer)
|
||||||
)
|
|
||||||
onDispose { activity.removeOnPictureInPictureModeChangedListener(observer) }
|
onDispose { activity.removeOnPictureInPictureModeChangedListener(observer) }
|
||||||
}
|
}
|
||||||
return pipMode
|
return pipMode
|
||||||
|
|||||||
+1
-1
@@ -81,7 +81,7 @@ fun PipVideo(controller: MediaControllerState) {
|
|||||||
DisposableEffect(controller) {
|
DisposableEffect(controller) {
|
||||||
BackgroundMedia.switchKeepPlaying(controller)
|
BackgroundMedia.switchKeepPlaying(controller)
|
||||||
onDispose {
|
onDispose {
|
||||||
BackgroundMedia.clearBackground()
|
BackgroundMedia.clearBackground(controller)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -74,12 +73,6 @@ fun NewUserMetadataScreen(
|
|||||||
postViewModel.load(accountViewModel.account)
|
postViewModel.load(accountViewModel.account)
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(Unit) {
|
|
||||||
onDispose {
|
|
||||||
postViewModel.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ fun keyboardAsState(): State<Keyboard> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
|
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
|
||||||
|
|
||||||
onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener) }
|
onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ package com.vitorpamplona.amethyst.ui.screen
|
|||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.lifecycle.ViewModelStore
|
import androidx.lifecycle.ViewModelStore
|
||||||
import androidx.lifecycle.ViewModelStoreOwner
|
import androidx.lifecycle.ViewModelStoreOwner
|
||||||
@@ -55,11 +54,13 @@ fun SetAccountCentricViewModelStore(
|
|||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|
||||||
DisposableEffect(key1 = state) {
|
// moved this clearing activity to the viewmodel account
|
||||||
onDispose {
|
// because the new composable might run before the onDispose.
|
||||||
state.currentViewModelStore.viewModelStore.clear()
|
// DisposableEffect(key1 = state) {
|
||||||
}
|
// onDispose {
|
||||||
}
|
// state.currentViewModelStore.viewModelStore.clear()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
class AccountCentricViewModelStore : ViewModelStoreOwner {
|
class AccountCentricViewModelStore : ViewModelStoreOwner {
|
||||||
|
|||||||
+8
-5
@@ -21,6 +21,7 @@
|
|||||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@@ -216,9 +217,7 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
val launcher: (Intent) -> Unit = {
|
||||||
accountViewModel.account.signer.launcher.registerLauncher(
|
|
||||||
launcher = {
|
|
||||||
try {
|
try {
|
||||||
launcher.launch(it)
|
launcher.launch(it)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -229,12 +228,16 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
|
|||||||
R.string.error_opening_external_signer_description,
|
R.string.error_opening_external_signer_description,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
lifeCycleOwner.lifecycle.addObserver(observer)
|
||||||
|
accountViewModel.account.signer.launcher.registerLauncher(
|
||||||
|
launcher = launcher,
|
||||||
contentResolver = Amethyst.instance::contentResolverFn,
|
contentResolver = Amethyst.instance::contentResolverFn,
|
||||||
)
|
)
|
||||||
onDispose {
|
onDispose {
|
||||||
accountViewModel.account.signer.launcher
|
accountViewModel.account.signer.launcher
|
||||||
.clearLauncher()
|
.clearLauncherIf(launcher)
|
||||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
lifeCycleOwner.lifecycle.removeObserver(observer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -64,8 +64,6 @@ fun TabRelays(
|
|||||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
lifeCycleOwner.lifecycle.addObserver(observer)
|
||||||
onDispose {
|
onDispose {
|
||||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
lifeCycleOwner.lifecycle.removeObserver(observer)
|
||||||
println("Profile Relay Dispose")
|
|
||||||
feedViewModel.unsubscribeTo(user)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-4
@@ -641,8 +641,7 @@ private fun PrepareExternalSignerReceiver(onLogin: (pubkey: String, packageName:
|
|||||||
val activity = getActivity() as MainActivity
|
val activity = getActivity() as MainActivity
|
||||||
|
|
||||||
DisposableEffect(launcher, activity, externalSignerLauncher) {
|
DisposableEffect(launcher, activity, externalSignerLauncher) {
|
||||||
externalSignerLauncher.registerLauncher(
|
val launcher: (Intent) -> Unit = {
|
||||||
launcher = {
|
|
||||||
try {
|
try {
|
||||||
launcher.launch(it)
|
launcher.launch(it)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -657,10 +656,15 @@ private fun PrepareExternalSignerReceiver(onLogin: (pubkey: String, packageName:
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
externalSignerLauncher.registerLauncher(
|
||||||
|
launcher = launcher,
|
||||||
contentResolver = Amethyst.instance::contentResolverFn,
|
contentResolver = Amethyst.instance::contentResolverFn,
|
||||||
)
|
)
|
||||||
onDispose { externalSignerLauncher.clearLauncher() }
|
onDispose {
|
||||||
|
externalSignerLauncher.clearLauncherIf(launcher)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(externalSignerLauncher) {
|
LaunchedEffect(externalSignerLauncher) {
|
||||||
|
|||||||
+3
-1
@@ -114,10 +114,12 @@ class ExternalSignerLauncher(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Call this function when the activity is destroyed or is about to be replaced. */
|
/** Call this function when the activity is destroyed or is about to be replaced. */
|
||||||
fun clearLauncher() {
|
fun clearLauncherIf(launcher: ((Intent) -> Unit)) {
|
||||||
|
if (signerAppLauncher == launcher) {
|
||||||
this.signerAppLauncher = null
|
this.signerAppLauncher = null
|
||||||
this.contentResolver = null
|
this.contentResolver = null
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun newResult(data: Intent) {
|
fun newResult(data: Intent) {
|
||||||
val results = data.getStringExtra("results")
|
val results = data.getStringExtra("results")
|
||||||
|
|||||||
Reference in New Issue
Block a user