diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml
index 1b639de..d8bf7ea 100644
--- a/composeApp/src/androidMain/AndroidManifest.xml
+++ b/composeApp/src/androidMain/AndroidManifest.xml
@@ -18,15 +18,30 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@android:style/Theme.Material.Light.NoActionBar">
+
+
+
+
+
+
+
+
+
+
+
+
{
@OptIn(ExperimentalMaterial3ExpressiveApi::class, ExperimentalMaterial3Api::class)
@Composable
-fun App(
- viewModel: NostrViewModel,
- openRoomId: Long? = null
-) {
+fun App(viewModel: NostrViewModel) {
val context = LocalContext.current
val navController = rememberNavController()
val scope = rememberCoroutineScope()
@@ -114,18 +112,94 @@ fun App(
LaunchedEffect(emptySecret) {
// Navigate to the home screen if the secret is already set
- if (emptySecret == false && openRoomId == null) {
+ if (emptySecret == false) {
navController.navigate(Screen.Home) {
popUpTo(Screen.Onboarding) { inclusive = true }
}
}
}
- LaunchedEffect(openRoomId) {
- if (openRoomId != null) {
- navController.navigate(Screen.Chat(openRoomId)) {
- popUpTo(Screen.Home) { saveState = true }
- }
+ NavHost(
+ navController = navController,
+ startDestination = if (emptySecret == false) Screen.Home else Screen.Onboarding
+ ) {
+ composable { backStackEntry ->
+ OnboardingScreen(
+ onOpenImport = { navController.navigate(Screen.Import) },
+ onOpenNew = { navController.navigate(Screen.NewIdentity) }
+ )
+ }
+ composable { backStackEntry ->
+ val isCreating by viewModel.isCreating.collectAsState()
+
+ ImportScreen(
+ isLoading = isCreating,
+ onBack = { navController.popBackStack() },
+ onSave = { secret ->
+ viewModel.importIdentity(secret)
+ }
+ )
+ }
+ composable { backStackEntry ->
+ val isCreating by viewModel.isCreating.collectAsState()
+
+ NewIdentityScreen(
+ isLoading = isCreating,
+ onBack = { navController.popBackStack() },
+ onSave = { name, bio, uri ->
+ val contentType = uri?.let { context.contentResolver.getType(it) }
+ val picture = uri?.let {
+ context.contentResolver.openInputStream(it)?.use { input ->
+ input.readBytes()
+ }
+ }
+ viewModel.createIdentity(name, bio, picture, contentType)
+ }
+ )
+ }
+ composable { backStackEntry ->
+ HomeScreen(
+ onOpenChat = { id -> navController.navigate(Screen.Chat(id)) },
+ onNewChat = { navController.navigate(Screen.NewChat) }
+ )
+ }
+ composable(
+ deepLinks = listOf(
+ navDeepLink(basePath = "coop://chat")
+ )
+ ) { backStackEntry ->
+ val chat: Screen.Chat = backStackEntry.toRoute()
+ ChatScreen(
+ id = chat.id,
+ onBack = { navController.popBackStack() },
+ )
+ }
+ composable { backStackEntry ->
+ val profile: Screen.Profile = backStackEntry.toRoute()
+ ProfileScreen(
+ pubkey = profile.pubkey,
+ onBack = { navController.popBackStack() },
+ )
+ }
+ composable { backStackEntry ->
+ NewChatScreen(
+ onBack = { navController.popBackStack() },
+ )
+ }
+ composable { backStackEntry ->
+ ScanScreen(
+ onBack = { navController.popBackStack() },
+ )
+ }
+ composable { backStackEntry ->
+ MyQrScreen(
+ onBack = { navController.popBackStack() },
+ )
+ }
+ composable { backStackEntry ->
+ RelayScreen(
+ onBack = { navController.popBackStack() },
+ )
}
}
@@ -183,86 +257,6 @@ fun App(
}
}
}
-
- NavHost(
- navController = navController,
- startDestination = if (emptySecret == false) Screen.Home else Screen.Onboarding
- ) {
- composable { backStackEntry ->
- OnboardingScreen(
- onOpenImport = { navController.navigate(Screen.Import) },
- onOpenNew = { navController.navigate(Screen.NewIdentity) }
- )
- }
- composable { backStackEntry ->
- val isCreating by viewModel.isCreating.collectAsState()
-
- ImportScreen(
- isLoading = isCreating,
- onBack = { navController.popBackStack() },
- onSave = { secret ->
- viewModel.importIdentity(secret)
- }
- )
- }
- composable { backStackEntry ->
- val isCreating by viewModel.isCreating.collectAsState()
-
- NewIdentityScreen(
- isLoading = isCreating,
- onBack = { navController.popBackStack() },
- onSave = { name, bio, uri ->
- val contentType = uri?.let { context.contentResolver.getType(it) }
- val picture = uri?.let {
- context.contentResolver.openInputStream(it)?.use { input ->
- input.readBytes()
- }
- }
- viewModel.createIdentity(name, bio, picture, contentType)
- }
- )
- }
- composable { backStackEntry ->
- HomeScreen(
- onOpenChat = { id -> navController.navigate(Screen.Chat(id)) },
- onNewChat = { navController.navigate(Screen.NewChat) }
- )
- }
- composable { backStackEntry ->
- val chat: Screen.Chat = backStackEntry.toRoute()
- ChatScreen(
- id = chat.id,
- onBack = { navController.popBackStack() },
- )
- }
- composable { backStackEntry ->
- val profile: Screen.Profile = backStackEntry.toRoute()
- ProfileScreen(
- pubkey = profile.pubkey,
- onBack = { navController.popBackStack() },
- )
- }
- composable { backStackEntry ->
- NewChatScreen(
- onBack = { navController.popBackStack() },
- )
- }
- composable { backStackEntry ->
- ScanScreen(
- onBack = { navController.popBackStack() },
- )
- }
- composable { backStackEntry ->
- MyQrScreen(
- onBack = { navController.popBackStack() },
- )
- }
- composable { backStackEntry ->
- RelayScreen(
- onBack = { navController.popBackStack() },
- )
- }
- }
}
}
}
diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt
index ff7d854..3daad42 100644
--- a/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt
+++ b/composeApp/src/androidMain/kotlin/su/reya/coop/MainActivity.kt
@@ -6,10 +6,22 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
+import androidx.activity.viewModels
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
import su.reya.coop.coop.storage.SecretStore
class MainActivity : ComponentActivity() {
+ private val viewModel: NostrViewModel by viewModels {
+ object : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ val secretStore = SecretStore(this@MainActivity)
+ return NostrViewModel(NostrManager.instance, secretStore) as T
+ }
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
enableEdgeToEdge()
@@ -17,25 +29,19 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
val serviceIntent = Intent(this, NostrForegroundService::class.java)
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
- val roomId = intent.getLongExtra("room_id", -1L)
- val secretStore = SecretStore(this)
- val viewModel = NostrViewModel(NostrManager.instance, secretStore)
-
splashScreen.setKeepOnScreenCondition {
viewModel.emptySecret.value == null
}
setContent {
- App(
- viewModel = viewModel,
- openRoomId = if (roomId != -1L) roomId else null
- )
+ App(viewModel = viewModel)
}
}
diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt
index 54a2349..48e9bc3 100644
--- a/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt
+++ b/composeApp/src/androidMain/kotlin/su/reya/coop/NostrForegroundService.kt
@@ -10,6 +10,7 @@ import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
+import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
import kotlinx.coroutines.CoroutineScope
@@ -111,10 +112,14 @@ class NostrForegroundService : Service() {
}
private fun showNewMessageNotification(roomId: Long, message: String) {
- val intent = Intent(this, MainActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
- putExtra("room_id", roomId)
- }
+ val deepLinkUri = "coop://chat/$roomId".toUri()
+
+ val intent = Intent(
+ Intent.ACTION_VIEW,
+ deepLinkUri,
+ this,
+ MainActivity::class.java
+ )
val pendingIntent = PendingIntent.getActivity(
this,
@@ -122,7 +127,7 @@ class NostrForegroundService : Service() {
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
-
+
val notification = NotificationCompat.Builder(this, "nostr_messages")
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("You received a new message")