diff --git a/.idea/navEditor.xml b/.idea/navEditor.xml
index 11226bbe99..a4094ca280 100644
--- a/.idea/navEditor.xml
+++ b/.idea/navEditor.xml
@@ -303,18 +303,6 @@
-
-
-
-
-
-
-
@@ -443,18 +431,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -875,6 +851,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1143,18 +1131,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/java/com/infomaniak/drive/MainApplication.kt b/app/src/main/java/com/infomaniak/drive/MainApplication.kt
index 36c7327cbc..a22ec0338d 100644
--- a/app/src/main/java/com/infomaniak/drive/MainApplication.kt
+++ b/app/src/main/java/com/infomaniak/drive/MainApplication.kt
@@ -44,6 +44,7 @@ import com.infomaniak.drive.data.models.UiSettings
import com.infomaniak.drive.data.services.MqttClientWrapper
import com.infomaniak.drive.ui.LaunchActivity
import com.infomaniak.drive.utils.AccountUtils
+import com.infomaniak.drive.utils.MyKSuiteDataUtils
import com.infomaniak.drive.utils.NotificationUtils.buildGeneralNotification
import com.infomaniak.drive.utils.NotificationUtils.initNotificationChannel
import com.infomaniak.drive.utils.NotificationUtils.notifyCompat
@@ -146,6 +147,8 @@ class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycleObser
)
HttpClient.init(tokenInterceptorListener)
MqttClientWrapper.init(applicationContext)
+
+ MyKSuiteDataUtils.initDatabase(this)
}
override fun onStart(owner: LifecycleOwner) {
diff --git a/app/src/main/java/com/infomaniak/drive/data/api/ApiRepository.kt b/app/src/main/java/com/infomaniak/drive/data/api/ApiRepository.kt
index 2a49a54f87..d44ffe7393 100644
--- a/app/src/main/java/com/infomaniak/drive/data/api/ApiRepository.kt
+++ b/app/src/main/java/com/infomaniak/drive/data/api/ApiRepository.kt
@@ -19,6 +19,7 @@ package com.infomaniak.drive.data.api
import androidx.collection.arrayMapOf
import com.google.gson.JsonElement
+import com.infomaniak.core.myksuite.ui.data.MyKSuiteData
import com.infomaniak.drive.data.api.UploadTask.Companion.ConflictOption
import com.infomaniak.drive.data.models.*
import com.infomaniak.drive.data.models.ArchiveUUID.ArchiveBody
@@ -44,6 +45,7 @@ import com.infomaniak.lib.core.models.ApiResponse
import com.infomaniak.lib.core.models.ApiResponseStatus
import com.infomaniak.lib.core.networking.HttpClient
import okhttp3.OkHttpClient
+import com.infomaniak.core.myksuite.ui.network.ApiRoutes as MyKSuiteApiRoutes
object ApiRepository : ApiRepositoryCore() {
@@ -509,6 +511,15 @@ object ApiRepository : ApiRepositoryCore() {
return callApi(ApiRoutes.cancelExternalImport(driveId, importId), PUT)
}
+ fun getMyKSuiteData(okHttpClient: OkHttpClient): ApiResponse {
+ return callApi(
+ url = MyKSuiteApiRoutes.myKSuiteData(),
+ method = ApiController.ApiMethod.GET,
+ okHttpClient = okHttpClient,
+ useKotlinxSerialization = true,
+ )
+ }
+
private fun pagination(page: Int, perPage: Int = PER_PAGE) = "page=$page&per_page=$perPage"
private fun loadCursor(cursor: String?, perPage: Int = PER_PAGE): String {
diff --git a/app/src/main/java/com/infomaniak/drive/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/drive/ui/MainActivity.kt
index 3a72897a45..90ac206263 100644
--- a/app/src/main/java/com/infomaniak/drive/ui/MainActivity.kt
+++ b/app/src/main/java/com/infomaniak/drive/ui/MainActivity.kt
@@ -102,12 +102,14 @@ import com.infomaniak.lib.stores.updatemanagers.InAppUpdateManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
+import com.infomaniak.core.myksuite.R as RMyKSuite
class MainActivity : BaseActivity() {
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
private val mainViewModel: MainViewModel by viewModels()
+ private val myKSuiteViewModel: MyKSuiteViewModel by viewModels()
private val navigationArgs: MainActivityArgs? by lazy { intent?.extras?.let { MainActivityArgs.fromBundle(it) } }
private val uiSettings by lazy { UiSettings(this) }
private val navController by lazy { setupNavController() }
@@ -204,6 +206,7 @@ class MainActivity : BaseActivity() {
override fun onStart() {
super.onStart()
mainViewModel.loadRootFiles()
+ myKSuiteViewModel.refreshMyKSuite()
handleDeletionOfUploadedPhotos()
}
@@ -439,7 +442,7 @@ class MainActivity : BaseActivity() {
}
when (destination.id) {
- R.id.fileDetailsFragment -> {
+ R.id.fileDetailsFragment, RMyKSuite.id.myKSuiteDashboardFragment -> {
setColorNavigationBar(true)
}
R.id.fileShareLinkSettingsFragment -> {
diff --git a/app/src/main/java/com/infomaniak/drive/ui/MyKSuiteViewModel.kt b/app/src/main/java/com/infomaniak/drive/ui/MyKSuiteViewModel.kt
new file mode 100644
index 0000000000..1470e65d2f
--- /dev/null
+++ b/app/src/main/java/com/infomaniak/drive/ui/MyKSuiteViewModel.kt
@@ -0,0 +1,35 @@
+/*
+ * Infomaniak kDrive - Android
+ * Copyright (C) 2025 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 .
+ */
+package com.infomaniak.drive.ui
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.infomaniak.core.myksuite.ui.data.MyKSuiteData
+import com.infomaniak.drive.utils.MyKSuiteDataUtils
+import com.infomaniak.lib.core.utils.SingleLiveEvent
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class MyKSuiteViewModel : ViewModel() {
+
+ val myKSuiteDataResult = SingleLiveEvent()
+
+ fun refreshMyKSuite() = viewModelScope.launch(Dispatchers.IO) {
+ MyKSuiteDataUtils.fetchData()?.let(myKSuiteDataResult::postValue)
+ }
+}
diff --git a/app/src/main/java/com/infomaniak/drive/ui/menu/settings/KSuiteDashboardFragment.kt b/app/src/main/java/com/infomaniak/drive/ui/menu/settings/KSuiteDashboardFragment.kt
new file mode 100644
index 0000000000..8934822b27
--- /dev/null
+++ b/app/src/main/java/com/infomaniak/drive/ui/menu/settings/KSuiteDashboardFragment.kt
@@ -0,0 +1,46 @@
+/*
+ * Infomaniak kDrive - Android
+ * Copyright (C) 2025 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 .
+ */
+package com.infomaniak.drive.ui.menu.settings
+
+import android.os.Bundle
+import android.view.View
+import androidx.fragment.app.viewModels
+import com.infomaniak.core.myksuite.ui.utils.MyKSuiteUiUtils
+import com.infomaniak.core.myksuite.ui.views.MyKSuiteDashboardFragment
+import com.infomaniak.drive.ui.MyKSuiteViewModel
+import com.infomaniak.drive.utils.AccountUtils
+
+class KSuiteDashboardFragment : MyKSuiteDashboardFragment() {
+
+ private val myKSuiteViewModel: MyKSuiteViewModel by viewModels()
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ myKSuiteViewModel.refreshMyKSuite()
+ myKSuiteViewModel.myKSuiteDataResult.observe(viewLifecycleOwner) { myKSuiteData ->
+ resetContent(
+ dashboardData = MyKSuiteUiUtils.getDashboardData(
+ context = requireContext(),
+ myKSuiteData = myKSuiteData,
+ avatarUri = AccountUtils.currentUser?.avatar ?: "",
+ )
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/com/infomaniak/drive/ui/menu/settings/SettingsFragment.kt b/app/src/main/java/com/infomaniak/drive/ui/menu/settings/SettingsFragment.kt
index a12013aff1..22e21db488 100644
--- a/app/src/main/java/com/infomaniak/drive/ui/menu/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/infomaniak/drive/ui/menu/settings/SettingsFragment.kt
@@ -28,6 +28,8 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.infomaniak.core.myksuite.ui.data.MyKSuiteData
+import com.infomaniak.core.myksuite.ui.utils.MyKSuiteUiUtils
import com.infomaniak.drive.MatomoDrive.toFloat
import com.infomaniak.drive.MatomoDrive.trackEvent
import com.infomaniak.drive.R
@@ -36,6 +38,7 @@ import com.infomaniak.drive.data.models.UiSettings
import com.infomaniak.drive.databinding.FragmentSettingsBinding
import com.infomaniak.drive.utils.AccountUtils
import com.infomaniak.drive.utils.DrivePermissions
+import com.infomaniak.drive.utils.MyKSuiteDataUtils
import com.infomaniak.drive.utils.SyncUtils.launchAllUpload
import com.infomaniak.drive.utils.SyncUtils.syncImmediately
import com.infomaniak.lib.applock.LockActivity
@@ -69,6 +72,8 @@ class SettingsFragment : Fragment() {
requireActivity().launchAllUpload(drivePermissions)
}
+ setupMyKSuiteLayout()
+
syncPicture.setOnClickListener {
safeNavigate(R.id.syncSettingsActivity)
}
@@ -96,6 +101,34 @@ class SettingsFragment : Fragment() {
}
}
+ private fun toggleMyKSuiteLayoutVisibility(isVisible: Boolean) {
+ binding.myKSuiteSettingsTitle.isVisible = isVisible
+ binding.myKSuiteLayout.isVisible = isVisible
+ }
+
+ private fun setupMyKSuiteLayout() = with(binding) {
+ toggleMyKSuiteLayoutVisibility(MyKSuiteDataUtils.myKSuite != null)
+
+ MyKSuiteDataUtils.myKSuite?.let { myKSuiteData ->
+
+ myKSuiteSettingsEmail.text = myKSuiteData.mail.email
+
+ AccountUtils.getCurrentDrive()?.let { drive ->
+ myKSuiteSettingsTitle.setText(if (drive.isFreeTier) R.string.myKSuiteName else R.string.myKSuitePlusName)
+ }
+
+ dashboardSettings.setOnClickListener {
+ trackSettingsEvent("openMyKSuiteDashboard")
+ openMyKSuiteDashboard(myKSuiteData)
+ }
+ }
+ }
+
+ private fun openMyKSuiteDashboard(myKSuiteData: MyKSuiteData) {
+ val data = MyKSuiteUiUtils.getDashboardData(requireContext(), myKSuiteData, AccountUtils.currentUser?.avatar)
+ safeNavigate(directions = SettingsFragmentDirections.actionSettingsFragmentToMyKSuiteDashboardFragment(data))
+ }
+
private fun openThemeSettings() {
val items = arrayOf(
getString(R.string.themeSettingsLightLabel),
diff --git a/app/src/main/java/com/infomaniak/drive/utils/AccountUtils.kt b/app/src/main/java/com/infomaniak/drive/utils/AccountUtils.kt
index d5103c2004..ced6ba4971 100644
--- a/app/src/main/java/com/infomaniak/drive/utils/AccountUtils.kt
+++ b/app/src/main/java/com/infomaniak/drive/utils/AccountUtils.kt
@@ -203,6 +203,7 @@ object AccountUtils : CredentialManager() {
}
}
+ MyKSuiteDataUtils.deleteData(user.id)
removeUser(context, user)
}
diff --git a/app/src/main/java/com/infomaniak/drive/utils/MyKSuiteDataUtils.kt b/app/src/main/java/com/infomaniak/drive/utils/MyKSuiteDataUtils.kt
new file mode 100644
index 0000000000..1f87351f72
--- /dev/null
+++ b/app/src/main/java/com/infomaniak/drive/utils/MyKSuiteDataUtils.kt
@@ -0,0 +1,57 @@
+/*
+ * Infomaniak kDrive - Android
+ * Copyright (C) 2025 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 .
+ */
+package com.infomaniak.drive.utils
+
+import com.infomaniak.core.myksuite.ui.data.MyKSuiteData
+import com.infomaniak.core.myksuite.ui.data.MyKSuiteDataManager
+import com.infomaniak.drive.data.api.ApiRepository
+import com.infomaniak.lib.core.networking.HttpClient
+import com.infomaniak.lib.core.utils.SentryLog
+import kotlinx.serialization.ExperimentalSerializationApi
+import kotlinx.serialization.MissingFieldException
+import kotlin.coroutines.cancellation.CancellationException
+
+object MyKSuiteDataUtils : MyKSuiteDataManager() {
+
+ private val TAG = MyKSuiteDataUtils::class.simpleName.toString()
+
+ override val currentUserId get() = AccountUtils.currentUserId
+
+ override var myKSuite: MyKSuiteData? = null
+
+ override suspend fun fetchData(): MyKSuiteData? = runCatching {
+ MyKSuiteDataUtils.requestKSuiteData()
+ val apiResponse = ApiRepository.getMyKSuiteData(HttpClient.okHttpClient)
+ if (apiResponse.data != null) {
+ MyKSuiteDataUtils.upsertKSuiteData(apiResponse.data!!)
+ } else {
+ @OptIn(ExperimentalSerializationApi::class)
+ apiResponse.error?.exception?.let {
+ if (it is MissingFieldException || it.message?.contains("Unexpected JSON token") == true) {
+ SentryLog.e(TAG, "Error decoding the api result MyKSuiteObject", it)
+ }
+ }
+ }
+
+ return@runCatching apiResponse.data
+ }.getOrElse { exception ->
+ if (exception is CancellationException) throw exception
+ SentryLog.d(TAG, "Exception during myKSuite data fetch", exception)
+ null
+ }
+}
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index dfaa6d2138..e92aea5124 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -45,233 +45,318 @@
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
-
+ android:layout_height="match_parent"
+ android:layout_marginVertical="@dimen/marginStandard"
+ android:orientation="vertical">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/marginStandard"
+ android:layout_marginBottom="@dimen/marginStandardMedium"
+ tools:text="@string/myKSuiteName" />
+
+
-
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
-
+ tools:text="ellen.ripley@ik.me" />
-
+
-
-
-
-
-
-
-
-
-
-
-
+ android:background="?selectableItemBackground"
+ android:padding="20dp">
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+ android:background="?selectableItemBackground"
+ android:paddingHorizontal="@dimen/marginStandard"
+ android:paddingTop="@dimen/marginStandardSmall"
+ android:paddingBottom="@dimen/marginStandard">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml
index 6cd6cfa919..b8959e9f8a 100644
--- a/app/src/main/res/navigation/main_navigation.xml
+++ b/app/src/main/res/navigation/main_navigation.xml
@@ -509,6 +509,11 @@
+
+
+
+