fix: move ContentResolver.getType off main thread in share intent consumer

The DisposableEffect's OnNewIntentListener is invoked on the main thread
  when a new ACTION_SEND intent arrives (e.g. SwiftKey sharing a GIF). It
  called contentResolver.getType() synchronously, which can Binder-IPC to
  a remote content provider and block for tens to hundreds of milliseconds.

  Use rememberCoroutineScope() + scope.launch(Dispatchers.IO) since
  DisposableEffect has no coroutine scope of its own. The scope is tied to
  composition and cancels cleanly on disposal.

  Also fix a latent bug: the consumer read activity.intent instead of the
  intent parameter passed into the callback. activity.intent is the
  Activity's launch intent, not the new one that triggered the listener,
  so subsequent intents would reuse the first intent's URI.
This commit is contained in:
davotoula
2026-04-10 11:40:24 +02:00
parent 6aa949a4f4
commit d506330cc7
@@ -55,6 +55,7 @@ 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
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -124,6 +125,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class, FlowPreview::class) @OptIn(ExperimentalMaterial3Api::class, FlowPreview::class)
@@ -144,6 +146,7 @@ fun ShortNotePostScreen(
val context = LocalContext.current val context = LocalContext.current
val activity = context.getActivity() val activity = context.getActivity()
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
postViewModel.initWritingAssistant(context) postViewModel.initWritingAssistant(context)
@@ -177,9 +180,12 @@ fun ShortNotePostScreen(
postViewModel.addToMessage(it) postViewModel.addToMessage(it)
} }
IntentCompat.getParcelableExtra(activity.intent, Intent.EXTRA_STREAM, Uri::class.java)?.let { // Use the `intent` parameter (the new intent), not activity.intent (the launch intent).
val mediaType = context.contentResolver.getType(it) IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)?.let { uri ->
postViewModel.selectImage(persistentListOf(SelectedMedia(it, mediaType))) scope.launch(Dispatchers.IO) {
val mediaType = context.contentResolver.getType(uri)
postViewModel.selectImage(persistentListOf(SelectedMedia(uri, mediaType)))
}
} }
} }
} }