Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
67d8ab2
refactor: Simplify manifest pathPattern for deeplink
benjaminVadon Jan 28, 2026
aeba162
feat: Parse deeplink path to different available type of file
benjaminVadon Jan 29, 2026
0fbc328
feat: Change way to handle deeplink with DeeplinkHandler
benjaminVadon Jan 29, 2026
a31b1dc
feat: Pass the new deeplinkType to MainActivity args
benjaminVadon Jan 29, 2026
80f2b05
fix: Convert action ids to int too
benjaminVadon Jan 29, 2026
ef53467
fix: Fix onlyOffice type to fileId
benjaminVadon Jan 29, 2026
ced87ad
feat: Base handle of deeplink in MainActivity
benjaminVadon Jan 29, 2026
a5aab32
feat: Handle only office edition deeplink
benjaminVadon Jan 29, 2026
3a70d99
feat: Support for not handled deeplink, and make Collaborate not handled
benjaminVadon Jan 29, 2026
3d3f1e4
fix: Allow empty folder properties
benjaminVadon Jan 30, 2026
e0728c6
fix: Change folder type to reflect the other source of truth
benjaminVadon Jan 30, 2026
f80f31f
feat: Navigate to trash
benjaminVadon Jan 30, 2026
db623c5
refactor: Migrate a lot of const for special directories to one seale…
benjaminVadon Jan 30, 2026
9743204
refactor: Migrate SpecialFolder to parcelable for use in RoleFolder
benjaminVadon Jan 30, 2026
ce9de0d
fix: Fix index for role folders which can have preview
benjaminVadon Jan 30, 2026
bf9264f
fix: Change Special RoleFolder to be a sealed class
benjaminVadon Jan 30, 2026
be9ea74
fix: Better handle deeplink to clear intent stack
benjaminVadon Feb 3, 2026
3516537
fix: Fix FileType and External to not include breakline
benjaminVadon Feb 3, 2026
d2a9ce3
fix: Fix FolderTypes patterns and types
benjaminVadon Feb 3, 2026
7ac37d1
feat: Allow files fragments to show preview
benjaminVadon Feb 3, 2026
ddc05db
feat: Transmit deeplink action to MainViewModel and retrieve it in Ro…
benjaminVadon Feb 3, 2026
d6bfcb1
fix: Add to nav graph files fragments new argument
benjaminVadon Feb 3, 2026
f901c4f
feat: Support deeplink in SharedWithMe screen
benjaminVadon Feb 3, 2026
795fbf4
chore: Remove useless SpecialFolder from RoleFolder
benjaminVadon Feb 3, 2026
1f8becc
chore: Rename forceFail to forceInvalid
benjaminVadon Feb 3, 2026
7a07bd0
chore: Remove old code for deeplink support
benjaminVadon Feb 3, 2026
0a4d164
fix: Shared with me deeplink, preventing mis unparcel of fileType by …
benjaminVadon Feb 3, 2026
16dbb40
fix: Shared with me deeplink by don't mis interpret longer deeplink p…
benjaminVadon Feb 3, 2026
01bb754
fix: Shared with me deeplink by don't mis interpret longer deeplink p…
benjaminVadon Feb 3, 2026
73e65c0
fix: Mark as not handled for Collaboratives and SharedLinks
benjaminVadon Feb 3, 2026
b9638d7
refactor: Refactor DeeplinkHandler to DeeplinkParser object to only p…
benjaminVadon Feb 3, 2026
9349ab0
feat: Add test to deeplink parsing
benjaminVadon Feb 3, 2026
34f9165
chore: Don't retrieve the useless filetype and migrate all to data cl…
benjaminVadon Feb 3, 2026
1939261
chore: Sonar feedback
benjaminVadon Feb 4, 2026
33382b8
fix: Correctly navigate for drive files
benjaminVadon Feb 4, 2026
5073461
chore: Simplify handle arguments of launch activity to clearly mutual…
benjaminVadon Feb 4, 2026
fca8d13
chore: Clean parsing documentation
benjaminVadon Feb 4, 2026
08f05ca
fix: Fix nav graph for shared fragments with other nav graph
benjaminVadon Feb 4, 2026
9566bbe
fix: Throw NotImplemented only if in Debug build, for developer purpo…
benjaminVadon Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 10 additions & 21 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -178,30 +178,19 @@

<data android:scheme="https" />
<data android:host="kdrive.infomaniak.com" />
<data android:host="ksuite.infomaniak.com" />

<data android:pathPattern="/app/collaborate/.*" />
<data android:pathPattern="/app/drive" />
<data android:pathPattern="/app/drive/" />
<data android:pathPattern="/app/drive/.*/files" />
<data android:pathPattern="/app/drive/.*/files/.*" />
<data android:pathPattern="/app/drive/.*/files/.*/preview/./..*" />
<data android:pathPattern="/app/drive/.*/redirect/..*" />
<data android:pathPattern="/app/drive/.*/trash" />
<data android:pathPattern="/app/drive/.*/trash/..*" />
<data android:pathPattern="/app/office/.*/..*" />
<data android:pathPattern="/app/share/.*/..*" />

<data android:host="ksuite.infomaniak.com" />
<data android:pathPattern="/app/drive/.*" />
<data android:pathPattern="/app/office/.*" />
<data android:pathPattern="/app/share/.*" />

<data android:pathPattern="/.*/kdrive/app/collaborate/.*" />
<data android:pathPattern="/.*/kdrive/app/drive" />
<data android:pathPattern="/.*/kdrive/app/drive/" />
<data android:pathPattern="/.*/kdrive/app/drive/.*/files" />
<data android:pathPattern="/.*/kdrive/app/drive/.*/files/.*" />
<data android:pathPattern="/.*/kdrive/app/drive/.*/files/.*/preview/./..*" />
<data android:pathPattern="/.*/kdrive/app/drive/.*/redirect/..*" />
<data android:pathPattern="/.*/kdrive/app/drive/.*/trash" />
<data android:pathPattern="/.*/kdrive/app/drive/.*/trash/..*" />
<data android:pathPattern="/.*/kdrive/app/office/.*/..*" />
<data android:pathPattern="/.*/kdrive/app/share/.*/..*" />
<data android:pathPattern="/.*/kdrive/app/drive/.*" />
<data android:pathPattern="/.*/kdrive/app/office/.*" />
<data android:pathPattern="/.*/kdrive/app/share/.*" />
</intent-filter>

<meta-data
Expand Down Expand Up @@ -235,7 +224,7 @@
<activity
android:name="com.infomaniak.core.inappupdate.ui.UpdateRequiredActivity"
android:exported="false"
android:theme="@style/AppTheme.RequiredUpdate"/>
android:theme="@style/AppTheme.RequiredUpdate" />

<activity
android:name="com.infomaniak.core.legacy.bugtracker.BugTrackerActivity"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.deeplink

internal enum class ActionType(val type: String, val actionPattern: String) {
Collaborate(type = "collaborate", actionPattern = "$DRIVE_ID/$UUID"),
Drive(type = "drive", actionPattern = "$DRIVE_ID/$ROLE_FOLDER(?:/$FOLDER_ALL_PROPERTIES)?"),
Office(type = "office", actionPattern = "$DRIVE_ID/$FILE_ID");

fun build(action: String): DeeplinkAction = action.find(actionPattern).run {
when (this@ActionType) {
Collaborate -> DeeplinkAction.Collaborate(
driveId = parseId(1),
uuid = groupValues[2],
)
Drive -> DeeplinkAction.Drive(
driveId = parseId(1),
roleFolder = RoleFolder.from(folderType = groupValues[2], folderProperties = groupValues[3])
)
Office -> DeeplinkAction.Office(
driveId = parseId(1),
fileId = parseId(2),
)
}
}

companion object {
fun from(value: String): ActionType = entries.find { it.type == value } ?: throw InvalidValue()
fun String.find(actionPattern: String): MatchResult =
Regex(actionPattern).find(this) ?: throw InvalidValue()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.deeplink

import kotlinx.parcelize.Parcelize


@Parcelize
sealed interface DeeplinkAction : DeeplinkType {
data class Collaborate(val driveId: Int, val uuid: String) : DeeplinkAction {
override val isHandled: Boolean
get() = false
}

data class Drive(val driveId: Int, val roleFolder: RoleFolder) : DeeplinkAction {
override val isHandled: Boolean
get() = roleFolder.isHandled
}

data class Office(val driveId: Int, val fileId: Int) : DeeplinkAction

companion object {
@Throws(InvalidValue::class)
fun from(actionType: String, action: String): DeeplinkAction =
ActionType.from(actionType).build(action)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.deeplink


private const val SELECT_DIGITS = "(\\d+)"
const val ACTION_TYPE = "([a-z]+)"
const val ACTION = "(.*)"
const val DRIVE_ID = SELECT_DIGITS
const val ROLE_FOLDER = "([a-z-]+)"
const val UUID = "([a-z0-9-]+)"
const val FOLDER_ALL_PROPERTIES = "(.*)"
const val FILE_ID = SELECT_DIGITS
const val FOLDER_ID = SELECT_DIGITS
const val FILE_TYPE = "[a-z]+"
const val END_OF_REGEX = "$"
const val KEY_PREVIEW = "preview"
const val PREVIEW = "$KEY_PREVIEW/$FILE_TYPE/$FILE_ID"
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.deeplink

import android.content.Intent
import android.os.Parcelable
import com.infomaniak.core.legacy.utils.clearStack
import com.infomaniak.drive.ui.MainActivityArgs
import kotlinx.parcelize.Parcelize

@Parcelize
sealed interface DeeplinkType : Parcelable {
val isHandled: Boolean
get() = true

data object Invalid : DeeplinkType
companion object {
fun DeeplinkType.addTo(intent: Intent) =
intent
.putExtras(MainActivityArgs(deeplinkType = this).toBundle())
.clearStack()

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.deeplink

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
sealed interface ExternalFileType : Parcelable {
val sourceDriveId: Int

data class FilePreview(override val sourceDriveId: Int, val fileId: Int) : ExternalFileType {
constructor(match: MatchResult) : this(
sourceDriveId = match.parseId(2),
fileId = match.parseId(3),
)

companion object {
const val PATTERN = "$DRIVE_ID/$KEY_PREVIEW/$FILE_TYPE/$FILE_ID"
}
}

data class Folder(override val sourceDriveId: Int, val folderId: Int) : ExternalFileType {
constructor(match: MatchResult) : this(
sourceDriveId = match.parseId(5),
folderId = match.parseId(6),
)

companion object {
const val PATTERN = "$DRIVE_ID/$FOLDER_ID$END_OF_REGEX"
}
}

data class FilePreviewInFolder(override val sourceDriveId: Int, val folderId: Int, val fileId: Int) : ExternalFileType {
constructor(match: MatchResult) : this(
sourceDriveId = match.parseId(8),
folderId = match.parseId(9),
fileId = match.parseId(10),
)

companion object {
const val PATTERN = "$DRIVE_ID/$FOLDER_ID/$KEY_PREVIEW/$FILE_TYPE/$FILE_ID"
}
}

companion object {

private const val GROUP_PREVIEW_FILE = "file"
private const val GROUP_FOLDER = "folder"
private const val GROUP_PREVIEW_FILE_IN_FOLDER = "fileInFolder"
/**
* SHARED_WITH_ME_FOLDER_PROPERTIES filters this kind of paths :
* <sourceDriveId>/preview/<type:string>/<fileId>
* <sourceDriveId>/<folderId>
* <sourceDriveId>/<folderId>/preview/<type:string>/<fileId>
* It find one of them and assign it to a named group
*
* Order in this Regex implies index for each parsing in ExternalFileType constructors
*/
val SHARED_WITH_ME_FOLDER_PROPERTIES = listOf(
"(?<$GROUP_PREVIEW_FILE>${FilePreview.PATTERN})",
"(?<$GROUP_FOLDER>${Folder.PATTERN})",
"(?<$GROUP_PREVIEW_FILE_IN_FOLDER>${FilePreviewInFolder.PATTERN})"
).joinToString(separator = "|")

fun MatchResult?.extractExternalFileType(): ExternalFileType? = this?.run {
groups[GROUP_PREVIEW_FILE]?.let { FilePreview(this) }
?: groups[GROUP_FOLDER]?.let { Folder(this) }
?: groups[GROUP_PREVIEW_FILE_IN_FOLDER]?.let { FilePreviewInFolder(this) }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.deeplink

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
sealed class FileType(open val fileId: Int) : Parcelable {
data class File(override val fileId: Int) : FileType(fileId = fileId) {
constructor(match: MatchResult) : this(
fileId = match.parseId(2),
)

companion object {
const val PATTERN = "$FILE_ID$END_OF_REGEX"
}
}

data class FilePreviewInFolder(val folderId: Int, override val fileId: Int) :
FileType(fileId = fileId) {
constructor(match: MatchResult) : this(
folderId = match.parseId(4),
fileId = match.parseId(5),
)

companion object {
const val PATTERN = "$FOLDER_ID/$KEY_PREVIEW/$FILE_TYPE/$FILE_ID"
}
}

companion object {
const val GROUP_FILE = "file"
const val GROUP_PREVIEW_IN_FOLDER = "previewFolder"

/**
* FOLDER_PROPERTIES filters this kind of paths :
* <folderId>/<fileId>
* <folderId>/preview/<type:string>/<fileId>
* It find one of them and assign it to a named group
*
* Order in this Regex implies index for each parsing in ExternalFileType constructors
*/
val FOLDER_PROPERTIES = listOf(
"(?<$GROUP_FILE>${File.PATTERN})",
"(?<$GROUP_PREVIEW_IN_FOLDER>${FilePreviewInFolder.PATTERN})"
).joinToString(separator = "|")

fun MatchResult?.extractFileType(): FileType =
this?.let {
groups[GROUP_FILE]?.let { File(this) }
?: groups[GROUP_PREVIEW_IN_FOLDER]?.let { FilePreviewInFolder(this) }
} ?: throw InvalidValue()
}
}
Loading