mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2025-09-27 08:17:50 +02:00
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:
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.service.playback.composable
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -67,23 +68,30 @@ fun ControlWhenPlayerIsActive(
|
||||
|
||||
// Keeps the screen on while playing and viewing videos.
|
||||
DisposableEffect(key1 = controller, key2 = view) {
|
||||
val listener =
|
||||
object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
// doesn't consider the mutex because the screen can turn off if the video
|
||||
// being played in the mutex is not visible.
|
||||
if (view.keepScreenOn != isPlaying) {
|
||||
view.keepScreenOn = isPlaying
|
||||
}
|
||||
}
|
||||
}
|
||||
val listener = PlayerEventListener(view)
|
||||
|
||||
controller.addListener(listener)
|
||||
onDispose {
|
||||
if (view.keepScreenOn) {
|
||||
view.keepScreenOn = false
|
||||
}
|
||||
controller.removeListener(listener)
|
||||
listener.destroy()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerEventListener(
|
||||
val view: View,
|
||||
) : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
// doesn't consider the mutex because the screen can turn off if the video
|
||||
// being played in the mutex is not visible.
|
||||
if (view.keepScreenOn != isPlaying) {
|
||||
view.keepScreenOn = isPlaying
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
if (view.keepScreenOn) {
|
||||
view.keepScreenOn = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -54,6 +54,10 @@ fun GetVideoController(
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
// 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) {
|
||||
// If it is not null, the user might have come back from a playing video, like clicking on
|
||||
// the notification of the video player.
|
||||
|
@@ -56,21 +56,25 @@ fun Waveform(
|
||||
|
||||
val restartFlow = remember { mutableIntStateOf(0) }
|
||||
|
||||
val myController = mediaControllerState.controller
|
||||
|
||||
// Keeps the screen on while playing and viewing videos.
|
||||
DisposableEffect(key1 = mediaControllerState.controller) {
|
||||
val listener =
|
||||
object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
// doesn't consider the mutex because the screen can turn off if the video
|
||||
// being played in the mutex is not visible.
|
||||
if (isPlaying) {
|
||||
restartFlow.intValue += 1
|
||||
if (myController != null) {
|
||||
DisposableEffect(key1 = myController) {
|
||||
val listener =
|
||||
object : Player.Listener {
|
||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||
// doesn't consider the mutex because the screen can turn off if the video
|
||||
// being played in the mutex is not visible.
|
||||
if (isPlaying) {
|
||||
restartFlow.intValue += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mediaControllerState.controller?.addListener(listener)
|
||||
onDispose { mediaControllerState.controller?.removeListener(listener) }
|
||||
myController.addListener(listener)
|
||||
onDispose { myController.removeListener(listener) }
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = restartFlow.intValue) {
|
||||
|
@@ -39,7 +39,7 @@ object BackgroundMedia {
|
||||
fun removeBackgroundControllerAndReleaseIt() {
|
||||
bgInstance.value?.let {
|
||||
PlaybackServiceClient.removeController(it)
|
||||
clearBackground()
|
||||
bgInstance.tryEmit(null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,9 @@ object BackgroundMedia {
|
||||
bgInstance.tryEmit(mediaControllerState)
|
||||
}
|
||||
|
||||
fun clearBackground() {
|
||||
bgInstance.tryEmit(null)
|
||||
fun clearBackground(mediaControllerState: MediaControllerState) {
|
||||
if (bgInstance.value == mediaControllerState) {
|
||||
bgInstance.tryEmit(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -58,9 +58,8 @@ fun rememberIsInPipMode(): Boolean {
|
||||
Consumer<PictureInPictureModeChangedInfo> { info ->
|
||||
pipMode = info.isInPictureInPictureMode
|
||||
}
|
||||
activity.addOnPictureInPictureModeChangedListener(
|
||||
observer,
|
||||
)
|
||||
|
||||
activity.addOnPictureInPictureModeChangedListener(observer)
|
||||
onDispose { activity.removeOnPictureInPictureModeChangedListener(observer) }
|
||||
}
|
||||
return pipMode
|
||||
|
@@ -81,7 +81,7 @@ fun PipVideo(controller: MediaControllerState) {
|
||||
DisposableEffect(controller) {
|
||||
BackgroundMedia.switchKeepPlaying(controller)
|
||||
onDispose {
|
||||
BackgroundMedia.clearBackground()
|
||||
BackgroundMedia.clearBackground(controller)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -40,7 +40,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -74,12 +73,6 @@ fun NewUserMetadataScreen(
|
||||
postViewModel.load(accountViewModel.account)
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
postViewModel.clear()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
|
@@ -104,7 +104,6 @@ fun keyboardAsState(): State<Keyboard> {
|
||||
}
|
||||
}
|
||||
view.viewTreeObserver.addOnGlobalLayoutListener(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.CompositionLocalProvider
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.lifecycle.ViewModelStore
|
||||
import androidx.lifecycle.ViewModelStoreOwner
|
||||
@@ -55,11 +54,13 @@ fun SetAccountCentricViewModelStore(
|
||||
content()
|
||||
}
|
||||
|
||||
DisposableEffect(key1 = state) {
|
||||
onDispose {
|
||||
state.currentViewModelStore.viewModelStore.clear()
|
||||
}
|
||||
}
|
||||
// moved this clearing activity to the viewmodel account
|
||||
// because the new composable might run before the onDispose.
|
||||
// DisposableEffect(key1 = state) {
|
||||
// onDispose {
|
||||
// state.currentViewModelStore.viewModelStore.clear()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
class AccountCentricViewModelStore : ViewModelStoreOwner {
|
||||
|
@@ -21,6 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.screen.loggedIn
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -216,25 +217,27 @@ private fun ListenToExternalSignerIfNeeded(accountViewModel: AccountViewModel) {
|
||||
}
|
||||
}
|
||||
|
||||
val launcher: (Intent) -> Unit = {
|
||||
try {
|
||||
launcher.launch(it)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.e("Signer", "Error opening Signer app", e)
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.error_opening_external_signer,
|
||||
R.string.error_opening_external_signer_description,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
||||
accountViewModel.account.signer.launcher.registerLauncher(
|
||||
launcher = {
|
||||
try {
|
||||
launcher.launch(it)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.e("Signer", "Error opening Signer app", e)
|
||||
accountViewModel.toastManager.toast(
|
||||
R.string.error_opening_external_signer,
|
||||
R.string.error_opening_external_signer_description,
|
||||
)
|
||||
}
|
||||
},
|
||||
launcher = launcher,
|
||||
contentResolver = Amethyst.instance::contentResolverFn,
|
||||
)
|
||||
onDispose {
|
||||
accountViewModel.account.signer.launcher
|
||||
.clearLauncher()
|
||||
.clearLauncherIf(launcher)
|
||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
|
@@ -64,8 +64,6 @@ fun TabRelays(
|
||||
lifeCycleOwner.lifecycle.addObserver(observer)
|
||||
onDispose {
|
||||
lifeCycleOwner.lifecycle.removeObserver(observer)
|
||||
println("Profile Relay Dispose")
|
||||
feedViewModel.unsubscribeTo(user)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -641,26 +641,30 @@ private fun PrepareExternalSignerReceiver(onLogin: (pubkey: String, packageName:
|
||||
val activity = getActivity() as MainActivity
|
||||
|
||||
DisposableEffect(launcher, activity, externalSignerLauncher) {
|
||||
externalSignerLauncher.registerLauncher(
|
||||
launcher = {
|
||||
try {
|
||||
launcher.launch(it)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.e("Signer", "Error opening Signer app", e)
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast
|
||||
.makeText(
|
||||
Amethyst.instance,
|
||||
R.string.error_opening_external_signer,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
val launcher: (Intent) -> Unit = {
|
||||
try {
|
||||
launcher.launch(it)
|
||||
} catch (e: Exception) {
|
||||
if (e is CancellationException) throw e
|
||||
Log.e("Signer", "Error opening Signer app", e)
|
||||
scope.launch(Dispatchers.Main) {
|
||||
Toast
|
||||
.makeText(
|
||||
Amethyst.instance,
|
||||
R.string.error_opening_external_signer,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
externalSignerLauncher.registerLauncher(
|
||||
launcher = launcher,
|
||||
contentResolver = Amethyst.instance::contentResolverFn,
|
||||
)
|
||||
onDispose { externalSignerLauncher.clearLauncher() }
|
||||
onDispose {
|
||||
externalSignerLauncher.clearLauncherIf(launcher)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(externalSignerLauncher) {
|
||||
|
@@ -114,9 +114,11 @@ class ExternalSignerLauncher(
|
||||
}
|
||||
|
||||
/** Call this function when the activity is destroyed or is about to be replaced. */
|
||||
fun clearLauncher() {
|
||||
this.signerAppLauncher = null
|
||||
this.contentResolver = null
|
||||
fun clearLauncherIf(launcher: ((Intent) -> Unit)) {
|
||||
if (signerAppLauncher == launcher) {
|
||||
this.signerAppLauncher = null
|
||||
this.contentResolver = null
|
||||
}
|
||||
}
|
||||
|
||||
fun newResult(data: Intent) {
|
||||
|
Reference in New Issue
Block a user