diff --git a/app/src/main/java/com/anytypeio/anytype/device/AnytypePushService.kt b/app/src/main/java/com/anytypeio/anytype/device/AnytypePushService.kt index 9d195d0809..6308a87245 100644 --- a/app/src/main/java/com/anytypeio/anytype/device/AnytypePushService.kt +++ b/app/src/main/java/com/anytypeio/anytype/device/AnytypePushService.kt @@ -11,6 +11,8 @@ import androidx.core.app.NotificationCompat import com.anytypeio.anytype.R import com.anytypeio.anytype.app.AndroidApplication import com.anytypeio.anytype.core_models.DecryptedPushContent +import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_ui.views.Relations1 import com.anytypeio.anytype.domain.device.DeviceTokenStoringService import com.anytypeio.anytype.presentation.notifications.DecryptionPushContentService import com.anytypeio.anytype.ui.main.MainActivity @@ -87,11 +89,11 @@ class AnytypePushService : FirebaseMessagingService() { Timber.d("New message received: $message") // Create an intent to open the app when notification is tapped - //todo extra task on Navigation val intent = Intent(this, MainActivity::class.java).apply { + action = ACTION_OPEN_CHAT flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP - putExtra(EXTRA_CHAT_ID, message.chatId) - putExtra(EXTRA_SPACE_ID, message.spaceName) + putExtra(Relations.CHAT_ID, message.chatId) + putExtra(Relations.SPACE_ID, message.spaceName) } val pendingIntent = PendingIntent.getActivity( @@ -145,9 +147,7 @@ class AnytypePushService : FirebaseMessagingService() { private const val PAYLOAD_KEY = "x-any-payload" private const val KEY_ID_KEY = "x-any-key-id" private const val CHANNEL_NAME = "Chat Messages" - val EXTRA_CHAT_ID = "chatId" - val EXTRA_SPACE_ID = "spaceId" - private const val NOTIFICATION_REQUEST_CODE = 100 + const val ACTION_OPEN_CHAT = "com.anytype.ACTION_OPEN_CHAT" } } \ No newline at end of file diff --git a/app/src/main/java/com/anytypeio/anytype/ui/chats/ChatFragment.kt b/app/src/main/java/com/anytypeio/anytype/ui/chats/ChatFragment.kt index edd2fedb43..af55b4796b 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/chats/ChatFragment.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/chats/ChatFragment.kt @@ -69,7 +69,7 @@ class ChatFragment : BaseComposeFragment() { private val vm by viewModels { factory } - private val ctx get() = arg(CTX_KEY) + val ctx get() = arg(CTX_KEY) private val space get() = arg(SPACE_KEY) // Rendering diff --git a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt index b67c274f5f..359e72529f 100644 --- a/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt +++ b/app/src/main/java/com/anytypeio/anytype/ui/main/MainActivity.kt @@ -20,6 +20,7 @@ import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavOptions import androidx.navigation.NavOptions.* import androidx.navigation.findNavController +import androidx.navigation.fragment.NavHostFragment import com.anytypeio.anytype.BuildConfig import com.anytypeio.anytype.R import com.anytypeio.anytype.analytics.base.EventsDictionary @@ -29,6 +30,7 @@ import com.anytypeio.anytype.app.DefaultAppActionManager import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.ThemeMode import com.anytypeio.anytype.core_models.Wallpaper +import com.anytypeio.anytype.core_models.multiplayer.SpaceType import com.anytypeio.anytype.core_models.primitives.SpaceId import com.anytypeio.anytype.core_utils.ext.Mimetype import com.anytypeio.anytype.core_utils.ext.parseActionSendMultipleUris @@ -36,6 +38,7 @@ import com.anytypeio.anytype.core_utils.ext.parseActionSendUri import com.anytypeio.anytype.core_utils.ext.toast import com.anytypeio.anytype.core_utils.insets.EDGE_TO_EDGE_MIN_SDK import com.anytypeio.anytype.core_utils.tools.FeatureToggles +import com.anytypeio.anytype.device.AnytypePushService import com.anytypeio.anytype.di.common.componentManager import com.anytypeio.anytype.domain.base.BaseUseCase import com.anytypeio.anytype.domain.theme.GetTheme @@ -205,13 +208,37 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr is Command.Deeplink.DeepLinkToObjectNotWorking -> { toast(getString(R.string.multiplayer_deeplink_to_your_object_error)) } + is Command.LaunchChat -> { + runCatching { + val controller = findNavController(R.id.fragment) + controller.popBackStack(R.id.vaultScreen, false) + controller.navigate( + R.id.actionOpenSpaceFromVault, + HomeScreenFragment.args( + space = command.space, + deeplink = null + ) + ) + controller.navigate( + R.id.chatScreen, + ChatFragment.args( + space = command.space, + ctx = command.chat + ) + ) + }.onFailure { + if (BuildConfig.DEBUG) { + toast("Failed to open chat from push notification") + } + } + } is Command.Deeplink.DeepLinkToObject -> { when(val effect = command.sideEffect) { is Command.Deeplink.DeepLinkToObject.SideEffect.SwitchSpace -> { runCatching { val controller = findNavController(R.id.fragment) controller.popBackStack(R.id.vaultScreen, false) - if (effect.chat != null) { + if (effect.chat != null && effect.spaceType == SpaceType.CHAT) { controller.navigate( R.id.actionOpenChatFromVault, ChatFragment.args( @@ -449,6 +476,34 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr AnytypeNotificationService.NOTIFICATION_INTENT_ACTION -> { proceedWithNotificationIntent(intent) } + AnytypePushService.ACTION_OPEN_CHAT -> { + proceedWithOpenChatIntent(intent) + } + } + } + + private fun proceedWithOpenChatIntent(intent: Intent) { + val chatId = intent.getStringExtra(Relations.CHAT_ID) + val spaceId = intent.getStringExtra(Relations.SPACE_ID) + if (!chatId.isNullOrEmpty() && !spaceId.isNullOrEmpty()) { + if (!isChatFragmentVisible(chatId)) { + vm.onOpenChatTriggeredByPush( + chatId = chatId, + spaceId = spaceId + ) + } else { + // Do nothing, already there. + } + } + } + + private fun isChatFragmentVisible(chatId: String): Boolean { + val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment) as? NavHostFragment + val currentFragment = navHostFragment?.childFragmentManager?.fragments?.firstOrNull() + return if (currentFragment is ChatFragment) { + currentFragment.ctx == chatId + } else { + false } } @@ -720,7 +775,6 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), AppNavigation.Pr } companion object { - const val AUTO_UPDATE_URL = "https://fra1.digitaloceanspaces.com/anytype-release/latest-android.json" const val SHARE_DIALOG_LABEL = "anytype.dialog.share.label" const val SHARE_IMAGE_INTENT_PATTERN = "image/" const val SHARE_VIDEO_INTENT_PATTERN = "video/" diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt index 18ec140122..a2ee3e7a63 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/main/MainViewModel.kt @@ -12,6 +12,7 @@ import com.anytypeio.anytype.core_models.NotificationPayload import com.anytypeio.anytype.core_models.NotificationStatus import com.anytypeio.anytype.core_models.Wallpaper import com.anytypeio.anytype.core_models.exceptions.NeedToUpdateApplicationException +import com.anytypeio.anytype.core_models.multiplayer.SpaceType import com.anytypeio.anytype.core_utils.ext.cancel import com.anytypeio.anytype.domain.account.AwaitAccountStartManager import com.anytypeio.anytype.domain.account.InterceptAccountStatus @@ -451,6 +452,31 @@ class MainViewModel( } } + fun onOpenChatTriggeredByPush(chatId: String, spaceId: String) { + viewModelScope.launch { + if (spaceManager.get() != spaceId) { + spaceManager.set(spaceId) + .onSuccess { + commands.emit( + Command.LaunchChat( + space = spaceId, + chat = chatId + ) + ) + }.onFailure { + Timber.e(it, "Error while switching space when launching chat from push notification") + } + } else { + commands.emit( + Command.LaunchChat( + space = spaceId, + chat = chatId + ) + ) + } + } + } + sealed class Command { data class ShowDeletedAccountScreen(val deadline: Long) : Command() data object LogoutDueToAccountDeletion : Command() @@ -467,6 +493,11 @@ class MainViewModel( data object Notifications: Command() data object RequestNotificationPermission: Command() + data class LaunchChat( + val space: Id, + val chat: Id + ): Command() + data class Navigate(val destination: OpenObjectNavigation): Command() sealed class Deeplink : Command() { @@ -480,7 +511,8 @@ class MainViewModel( sealed class SideEffect { data class SwitchSpace( val home: Id, - val chat: Id? + val chat: Id? = null, + val spaceType: SpaceType? = null ): SideEffect() } }