Skip to content

Commit

Permalink
feat: My ksuite (#2198)
Browse files Browse the repository at this point in the history
  • Loading branch information
FabianDevel authored Feb 25, 2025
2 parents c6eee12 + a53475b commit 076527f
Show file tree
Hide file tree
Showing 43 changed files with 755 additions and 167 deletions.
80 changes: 75 additions & 5 deletions .idea/navEditor.xml

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

2 changes: 1 addition & 1 deletion Core
Submodule Core updated 72 files
+72 −0 MykSuite/build.gradle.kts
+0 −0 MykSuite/consumer-rules.pro
+21 −0 MykSuite/proguard-rules.pro
+112 −0 MykSuite/schemas/com.infomaniak.core.myksuite.ui.data.MyKSuiteDatabase/1.json
+73 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/MyKSuiteChips.kt
+37 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/MyKSuiteGradient.kt
+78 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/MyKSuitePrimaryButton.kt
+113 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/TextWithIcon.kt
+36 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/components/WeightOneSpacer.kt
+54 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteData.kt
+38 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteDataDao.kt
+54 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteDataManager.kt
+54 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/MyKSuiteDatabase.kt
+32 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/kSuiteDrive.kt
+40 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/data/kSuiteMail.kt
+27 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/network/ApiRoutes.kt
+305 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteDashboardScreen.kt
+153 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeBottomSheet.kt
+47 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/MyKSuiteUpgradeFeature.kt
+103 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ExpandableItemView.kt
+80 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/InformationBlock.kt
+74 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/LimitedFunctionnalities.kt
+60 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuiteButtonType.kt
+72 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/MyKSuiteTextItem.kt
+125 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/ProductsStorageQuotas.kt
+80 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/UpgradeFeature.kt
+96 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/screens/components/UserAvatar.kt
+61 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsDark.kt
+67 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/ColorsLight.kt
+38 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Dimens.kt
+37 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Margin.kt
+62 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Theme.kt
+82 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/theme/Type.kt
+77 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/utils/MyKSuiteUiUtils.kt
+51 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/BaseMyKSuiteChipView.kt
+28 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteChipView.kt
+51 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteDashboardFragment.kt
+28 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuitePlusChipView.kt
+62 −0 MykSuite/src/main/java/com/infomaniak/core/myksuite/ui/views/MyKSuiteUpgradeBottomSheetDialog.kt
+79 −0 MykSuite/src/main/res/drawable-night/ic_logo_my_ksuite.xml
+105 −0 MykSuite/src/main/res/drawable-night/ic_logo_my_ksuite_plus.xml
+29 −0 MykSuite/src/main/res/drawable-night/ic_person.xml
+198 −0 MykSuite/src/main/res/drawable-night/illu_banner.xml
+26 −0 MykSuite/src/main/res/drawable-night/illu_dashboard_background.xml
+26 −0 MykSuite/src/main/res/drawable/ic_chevron_down.xml
+26 −0 MykSuite/src/main/res/drawable/ic_chevron_up.xml
+47 −0 MykSuite/src/main/res/drawable/ic_circle_i.xml
+26 −0 MykSuite/src/main/res/drawable/ic_cross_thick.xml
+27 −0 MykSuite/src/main/res/drawable/ic_drive_cloud.xml
+27 −0 MykSuite/src/main/res/drawable/ic_envelope.xml
+27 −0 MykSuite/src/main/res/drawable/ic_enveloppe_italic.xml
+27 −0 MykSuite/src/main/res/drawable/ic_folder_circle_filled_arrow_up.xml
+27 −0 MykSuite/src/main/res/drawable/ic_gift.xml
+79 −0 MykSuite/src/main/res/drawable/ic_logo_my_ksuite.xml
+105 −0 MykSuite/src/main/res/drawable/ic_logo_my_ksuite_plus.xml
+27 −0 MykSuite/src/main/res/drawable/ic_padlock.xml
+27 −0 MykSuite/src/main/res/drawable/ic_paperplane.xml
+29 −0 MykSuite/src/main/res/drawable/ic_person.xml
+198 −0 MykSuite/src/main/res/drawable/illu_banner.xml
+26 −0 MykSuite/src/main/res/drawable/illu_dashboard_background.xml
+41 −0 MykSuite/src/main/res/navigation/my_ksuite_navigation.xml
+47 −0 MykSuite/src/main/res/values-de/strings.xml
+47 −0 MykSuite/src/main/res/values-es/strings.xml
+47 −0 MykSuite/src/main/res/values-fr/strings.xml
+47 −0 MykSuite/src/main/res/values-it/strings.xml
+20 −0 MykSuite/src/main/res/values-night/colors.xml
+24 −0 MykSuite/src/main/res/values/attrs.xml
+20 −0 MykSuite/src/main/res/values/colors.xml
+51 −0 MykSuite/src/main/res/values/strings.xml
+16 −1 gradle/core.versions.toml
+6 −0 src/main/kotlin/com/infomaniak/core/extensions/ContextExt.kt
+20 −0 src/main/kotlin/com/infomaniak/core/extensions/StringExt.kt
4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ dependencies {
implementation project(':Core:Legacy:BugTracker')
implementation project(':Core:Legacy:Confetti')
implementation project(':Core:Legacy:Stores')
implementation project(':Core:MyKSuite')
implementation project(':HtmlCleaner')

implementation libs.rich.html.editor
Expand Down Expand Up @@ -120,6 +121,9 @@ dependencies {

implementation libs.coil.svg

// Compose
implementation libs.compose.ui.android

// Test
testImplementation libs.junit
androidTestImplementation libs.ext.junit
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/com/infomaniak/mail/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,13 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle
if (BuildConfig.DEBUG) configureDebugMode()
configureSentry()
enforceAppTheme()
configureAccountUtils()
configureRoomDatabases()
configureAppReloading()
configureInfomaniakCore()
notificationUtils.initNotificationChannel()
configureHttpClient()

localSettings.storageBannerDisplayAppLaunches++
}

override fun onStart(owner: LifecycleOwner) {
Expand Down Expand Up @@ -217,8 +219,9 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle
AppCompatDelegate.setDefaultNightMode(localSettings.theme.mode)
}

private fun configureAccountUtils() {
private fun configureRoomDatabases() {
AccountUtils.init(this)
MyKSuiteDataUtils.initDatabase(this)
}

private fun configureAppReloading() {
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/com/infomaniak/mail/data/LocalSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class LocalSettings private constructor(context: Context) : SharedValues {
var showWebViewOutdated by sharedValue("showWebViewOutdatedKey", true)
var accessTokenApiCallRecord by sharedValue<ApiCallRecord>("accessTokenApiCallRecordKey", null)
var lastSelectedScheduleEpoch by sharedValue<Long>("lastSelectedScheduleEpochKey", null)
var storageBannerDisplayAppLaunches by sharedValue("storageBannerDisplayAppLaunchesKey", 0)
var hasClosedStorageBanner by sharedValue("hasClosedStorageBannerKey", false)

fun removeSettings() = sharedPreferences.transaction { clear() }

Expand All @@ -93,6 +95,11 @@ class LocalSettings private constructor(context: Context) : SharedValues {
firebaseRegisteredUsers = mutableSetOf()
}

fun resetStorageBannerAppLaunches() {
hasClosedStorageBanner = true
storageBannerDisplayAppLaunches = 0
}

enum class EmailForwarding(@StringRes val localisedNameRes: Int) {
IN_BODY(R.string.settingsTransferInBody),
AS_ATTACHMENT(R.string.settingsTransferAsAttachment),
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/com/infomaniak/mail/data/api/ApiRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.infomaniak.mail.data.api

import com.infomaniak.core.myksuite.ui.data.MyKSuiteData
import com.infomaniak.core.utils.FORMAT_FULL_DATE_WITH_HOUR
import com.infomaniak.core.utils.format
import com.infomaniak.lib.core.InfomaniakCore
Expand Down Expand Up @@ -64,6 +65,7 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.util.Date
import com.infomaniak.core.myksuite.ui.network.ApiRoutes as MyKSuiteApiRoutes

object ApiRepository : ApiRepositoryCore() {

Expand Down Expand Up @@ -449,6 +451,15 @@ object ApiRepository : ApiRepositoryCore() {
return callApi(url = ApiRoutes.shareLink(mailboxUuid, folderId, mailId), method = POST)
}

fun getMyKSuiteData(okHttpClient: OkHttpClient): ApiResponse<MyKSuiteData> {
return ApiController.callApi(
url = MyKSuiteApiRoutes.myKSuiteData(),
method = ApiController.ApiMethod.GET,
okHttpClient = okHttpClient,
useKotlinxSerialization = true,
)
}

/**
* Create batches of the given values to perform the given request
* @param values Data to batch
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/infomaniak/mail/data/models/Quotas.kt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ class Quotas : EmbeddedRealmObject {
@SerialName("size")
private var _size: Long = 0L

val size: Long get() = _size * 1_000L // Convert from KiloOctets to Octets
val size: Long get() = _size * 1_024L // Convert from KiloOctets to Octets

val isFull get() = getProgress() >= 100

fun getText(context: Context): String {

Expand Down
24 changes: 20 additions & 4 deletions app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import androidx.navigation.NavDestination
import androidx.navigation.fragment.NavHostFragment
import androidx.work.Data
import com.airbnb.lottie.LottieAnimationView
import com.infomaniak.core.myksuite.ui.screens.KSuiteApp
import com.infomaniak.core.myksuite.ui.utils.MyKSuiteUiUtils.openMyKSuiteUpgradeBottomSheet
import com.infomaniak.core.utils.FORMAT_SCHEDULE_MAIL
import com.infomaniak.core.utils.year
import com.infomaniak.lib.core.MatomoCore.TrackerAction
Expand Down Expand Up @@ -269,16 +271,30 @@ class MainActivity : BaseActivity() {
with(workInfo.outputData) {
refreshDraftFolderIfNeeded()
val errorRes = getInt(DraftsActionsWorker.ERROR_MESSAGE_RESID_KEY, 0)
if (errorRes > 0) {
showSendingSnackbarTimer.cancel()
snackbarManager.setValue(getString(errorRes))
}
displayError(errorRes)
}
}
}
}
}

private fun displayError(errorRes: Int) {
if (errorRes > 0) {
showSendingSnackbarTimer.cancel()

val hasLimitBeenReached = errorRes == ErrorCode.getTranslateResForDrafts(ErrorCode.SEND_LIMIT_EXCEEDED) ||
errorRes == ErrorCode.getTranslateResForDrafts(ErrorCode.SEND_DAILY_LIMIT_REACHED)

if (mainViewModel.currentMailbox.value?.isFreeMailbox == true && hasLimitBeenReached) {
snackbarManager.setValue(getString(errorRes), buttonTitle = R.string.buttonUpgrade) {
navController.openMyKSuiteUpgradeBottomSheet(KSuiteApp.Mail)
}
} else {
snackbarManager.setValue(getString(errorRes))
}
}
}

private fun Data.displayCompletedDraftWorkerResults() {
getString(DraftsActionsWorker.RESULT_DRAFT_ACTION_KEY)?.let { draftAction ->
when (draftAction.toEnumOrThrow<DraftAction>()) {
Expand Down
29 changes: 25 additions & 4 deletions app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.infomaniak.lib.core.utils.SentryLog
import com.infomaniak.lib.core.utils.SingleLiveEvent
import com.infomaniak.mail.MatomoMail.trackMultiSelectionEvent
import com.infomaniak.mail.R
import com.infomaniak.mail.data.LocalSettings
import com.infomaniak.mail.data.api.ApiRepository
import com.infomaniak.mail.data.api.ApiRoutes
import com.infomaniak.mail.data.cache.RealmDatabase
Expand All @@ -54,18 +55,16 @@ import com.infomaniak.mail.di.IoDispatcher
import com.infomaniak.mail.di.MailboxInfoRealm
import com.infomaniak.mail.ui.main.SnackbarManager
import com.infomaniak.mail.ui.main.SnackbarManager.UndoData
import com.infomaniak.mail.utils.AccountUtils
import com.infomaniak.mail.utils.*
import com.infomaniak.mail.utils.ContactUtils.getPhoneContacts
import com.infomaniak.mail.utils.ContactUtils.mergeApiContactsIntoPhoneContacts
import com.infomaniak.mail.utils.NotificationUtils
import com.infomaniak.mail.utils.NotificationUtils.Companion.cancelNotification
import com.infomaniak.mail.utils.SharedUtils
import com.infomaniak.mail.utils.SharedUtils.Companion.updateSignatures
import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder
import com.infomaniak.mail.utils.Utils.runCatchingRealm
import com.infomaniak.mail.utils.coroutineContext
import com.infomaniak.mail.utils.extensions.*
import com.infomaniak.mail.views.itemViews.AvatarMergedContactData
import com.infomaniak.mail.views.itemViews.MyKSuiteStorageBanner.StorageLevel
import dagger.hilt.android.lifecycle.HiltViewModel
import io.realm.kotlin.Realm
import io.realm.kotlin.ext.toRealmList
Expand Down Expand Up @@ -109,6 +108,9 @@ class MainViewModel @Inject constructor(
private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher)
private var refreshEverythingJob: Job? = null

@Inject
lateinit var localSettings: LocalSettings

val isDownloadingChanges: MutableLiveData<Boolean> = MutableLiveData(false)
val isMovedToNewFolder = SingleLiveEvent<Boolean>()
val toggleLightThemeForMessage = SingleLiveEvent<Message>()
Expand Down Expand Up @@ -162,6 +164,22 @@ class MainViewModel @Inject constructor(
it?.let(quotasController::getQuotasAsync) ?: emptyFlow()
}.asLiveData(ioCoroutineContext)

val storageBannerStatus = currentQuotasLive.map { quotas ->
when {
quotas == null -> null
quotas.isFull -> StorageLevel.Full
quotas.getProgress() > StorageLevel.WARNING_THRESHOLD -> {
if (!localSettings.hasClosedStorageBanner || localSettings.storageBannerDisplayAppLaunches % 10 == 0) {
localSettings.hasClosedStorageBanner = false
StorageLevel.Warning
} else {
StorageLevel.Normal
}
}
else -> StorageLevel.Normal
}
}

val currentPermissionsLive = _currentMailboxObjectId.flatMapLatest {
it?.let(permissionsController::getPermissionsAsync) ?: emptyFlow()
}.asLiveData(ioCoroutineContext)
Expand Down Expand Up @@ -300,6 +318,9 @@ class MainViewModel @Inject constructor(
// Refresh User
AccountUtils.updateCurrentUser()

// Refresh My kSuite asynchronously, because it's not required for the threads list display
launch { MyKSuiteDataUtils.fetchData() }

// Refresh Mailboxes
SentryLog.d(TAG, "Refresh mailboxes from remote")
with(ApiRepository.getMailboxes()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,23 @@
*/
package com.infomaniak.mail.ui.bottomSheetDialogs

import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.infomaniak.lib.core.utils.safeNavigate
import com.infomaniak.core.myksuite.ui.screens.KSuiteApp
import com.infomaniak.core.myksuite.ui.utils.MyKSuiteUiUtils.openMyKSuiteUpgradeBottomSheet
import com.infomaniak.lib.core.utils.setBackNavigationResult
import com.infomaniak.mail.MatomoMail.trackScheduleSendEvent
import com.infomaniak.mail.R
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject


@AndroidEntryPoint
class ScheduleSendBottomSheetDialog @Inject constructor() : SelectScheduleOptionBottomSheet() {

private val navigationArgs: ScheduleSendBottomSheetDialogArgs by navArgs()

override val isCurrentMailboxFree: Boolean by lazy { navigationArgs.isCurrentMailboxFree }

// Navigation args does not support nullable primitive types, so we use 0L
// as a replacement (corresponding to Thursday 1 January 1970 00:00:00 UT).
override val lastSelectedEpoch: Long? by lazy { navigationArgs.lastSelectedScheduleEpoch.takeIf { it != 0L } }
Expand All @@ -51,10 +54,7 @@ class ScheduleSendBottomSheetDialog @Inject constructor() : SelectScheduleOption

override fun onCustomScheduleOptionClicked() {
if (navigationArgs.isCurrentMailboxFree) {
safeNavigate(
resId = R.id.upgradeProductBottomSheetDialog,
currentClassName = ScheduleSendBottomSheetDialog::class.java.name,
)
findNavController().openMyKSuiteUpgradeBottomSheet(KSuiteApp.Mail)
} else {
setBackNavigationResult(OPEN_DATE_AND_TIME_SCHEDULE_DIALOG, true)
}
Expand Down
Loading

0 comments on commit 076527f

Please sign in to comment.