Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(MyKSuite-dashboard): Add dashboard #1532

Merged
merged 7 commits into from
Feb 24, 2025
48 changes: 12 additions & 36 deletions .idea/navEditor.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions app/src/main/java/com/infomaniak/drive/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -146,6 +147,8 @@ class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycleObser
)
HttpClient.init(tokenInterceptorListener)
MqttClientWrapper.init(applicationContext)

MyKSuiteDataUtils.initDatabase(this)
}

override fun onStart(owner: LifecycleOwner) {
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/com/infomaniak/drive/data/api/ApiRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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() {

Expand Down Expand Up @@ -509,6 +511,15 @@ object ApiRepository : ApiRepositoryCore() {
return callApi(ApiRoutes.cancelExternalImport(driveId, importId), PUT)
}

fun getMyKSuiteData(okHttpClient: OkHttpClient): ApiResponse<MyKSuiteData> {
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 {
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/java/com/infomaniak/drive/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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() }
Expand Down Expand Up @@ -204,6 +206,7 @@ class MainActivity : BaseActivity() {
override fun onStart() {
super.onStart()
mainViewModel.loadRootFiles()
myKSuiteViewModel.refreshMyKSuite()
handleDeletionOfUploadedPhotos()
}

Expand Down Expand Up @@ -439,7 +442,7 @@ class MainActivity : BaseActivity() {
}

when (destination.id) {
R.id.fileDetailsFragment -> {
R.id.fileDetailsFragment, RMyKSuite.id.myKSuiteDashboardFragment -> {
setColorNavigationBar(true)
}
R.id.fileShareLinkSettingsFragment -> {
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/java/com/infomaniak/drive/ui/MyKSuiteViewModel.kt
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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<MyKSuiteData>()

fun refreshMyKSuite() = viewModelScope.launch(Dispatchers.IO) {
MyKSuiteDataUtils.fetchData()?.let(myKSuiteDataResult::postValue)
}
}
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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 ?: "",
)
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -69,6 +72,8 @@ class SettingsFragment : Fragment() {
requireActivity().launchAllUpload(drivePermissions)
}

setupMyKSuiteLayout()

syncPicture.setOnClickListener {
safeNavigate(R.id.syncSettingsActivity)
}
Expand Down Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ object AccountUtils : CredentialManager() {
}
}

MyKSuiteDataUtils.deleteData(user.id)
removeUser(context, user)
}

Expand Down
57 changes: 57 additions & 0 deletions app/src/main/java/com/infomaniak/drive/utils/MyKSuiteDataUtils.kt
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/
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
}
}
Loading