feat: Implement deep linking functionality
This commit introduces deep linking capabilities to the application. - Added `DeepLinkActivity` to handle incoming deep links and navigate users to the appropriate content within the app. - Created `DeepLinkUtils` to provide utility functions for parsing and creating deep link URIs. - Updated `MainActivity` to receive and process deep link data from `DeepLinkActivity`. - Modified `ChatRouterScreen` and its `ViewModel` to handle navigation based on deep link parameters. - Updated `AndroidManifest.xml` to include intent filters for `DeepLinkActivity` to capture relevant URL schemes and paths. - Added `assetlinks.json` and `README_APP_LINKS.md` to support Android App Links verification. - Changed the `applicationId` in `app/build.gradle.kts` from "chat.revolt" to "chat.peptide". - Renamed project in `.idea/.name` from "PEP" to "Revolt". - Changed the default route in `ChatRouterScreen` from `Settings` to `Home`.
This commit is contained in:
parent
b89b1e9415
commit
9d53be9e41
|
|
@ -1 +1 @@
|
|||
PEP
|
||||
Revolt
|
||||
|
|
@ -77,7 +77,7 @@ android {
|
|||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "chat.revolt"
|
||||
applicationId = "chat.peptide"
|
||||
minSdk = 24
|
||||
targetSdk = 35
|
||||
versionCode = Integer.parseInt("001_003_106".replace("_", ""), 10)
|
||||
|
|
|
|||
|
|
@ -161,6 +161,34 @@
|
|||
android:value="true" />
|
||||
</service>
|
||||
|
||||
<!-- DeepLink Activity for handling App Links -->
|
||||
<activity
|
||||
android:name=".activities.DeepLinkActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Revolt">
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data android:scheme="https" />
|
||||
<data android:scheme="http" />
|
||||
<data android:host="peptide.chat" />
|
||||
<data android:host="app.peptide.chat" />
|
||||
|
||||
<!-- Channel deep links -->
|
||||
<data android:pathPrefix="/channels/" />
|
||||
<!-- Server deep links -->
|
||||
<data android:pathPrefix="/servers/" />
|
||||
<data android:pathPrefix="/server/" />
|
||||
<!-- User deep links -->
|
||||
<data android:pathPrefix="/users/" />
|
||||
<!-- Message deep links -->
|
||||
<data android:pathPrefix="/messages/" />
|
||||
<!-- Server channel message deep links -->
|
||||
<data android:pathPattern="/server/.*/channel/.*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
# Android App Links Setup Instructions
|
||||
|
||||
This document provides instructions for setting up Android App Links for the Revolt app.
|
||||
|
||||
## What are Android App Links?
|
||||
|
||||
Android App Links are HTTP URLs that bring users directly to specific content in your Android app. When a user clicks an App Link, the app opens immediately if it's installed, without showing the app chooser dialog.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. Your app must have an intent filter that handles the specific URLs
|
||||
2. Your website must have a Digital Asset Links JSON file that verifies the association with your app
|
||||
|
||||
## Steps to Complete the Setup
|
||||
|
||||
### 1. Get your app's signing certificate fingerprint
|
||||
|
||||
Run the following command to get your app's signing certificate fingerprint:
|
||||
|
||||
```bash
|
||||
keytool -list -v -keystore your_keystore.jks -alias your_alias
|
||||
```
|
||||
|
||||
Look for the SHA-256 fingerprint in the output. Remove all colons (:) from the fingerprint.
|
||||
|
||||
### 2. Update the assetlinks.json file
|
||||
|
||||
Open the `assetlinks.json` file in this directory and replace the placeholder with your actual SHA-256 fingerprint:
|
||||
|
||||
```json
|
||||
[{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "chat.revolt",
|
||||
"sha256_cert_fingerprints": [
|
||||
"YOUR_SHA256_FINGERPRINT_HERE"
|
||||
]
|
||||
}
|
||||
}]
|
||||
```
|
||||
|
||||
### 3. Host the assetlinks.json file on your server
|
||||
|
||||
Upload the `assetlinks.json` file to the following location on your server:
|
||||
|
||||
```
|
||||
https://revolt.chat/.well-known/assetlinks.json
|
||||
https://app.revolt.chat/.well-known/assetlinks.json
|
||||
```
|
||||
|
||||
Make sure the file is accessible via HTTPS and returns with Content-Type: application/json.
|
||||
|
||||
### 4. Test your App Links
|
||||
|
||||
Use the Android Debug Bridge (ADB) to test your App Links:
|
||||
|
||||
```bash
|
||||
adb shell am start -a android.intent.action.VIEW -d "https://revolt.chat/channels/CHANNEL_ID" chat.revolt
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If your App Links aren't working:
|
||||
|
||||
1. Verify that the assetlinks.json file is accessible via HTTPS
|
||||
2. Check that the SHA-256 fingerprint in the assetlinks.json file matches your app's signing certificate
|
||||
3. Make sure your app is using the correct signing certificate
|
||||
4. Use the App Links Assistant in Android Studio to verify your App Links setup
|
||||
|
||||
For more information, see the [Android App Links documentation](https://developer.android.com/training/app-links).
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
[{
|
||||
"relation": ["delegate_permission/common.handle_all_urls"],
|
||||
"target": {
|
||||
"namespace": "android_app",
|
||||
"package_name": "chat.peptide",
|
||||
"sha256_cert_fingerprints": [
|
||||
"PUT_YOUR_APP_SIGNING_CERTIFICATE_SHA256_FINGERPRINT_HERE"
|
||||
]
|
||||
}
|
||||
}]
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
package chat.revolt.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import chat.revolt.utils.DeepLinkUtils
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DeepLinkActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var kvStorage: KVStorage
|
||||
|
||||
private val viewModel by viewModels<MainActivityViewModel>()
|
||||
|
||||
companion object {
|
||||
private const val TAG = "DeepLinkActivity"
|
||||
|
||||
// Constants for deep link paths
|
||||
const val PATH_CHANNEL = "channel"
|
||||
const val PATH_SERVER = "server"
|
||||
const val PATH_USER = "user"
|
||||
const val PATH_MESSAGE = "message"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Handle the incoming intent
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
|
||||
// Handle the new intent
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent) {
|
||||
val appLinkAction = intent.action
|
||||
val appLinkData: Uri? = intent.data
|
||||
|
||||
if (Intent.ACTION_VIEW == appLinkAction && appLinkData != null) {
|
||||
Log.d(TAG, "Deep link received: $appLinkData")
|
||||
|
||||
// Check if this is a valid Revolt deep link
|
||||
if (!DeepLinkUtils.isRevoltDeepLink(appLinkData)) {
|
||||
Log.d(TAG, "Not a valid Revolt deep link: $appLinkData")
|
||||
startMainActivity()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if user is logged in and handle deep link in a coroutine
|
||||
lifecycleScope.launch {
|
||||
val token = kvStorage.get("sessionToken")
|
||||
if (token == null) {
|
||||
// User is not logged in, redirect to login screen
|
||||
Log.d(TAG, "User not logged in, redirecting to login screen")
|
||||
startMainActivity("choose-platform")
|
||||
return@launch
|
||||
}
|
||||
|
||||
// First, check for server/channel/message format
|
||||
val serverChannelMessage = DeepLinkUtils.extractServerChannelMessage(appLinkData)
|
||||
if (serverChannelMessage != null) {
|
||||
Log.d(TAG, "Detected server/channel/message format: ${serverChannelMessage.serverId}/${serverChannelMessage.channelId}/${serverChannelMessage.messageId}")
|
||||
|
||||
if (serverChannelMessage.messageId != null) {
|
||||
// Navigate to the specific message in the channel
|
||||
navigateToMessage(serverChannelMessage.messageId, serverChannelMessage.channelId)
|
||||
} else {
|
||||
// Navigate to the channel in the server
|
||||
navigateToChannel(serverChannelMessage.channelId)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
// Extract IDs from the URI using our utility class for other formats
|
||||
val channelId = DeepLinkUtils.extractChannelId(appLinkData)
|
||||
val serverId = DeepLinkUtils.extractServerId(appLinkData)
|
||||
val userId = DeepLinkUtils.extractUserId(appLinkData)
|
||||
val messageId = DeepLinkUtils.extractMessageId(appLinkData)
|
||||
|
||||
when {
|
||||
channelId != null -> {
|
||||
Log.d(TAG, "Navigating to channel: $channelId")
|
||||
navigateToChannel(channelId)
|
||||
}
|
||||
serverId != null -> {
|
||||
Log.d(TAG, "Navigating to server: $serverId")
|
||||
navigateToServer(serverId)
|
||||
}
|
||||
userId != null -> {
|
||||
Log.d(TAG, "Navigating to user: $userId")
|
||||
navigateToUser(userId)
|
||||
}
|
||||
messageId != null -> {
|
||||
Log.d(TAG, "Navigating to message: $messageId")
|
||||
val channelParam = appLinkData.getQueryParameter("channel")
|
||||
navigateToMessage(messageId, channelParam)
|
||||
}
|
||||
else -> {
|
||||
// Default case, just open the main activity
|
||||
Log.d(TAG, "No specific path, opening main activity")
|
||||
startMainActivity()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Not a deep link, just open the main activity
|
||||
Log.d(TAG, "Not a deep link, opening main activity")
|
||||
startMainActivity()
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToChannel(channelId: String) {
|
||||
Log.d(TAG, "Navigating to channel: $channelId")
|
||||
|
||||
// Use our utility class to create the intent
|
||||
val intent = DeepLinkUtils.createChannelIntent(this, channelId)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun navigateToServer(serverId: String) {
|
||||
Log.d(TAG, "Navigating to server: $serverId")
|
||||
|
||||
// Use our utility class to create the intent
|
||||
val intent = DeepLinkUtils.createServerIntent(this, serverId)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun navigateToUser(userId: String) {
|
||||
Log.d(TAG, "Navigating to user: $userId")
|
||||
|
||||
// Use our utility class to create the intent
|
||||
val intent = DeepLinkUtils.createUserIntent(this, userId)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun navigateToMessage(messageId: String, channelId: String?) {
|
||||
Log.d(TAG, "Navigating to message: $messageId in channel: $channelId")
|
||||
|
||||
// Use our utility class to create the intent
|
||||
val intent = DeepLinkUtils.createMessageIntent(this, messageId, channelId)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun startMainActivity(destination: String? = null) {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
if (destination != null) {
|
||||
intent.putExtra("next_destination", destination)
|
||||
}
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package chat.revolt.activities
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.os.Bundle
|
||||
|
|
@ -127,6 +128,7 @@ import chat.revolt.screens.settings.channel.ChannelSettingsHome
|
|||
import chat.revolt.screens.settings.channel.ChannelSettingsOverview
|
||||
import chat.revolt.screens.settings.channel.ChannelSettingsPermissions
|
||||
import chat.revolt.ui.theme.RevoltTheme
|
||||
import chat.revolt.utils.DeepLinkHandler
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -383,6 +385,9 @@ class MainActivity : AppCompatActivity() {
|
|||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
RevoltAPI.hydrateFromPersistentCache()
|
||||
|
||||
// Handle deep link data if available
|
||||
handleDeepLinkData(intent)
|
||||
|
||||
setContent {
|
||||
val windowSizeClass = calculateWindowSizeClass(this)
|
||||
|
|
@ -419,6 +424,70 @@ class MainActivity : AppCompatActivity() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
super.onNewIntent(intent)
|
||||
handleDeepLinkData(intent)
|
||||
}
|
||||
|
||||
private fun handleDeepLinkData(intent: Intent) {
|
||||
// Check for deep link data from DeepLinkActivity
|
||||
val channelId = intent.getStringExtra(DeepLinkActivity.PATH_CHANNEL)
|
||||
val serverId = intent.getStringExtra(DeepLinkActivity.PATH_SERVER)
|
||||
val userId = intent.getStringExtra(DeepLinkActivity.PATH_USER)
|
||||
val messageId = intent.getStringExtra(DeepLinkActivity.PATH_MESSAGE)
|
||||
val nextDestination = intent.getStringExtra("next_destination")
|
||||
|
||||
if (nextDestination != null) {
|
||||
Log.d(TAG, "Setting next destination from deep link: $nextDestination")
|
||||
viewModel.updateNextDestination(nextDestination)
|
||||
return
|
||||
}
|
||||
|
||||
// Store deep link data in a static companion object for ChatRouterScreen to access
|
||||
DeepLinkHandler.channelId = channelId
|
||||
DeepLinkHandler.serverId = serverId
|
||||
DeepLinkHandler.userId = userId
|
||||
DeepLinkHandler.messageId = messageId
|
||||
DeepLinkHandler.hasDeepLink = channelId != null || serverId != null || userId != null || messageId != null
|
||||
|
||||
// Wait for the app to be ready before navigating to the deep link destination
|
||||
viewModel.viewModelScope.launch {
|
||||
viewModel.isReady.collect { isReady ->
|
||||
if (isReady) {
|
||||
when {
|
||||
channelId != null -> {
|
||||
Log.d(TAG, "Navigating to channel from deep link: $channelId")
|
||||
// Check if Polar mode is enabled
|
||||
if (Experiments.usePolar.isEnabled) {
|
||||
viewModel.updateNextDestination("main/conversation/$channelId")
|
||||
} else {
|
||||
// For non-Polar mode, we'll use the DeepLinkHandler to communicate with ChatRouterScreen
|
||||
viewModel.updateNextDestination("chat")
|
||||
}
|
||||
}
|
||||
serverId != null -> {
|
||||
Log.d(TAG, "Navigating to server from deep link: $serverId")
|
||||
// Navigate to chat screen, the ChatRouterScreen will handle the server navigation
|
||||
viewModel.updateNextDestination("chat")
|
||||
}
|
||||
userId != null -> {
|
||||
Log.d(TAG, "Navigating to user from deep link: $userId")
|
||||
// Navigate to chat screen, the ChatRouterScreen will handle the user navigation
|
||||
viewModel.updateNextDestination("chat")
|
||||
}
|
||||
messageId != null -> {
|
||||
Log.d(TAG, "Navigating to message from deep link: $messageId")
|
||||
// Navigate to chat screen, the ChatRouterScreen will handle the message navigation
|
||||
viewModel.updateNextDestination("chat")
|
||||
}
|
||||
}
|
||||
// Only collect once
|
||||
return@collect
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProvideKeyboardShortcuts(
|
||||
data: MutableList<KeyboardShortcutGroup>?,
|
||||
menu: Menu?,
|
||||
|
|
@ -444,6 +513,7 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MainActivity"
|
||||
init {
|
||||
NativeLibraries.init()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ import chat.revolt.composables.chat.DisconnectedNotice
|
|||
import chat.revolt.composables.screens.chat.drawer.ChannelSideDrawer
|
||||
import chat.revolt.dialogs.NotificationRationaleDialog
|
||||
import chat.revolt.internals.Changelogs
|
||||
import chat.revolt.utils.DeepLinkHandler
|
||||
import chat.revolt.persistence.KVStorage
|
||||
import chat.revolt.screens.chat.dialogs.safety.ReportMessageDialog
|
||||
import chat.revolt.screens.chat.dialogs.safety.ReportServerDialog
|
||||
|
|
@ -112,12 +113,12 @@ sealed class ChatRouterDestination {
|
|||
data class NoCurrentChannel(val serverId: String?) : ChatRouterDestination()
|
||||
|
||||
companion object {
|
||||
val default = Settings
|
||||
val default = Home // Changed default from Settings to Home
|
||||
|
||||
fun fromString(destination: String): ChatRouterDestination {
|
||||
return when {
|
||||
destination == "home" -> Settings // previous name for overview
|
||||
destination == "overview" -> Settings
|
||||
destination == "home" -> Home // Changed from Settings to Home
|
||||
destination == "overview" -> Home // Changed from Settings to Home
|
||||
destination == "friends" -> Friends
|
||||
destination.startsWith("no_current_channel/") -> NoCurrentChannel(
|
||||
destination.removePrefix(
|
||||
|
|
@ -150,8 +151,15 @@ class ChatRouterViewModel @Inject constructor(
|
|||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val current = kvStorage.get("currentDestination")
|
||||
setSaveDestination(ChatRouterDestination.fromString(current ?: ""))
|
||||
// Check if there's a deep link to handle first
|
||||
if (DeepLinkHandler.hasDeepLink) {
|
||||
Log.d("ChatRouterViewModel", "Deep link detected in init, skipping saved destination")
|
||||
// Don't set any destination here, let the LaunchedEffect in ChatRouterScreen handle it
|
||||
} else {
|
||||
// No deep link, load the saved destination
|
||||
val current = kvStorage.get("currentDestination")
|
||||
setSaveDestination(ChatRouterDestination.fromString(current ?: ""))
|
||||
}
|
||||
|
||||
latestChangelogRead = changelogs.hasSeenCurrent()
|
||||
latestChangelog = changelogs.getLatestChangelogCode()
|
||||
|
|
@ -361,6 +369,63 @@ fun ChatRouterScreen(
|
|||
}
|
||||
}
|
||||
|
||||
// Handle deep links when the ChatRouterScreen is first composed
|
||||
LaunchedEffect(Unit) {
|
||||
// Check if there's a deep link to handle
|
||||
if (DeepLinkHandler.hasDeepLink) {
|
||||
Log.d("ChatRouterScreen", "Processing deep link data")
|
||||
|
||||
// Process the deep link based on priority
|
||||
when {
|
||||
// Priority 1: Handle server/channel/message format (most specific)
|
||||
DeepLinkHandler.messageId != null && DeepLinkHandler.channelId != null -> {
|
||||
val messageId = DeepLinkHandler.messageId!!
|
||||
val channelId = DeepLinkHandler.channelId!!
|
||||
Log.d("ChatRouterScreen", "Setting destination to message: $messageId in channel: $channelId")
|
||||
|
||||
val resolvedChannel = RevoltAPI.channelCache[channelId]
|
||||
if (resolvedChannel == null) {
|
||||
showChannelUnavailableAlert = true
|
||||
} else {
|
||||
// Navigate to the channel first, then the message will be handled by the channel screen
|
||||
viewModel.setSaveDestination(ChatRouterDestination.Channel(channelId))
|
||||
// TODO: Add logic to scroll to the specific message in the channel
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 2: Handle channel deep link
|
||||
DeepLinkHandler.channelId != null -> {
|
||||
val channelId = DeepLinkHandler.channelId!!
|
||||
Log.d("ChatRouterScreen", "Setting destination to channel: $channelId")
|
||||
val resolvedChannel = RevoltAPI.channelCache[channelId]
|
||||
|
||||
if (resolvedChannel == null) {
|
||||
showChannelUnavailableAlert = true
|
||||
} else {
|
||||
viewModel.setSaveDestination(ChatRouterDestination.Channel(channelId))
|
||||
}
|
||||
}
|
||||
|
||||
// Priority 3: Handle server deep link
|
||||
DeepLinkHandler.serverId != null -> {
|
||||
val serverId = DeepLinkHandler.serverId!!
|
||||
Log.d("ChatRouterScreen", "Setting destination to server: $serverId")
|
||||
viewModel.navigateToServer(serverId)
|
||||
}
|
||||
|
||||
// Priority 4: Handle user deep link
|
||||
DeepLinkHandler.userId != null -> {
|
||||
val userId = DeepLinkHandler.userId!!
|
||||
Log.d("ChatRouterScreen", "Setting destination to user: $userId")
|
||||
// TODO: Implement user navigation if needed
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the deep link data after processing
|
||||
DeepLinkHandler.clear()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
while (true) {
|
||||
ActionChannel.receive().let { action ->
|
||||
|
|
|
|||
|
|
@ -0,0 +1,218 @@
|
|||
package chat.revolt.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import chat.revolt.activities.DeepLinkActivity
|
||||
import chat.revolt.activities.MainActivity
|
||||
|
||||
/**
|
||||
* Utility class for handling deep links in the Revolt app.
|
||||
*/
|
||||
object DeepLinkUtils {
|
||||
private const val TAG = "DeepLinkUtils"
|
||||
|
||||
/**
|
||||
* Checks if the given URI is a valid Revolt deep link.
|
||||
*
|
||||
* @param uri The URI to check
|
||||
* @return true if the URI is a valid Revolt deep link, false otherwise
|
||||
*/
|
||||
fun isRevoltDeepLink(uri: Uri?): Boolean {
|
||||
if (uri == null) return false
|
||||
|
||||
val host = uri.host ?: return false
|
||||
val scheme = uri.scheme ?: return false
|
||||
|
||||
// Check if the scheme is http or https
|
||||
if (scheme != "http" && scheme != "https") return false
|
||||
|
||||
// Check if the host is a valid Revolt host
|
||||
return host == "peptide.chat" || host == "app.peptide.chat"
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the channel ID from a channel deep link URI.
|
||||
*
|
||||
* @param uri The URI to extract the channel ID from
|
||||
* @return The channel ID, or null if the URI is not a valid channel deep link
|
||||
*/
|
||||
fun extractChannelId(uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
|
||||
val pathSegments = uri.pathSegments
|
||||
|
||||
// Handle /channels/CHANNEL_ID format
|
||||
if (pathSegments.size >= 2 && pathSegments[0] == "channels") {
|
||||
return pathSegments[1]
|
||||
}
|
||||
|
||||
// Handle /server/SERVER_ID/channel/CHANNEL_ID format
|
||||
if (pathSegments.size >= 4 &&
|
||||
pathSegments[0] == "server" &&
|
||||
pathSegments[2] == "channel") {
|
||||
return pathSegments[3]
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the server ID from a server deep link URI.
|
||||
*
|
||||
* @param uri The URI to extract the server ID from
|
||||
* @return The server ID, or null if the URI is not a valid server deep link
|
||||
*/
|
||||
fun extractServerId(uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
|
||||
val pathSegments = uri.pathSegments
|
||||
|
||||
// Handle /servers/SERVER_ID format
|
||||
if (pathSegments.size >= 2 && pathSegments[0] == "servers") {
|
||||
return pathSegments[1]
|
||||
}
|
||||
|
||||
// Handle /server/SERVER_ID format
|
||||
if (pathSegments.size >= 2 && pathSegments[0] == "server") {
|
||||
return pathSegments[1]
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the user ID from a user deep link URI.
|
||||
*
|
||||
* @param uri The URI to extract the user ID from
|
||||
* @return The user ID, or null if the URI is not a valid user deep link
|
||||
*/
|
||||
fun extractUserId(uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
|
||||
val pathSegments = uri.pathSegments
|
||||
if (pathSegments.size < 2 || pathSegments[0] != "users") return null
|
||||
|
||||
return pathSegments[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the message ID from a message deep link URI.
|
||||
*
|
||||
* @param uri The URI to extract the message ID from
|
||||
* @return The message ID, or null if the URI is not a valid message deep link
|
||||
*/
|
||||
fun extractMessageId(uri: Uri?): String? {
|
||||
if (uri == null) return null
|
||||
|
||||
val pathSegments = uri.pathSegments
|
||||
|
||||
// Handle /messages/MESSAGE_ID format
|
||||
if (pathSegments.size >= 2 && pathSegments[0] == "messages") {
|
||||
return pathSegments[1]
|
||||
}
|
||||
|
||||
// Handle /server/SERVER_ID/channel/CHANNEL_ID/MESSAGE_ID format
|
||||
if (pathSegments.size >= 5 &&
|
||||
pathSegments[0] == "server" &&
|
||||
pathSegments[2] == "channel") {
|
||||
return pathSegments[4]
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Data class to hold server, channel, and message IDs extracted from a URI.
|
||||
*/
|
||||
data class ServerChannelMessage(
|
||||
val serverId: String,
|
||||
val channelId: String,
|
||||
val messageId: String? = null
|
||||
)
|
||||
|
||||
/**
|
||||
* Extracts server, channel, and message IDs from a server channel message deep link URI.
|
||||
*
|
||||
* @param uri The URI to extract the IDs from
|
||||
* @return A ServerChannelMessage object containing the extracted IDs, or null if the URI is not a valid server channel message deep link
|
||||
*/
|
||||
fun extractServerChannelMessage(uri: Uri?): ServerChannelMessage? {
|
||||
if (uri == null) return null
|
||||
|
||||
val pathSegments = uri.pathSegments
|
||||
|
||||
// Handle /server/SERVER_ID/channel/CHANNEL_ID format
|
||||
if (pathSegments.size >= 4 &&
|
||||
pathSegments[0] == "server" &&
|
||||
pathSegments[2] == "channel") {
|
||||
|
||||
val serverId = pathSegments[1]
|
||||
val channelId = pathSegments[3]
|
||||
|
||||
// Check if there's a message ID (5th segment)
|
||||
val messageId = if (pathSegments.size >= 5) pathSegments[4] else null
|
||||
|
||||
return ServerChannelMessage(serverId, channelId, messageId)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent to navigate to a specific channel.
|
||||
*
|
||||
* @param context The context to use for creating the intent
|
||||
* @param channelId The ID of the channel to navigate to
|
||||
* @return An intent that will navigate to the specified channel
|
||||
*/
|
||||
fun createChannelIntent(context: Context, channelId: String): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
putExtra(DeepLinkActivity.PATH_CHANNEL, channelId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent to navigate to a specific server.
|
||||
*
|
||||
* @param context The context to use for creating the intent
|
||||
* @param serverId The ID of the server to navigate to
|
||||
* @return An intent that will navigate to the specified server
|
||||
*/
|
||||
fun createServerIntent(context: Context, serverId: String): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
putExtra(DeepLinkActivity.PATH_SERVER, serverId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent to navigate to a specific user.
|
||||
*
|
||||
* @param context The context to use for creating the intent
|
||||
* @param userId The ID of the user to navigate to
|
||||
* @return An intent that will navigate to the specified user
|
||||
*/
|
||||
fun createUserIntent(context: Context, userId: String): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
putExtra(DeepLinkActivity.PATH_USER, userId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an intent to navigate to a specific message.
|
||||
*
|
||||
* @param context The context to use for creating the intent
|
||||
* @param messageId The ID of the message to navigate to
|
||||
* @param channelId The ID of the channel containing the message, if known
|
||||
* @return An intent that will navigate to the specified message
|
||||
*/
|
||||
fun createMessageIntent(context: Context, messageId: String, channelId: String? = null): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
putExtra(DeepLinkActivity.PATH_MESSAGE, messageId)
|
||||
if (channelId != null) {
|
||||
putExtra(DeepLinkActivity.PATH_CHANNEL, channelId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue