Merge branch 'feat/2604_option-to-toggle-webview-light-or-dark-mode-in-action-sheet' into 'develop'
Add an action for switching between light and dark mode in the message details web view See merge request android/mail/proton-mail-android!954
This commit is contained in:
commit
01b143f6ad
|
@ -28,7 +28,6 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
|
@ -261,9 +260,6 @@ public class ComposeMessageActivity
|
|||
@Inject
|
||||
RenderDimensionsProvider renderDimensionsProvider;
|
||||
|
||||
@Inject
|
||||
SetUpWebViewDarkModeHandlingIfSupported setUpWebViewDarkModeHandlingIfSupported;
|
||||
|
||||
String composerInstanceId;
|
||||
|
||||
Menu menu;
|
||||
|
@ -405,7 +401,12 @@ public class ComposeMessageActivity
|
|||
webSettings.setBuiltInZoomControls(true);
|
||||
webSettings.setPluginState(WebSettings.PluginState.OFF);
|
||||
|
||||
setUpWebViewDarkModeHandlingIfSupported.invoke(this, quotedMessageWebView);
|
||||
composeMessageViewModel.setUpWebViewDarkMode(
|
||||
this,
|
||||
mUserManager.requireCurrentUserId(),
|
||||
quotedMessageWebView,
|
||||
composeMessageViewModel.getDraftId()
|
||||
);
|
||||
|
||||
binding.composerQuotedMessageContainer.addView(quotedMessageWebView);
|
||||
}
|
||||
|
@ -1617,7 +1618,7 @@ public class ComposeMessageActivity
|
|||
if (!respondInline) {
|
||||
String css = AppUtil.readTxt(this, R.raw.css_reset_with_custom_props);
|
||||
String darkCss = "";
|
||||
if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES) {
|
||||
if (composeMessageViewModel.isAppInDarkMode(this)) {
|
||||
darkCss = AppUtil.readTxt(this, R.raw.css_reset_dark_mode_only);
|
||||
}
|
||||
Transformer viewportTransformer = new ViewportTransformer(renderDimensionsProvider.getRenderWidth(this), css, darkCss);
|
||||
|
@ -1661,7 +1662,7 @@ public class ComposeMessageActivity
|
|||
String content = composeMessageViewModel.getMessageDataResult().getContent();
|
||||
String css = AppUtil.readTxt(this, R.raw.css_reset_with_custom_props);
|
||||
String darkCss = "";
|
||||
if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES) {
|
||||
if (composeMessageViewModel.isAppInDarkMode(this)) {
|
||||
darkCss = AppUtil.readTxt(this, R.raw.css_reset_dark_mode_only);
|
||||
}
|
||||
Transformer contentTransformer = new ViewportTransformer(renderDimensionsProvider.getRenderWidth(this), css, darkCss);
|
||||
|
|
|
@ -69,6 +69,7 @@ import ch.protonmail.android.views.messageDetails.MessageDetailsHeaderView
|
|||
import ch.protonmail.android.views.messageDetails.ReplyActionsView
|
||||
import kotlinx.android.synthetic.main.layout_message_details.view.*
|
||||
import kotlinx.android.synthetic.main.layout_message_details_body.view.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.apache.http.protocol.HTTP
|
||||
import timber.log.Timber
|
||||
|
||||
|
@ -322,6 +323,10 @@ internal class MessageDetailsAdapter(
|
|||
val messageBodyProgress = itemView.messageWebViewContainer
|
||||
.findViewById<ProgressBar>(R.id.item_message_body_progress_view_id) ?: return
|
||||
|
||||
message.messageId?.let {
|
||||
setUpWebViewDarkModeBlocking(webView, it)
|
||||
}
|
||||
|
||||
messageBodyProgress.isVisible = listItem.messageFormattedHtml.isNullOrEmpty()
|
||||
displayRemoteContentButton.isVisible = false
|
||||
loadEmbeddedImagesButton.isVisible = listItem.showLoadEmbeddedImagesButton
|
||||
|
@ -427,8 +432,6 @@ internal class MessageDetailsAdapter(
|
|||
openInProtonCalenderView: View,
|
||||
editDraftButton: Button
|
||||
) {
|
||||
val item = visibleItems[position]
|
||||
|
||||
loadEmbeddedImagesContainer.setOnClickListener { view ->
|
||||
view.visibility = View.GONE
|
||||
// Once images were loaded for one message, we automatically load them for all the others, so:
|
||||
|
@ -458,6 +461,7 @@ internal class MessageDetailsAdapter(
|
|||
}
|
||||
|
||||
openInProtonCalenderView.setOnClickListener {
|
||||
val item = visibleItems[position]
|
||||
onOpenInProtonCalendarClicked(item.message)
|
||||
}
|
||||
|
||||
|
@ -561,6 +565,18 @@ internal class MessageDetailsAdapter(
|
|||
expandLastNonDraftItem()
|
||||
}
|
||||
|
||||
fun reloadMessage(messageId: String) {
|
||||
val item = visibleItems.find { it.message.messageId == messageId && it.itemType == TYPE_ITEM }
|
||||
val itemIndex = visibleItems.indexOf(item)
|
||||
// Set message formatted html to null, in order to trigger loading
|
||||
// and switch to light/dark mode in the web view
|
||||
val newItem = item?.apply { messageFormattedHtml = null }
|
||||
newItem?.let {
|
||||
visibleItems[itemIndex] = newItem
|
||||
notifyItemChanged(itemIndex)
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandLastNonDraftItem() {
|
||||
val lastNonDraftHeaderIndex = visibleItems.indexOfLast {
|
||||
!it.message.isDraft() && it.itemType == TYPE_HEADER
|
||||
|
@ -613,8 +629,6 @@ internal class MessageDetailsAdapter(
|
|||
}
|
||||
|
||||
private fun configureWebView(webView: WebView, pmWebViewClient: PMWebViewClient) {
|
||||
setUpWebViewDarkModeHandlingIfSupported(context, webView)
|
||||
|
||||
webView.isScrollbarFadingEnabled = false
|
||||
webView.isVerticalScrollBarEnabled = false
|
||||
webView.isHorizontalScrollBarEnabled = false
|
||||
|
@ -742,4 +756,7 @@ internal class MessageDetailsAdapter(
|
|||
|
||||
private fun isAndroidAPILevelLowerThan26() = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
|
||||
|
||||
private fun setUpWebViewDarkModeBlocking(webView: WebView, messageId: String) = runBlocking {
|
||||
setUpWebViewDarkModeHandlingIfSupported(context, userManager.requireCurrentUserId(), webView, messageId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ import ch.protonmail.android.mailbox.domain.usecase.MoveMessagesToFolder
|
|||
import ch.protonmail.android.mailbox.presentation.ConversationModeEnabled
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import ch.protonmail.android.ui.model.LabelChipUiModel
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.usecase.delete.DeleteMessage
|
||||
import ch.protonmail.android.usecase.fetch.FetchVerificationKeys
|
||||
|
@ -89,6 +90,7 @@ import ch.protonmail.android.utils.HTMLTransformer.DefaultTransformer
|
|||
import ch.protonmail.android.utils.HTMLTransformer.ViewportTransformer
|
||||
import ch.protonmail.android.utils.UiUtil
|
||||
import ch.protonmail.android.utils.crypto.KeyInformation
|
||||
import ch.protonmail.android.details.domain.usecase.GetViewInDarkModeMessagePreference
|
||||
import ch.protonmail.android.viewmodel.ConnectivityBaseViewModel
|
||||
import com.birbit.android.jobqueue.JobManager
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
@ -128,6 +130,8 @@ import javax.inject.Inject
|
|||
@Suppress("LongParameterList") // Every new parameter adds a new issue and breaks the build
|
||||
@HiltViewModel
|
||||
internal class MessageDetailsViewModel @Inject constructor(
|
||||
private val isAppInDarkMode: IsAppInDarkMode,
|
||||
private val getViewInDarkModeMessagePreference: GetViewInDarkModeMessagePreference,
|
||||
private val messageDetailsRepository: MessageDetailsRepository,
|
||||
private val messageRepository: MessageRepository,
|
||||
private val userManager: UserManager,
|
||||
|
@ -191,6 +195,7 @@ internal class MessageDetailsViewModel @Inject constructor(
|
|||
private val _messageDetailsError: MutableLiveData<Event<String>> = MutableLiveData()
|
||||
private val _showPermissionMissingDialog: MutableLiveData<Unit> = MutableLiveData()
|
||||
private val _conversationUiFlow = MutableSharedFlow<ConversationUiModel>(replay = 1)
|
||||
private val _reloadMessageFlow = MutableSharedFlow<String>(replay = 1)
|
||||
|
||||
val conversationUiModel: SharedFlow<ConversationUiModel>
|
||||
get() = _conversationUiFlow
|
||||
|
@ -213,6 +218,9 @@ internal class MessageDetailsViewModel @Inject constructor(
|
|||
val messageRenderedWithImages: LiveData<Message>
|
||||
get() = _messageRenderedWithImages
|
||||
|
||||
val reloadMessageFlow: SharedFlow<String>
|
||||
get() = _reloadMessageFlow
|
||||
|
||||
private var areImagesDisplayed: Boolean = false
|
||||
|
||||
private var visibleToTheUser = true
|
||||
|
@ -898,4 +906,14 @@ internal class MessageDetailsViewModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun isAppInDarkMode(context: Context) = isAppInDarkMode.invoke(context)
|
||||
|
||||
fun isWebViewInDarkModeBlocking(context: Context, messageId: String) = runBlocking {
|
||||
getViewInDarkModeMessagePreference(context, userManager.requireCurrentUserId(), messageId)
|
||||
}
|
||||
|
||||
fun reloadMessage(messageId: String) {
|
||||
_reloadMessageFlow.tryEmit(messageId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import ch.protonmail.android.data.local.ContactDatabase
|
|||
import ch.protonmail.android.data.local.CounterDatabase
|
||||
import ch.protonmail.android.data.local.MessageDao
|
||||
import ch.protonmail.android.data.local.MessageDatabase
|
||||
import ch.protonmail.android.data.local.MessagePreferenceDao
|
||||
import ch.protonmail.android.data.local.NotificationDao
|
||||
import ch.protonmail.android.data.local.NotificationDatabase
|
||||
import ch.protonmail.android.data.local.PendingActionDao
|
||||
|
@ -79,4 +80,6 @@ class DatabaseProvider @Inject constructor(
|
|||
fun providePendingActionDao(userId: UserId): PendingActionDao =
|
||||
PendingActionDatabase.getInstance(context, userId).getDao()
|
||||
|
||||
fun provideMessagePreferenceDao(userId: UserId): MessagePreferenceDao =
|
||||
MessageDatabase.getInstance(context, userId).getMessagePreferenceDao()
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
package ch.protonmail.android.compose
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.text.Spanned
|
||||
import android.text.TextUtils
|
||||
import android.webkit.WebView
|
||||
import androidx.core.net.MailTo
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
|
@ -57,6 +59,7 @@ import ch.protonmail.android.events.Status
|
|||
import ch.protonmail.android.feature.account.allLoggedInBlocking
|
||||
import ch.protonmail.android.jobs.contacts.GetSendPreferenceJob
|
||||
import ch.protonmail.android.ui.view.DaysHoursPair
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.usecase.compose.SaveDraft
|
||||
import ch.protonmail.android.usecase.compose.SaveDraftResult
|
||||
|
@ -69,6 +72,7 @@ import ch.protonmail.android.utils.MailToData
|
|||
import ch.protonmail.android.utils.MessageUtils
|
||||
import ch.protonmail.android.utils.UiUtil
|
||||
import ch.protonmail.android.utils.resources.StringResourceResolver
|
||||
import ch.protonmail.android.utils.webview.SetUpWebViewDarkModeHandlingIfSupported
|
||||
import ch.protonmail.android.viewmodel.ConnectivityBaseViewModel
|
||||
import com.squareup.otto.Subscribe
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
@ -107,6 +111,7 @@ const val GREATER_THAN = ">"
|
|||
|
||||
@HiltViewModel
|
||||
class ComposeMessageViewModel @Inject constructor(
|
||||
private val isAppInDarkMode: IsAppInDarkMode,
|
||||
private val composeMessageRepository: ComposeMessageRepository,
|
||||
private val userManager: UserManager,
|
||||
accountManager: AccountManager,
|
||||
|
@ -120,7 +125,8 @@ class ComposeMessageViewModel @Inject constructor(
|
|||
verifyConnection: VerifyConnection,
|
||||
networkConfigurator: NetworkConfigurator,
|
||||
private val htmlToSpanned: HtmlToSpanned,
|
||||
private val addExpirationTimeToMessage: AddExpirationTimeToMessage
|
||||
private val addExpirationTimeToMessage: AddExpirationTimeToMessage,
|
||||
private val setUpWebViewDarkModeHandlingIfSupported: SetUpWebViewDarkModeHandlingIfSupported
|
||||
) : ConnectivityBaseViewModel(verifyConnection, networkConfigurator) {
|
||||
|
||||
// region events data
|
||||
|
@ -1312,4 +1318,17 @@ class ComposeMessageViewModel @Inject constructor(
|
|||
Timber.v("Parsed mailto: $dataString to $mailToData")
|
||||
return mailToData
|
||||
}
|
||||
|
||||
fun setUpWebViewDarkMode(context: Context, userId: UserId, webView: WebView, draftId: String) {
|
||||
viewModelScope.launch {
|
||||
setUpWebViewDarkModeHandlingIfSupported(
|
||||
context,
|
||||
userId,
|
||||
webView,
|
||||
draftId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun isAppInDarkMode(context: Context) = isAppInDarkMode.invoke(context)
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import ch.protonmail.android.data.ProtonMailConverters
|
|||
import ch.protonmail.android.data.local.model.Attachment
|
||||
import ch.protonmail.android.data.local.model.AttachmentTypesConverter
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity
|
||||
import ch.protonmail.android.data.local.model.MessagesTypesConverter
|
||||
import ch.protonmail.android.mailbox.data.local.ConversationDao
|
||||
import ch.protonmail.android.mailbox.data.local.ConversationTypesConverter
|
||||
|
@ -38,9 +39,10 @@ import me.proton.core.data.room.db.CommonConverters
|
|||
Attachment::class,
|
||||
ConversationDatabaseModel::class,
|
||||
Message::class,
|
||||
UnreadCounterEntity::class,
|
||||
MessagePreferenceEntity::class,
|
||||
UnreadCounterEntity::class
|
||||
],
|
||||
version = 14
|
||||
version = 15
|
||||
)
|
||||
@TypeConverters(
|
||||
value = [
|
||||
|
@ -57,8 +59,9 @@ internal abstract class MessageDatabase : RoomDatabase() {
|
|||
fun getDao(): MessageDao =
|
||||
getMessageDao()
|
||||
|
||||
abstract fun getMessageDao(): MessageDao
|
||||
abstract fun getConversationDao(): ConversationDao
|
||||
abstract fun getMessageDao(): MessageDao
|
||||
abstract fun getMessagePreferenceDao(): MessagePreferenceDao
|
||||
abstract fun getUnreadCounterDao(): UnreadCounterDao
|
||||
|
||||
companion object Factory : DatabaseFactory<MessageDatabase>(
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.data.local
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity.Companion.COLUMN_MESSAGE_ID
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity.Companion.TABLE_MESSAGE_PREFERENCE
|
||||
import me.proton.core.data.room.db.BaseDao
|
||||
|
||||
@Dao
|
||||
abstract class MessagePreferenceDao : BaseDao<MessagePreferenceEntity>() {
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
abstract suspend fun saveMessagePreference(messagePreferenceEntity: MessagePreferenceEntity): Long
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
FROM $TABLE_MESSAGE_PREFERENCE
|
||||
WHERE $COLUMN_MESSAGE_ID = :messageId
|
||||
"""
|
||||
)
|
||||
abstract suspend fun findMessagePreference(messageId: String): MessagePreferenceEntity?
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.data.local.model
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity.Companion.TABLE_MESSAGE_PREFERENCE
|
||||
|
||||
@Entity(
|
||||
tableName = TABLE_MESSAGE_PREFERENCE
|
||||
)
|
||||
data class MessagePreferenceEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = COLUMN_ID)
|
||||
val id: Long = 0,
|
||||
|
||||
@ColumnInfo(name = COLUMN_MESSAGE_ID)
|
||||
val messageId: String,
|
||||
|
||||
@ColumnInfo(name = COLUMN_VIEW_IN_DARK_MODE)
|
||||
val viewInDarkMode: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
const val TABLE_MESSAGE_PREFERENCE = "message_preference"
|
||||
const val COLUMN_ID = "ID"
|
||||
const val COLUMN_MESSAGE_ID = "message_id"
|
||||
const val COLUMN_VIEW_IN_DARK_MODE = "view_in_dark_mode"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.details.domain.usecase
|
||||
|
||||
import android.content.Context
|
||||
import androidx.webkit.WebViewFeature
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A use case that checks whether a given message should be viewed in dark mode or not
|
||||
*/
|
||||
class GetViewInDarkModeMessagePreference @Inject constructor(
|
||||
private val messageRepository: MessageRepository,
|
||||
private val isAppInDarkMode: IsAppInDarkMode
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
context: Context,
|
||||
userId: UserId,
|
||||
messageId: String
|
||||
): Boolean {
|
||||
return if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
|
||||
messageRepository.getViewInDarkModeMessagePreference(userId, messageId)
|
||||
?: isAppInDarkMode(context)
|
||||
} else false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.details.domain.usecase
|
||||
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A use case that saves the view in dark mode message preference
|
||||
*/
|
||||
class SetViewInDarkModeMessagePreference @Inject constructor(
|
||||
private val messageRepository: MessageRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(
|
||||
userId: UserId,
|
||||
messageId: String,
|
||||
viewInDarkMode: Boolean
|
||||
) {
|
||||
messageRepository.saveViewInDarkModeMessagePreference(userId, messageId, viewInDarkMode)
|
||||
}
|
||||
}
|
|
@ -66,7 +66,7 @@ class MessageBodyLoader @Inject constructor(
|
|||
fetchedMessage,
|
||||
renderDimensionsProvider.getRenderWidth(fragmentActivity),
|
||||
messageBodyCssProvider.getMessageBodyCss(),
|
||||
messageBodyCssProvider.getMessageBodyDarkModeCss(),
|
||||
messageBodyCssProvider.getMessageBodyDarkModeCss(userManager.requireCurrentUserId(), fetchedMessage.messageId!!),
|
||||
getStringResource(R.string.request_timeout)
|
||||
)
|
||||
fetchedMessage.decryptedHTML = messageBody
|
||||
|
|
|
@ -25,7 +25,6 @@ import android.content.ClipboardManager
|
|||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.ContextMenu
|
||||
|
@ -85,6 +84,7 @@ import kotlinx.android.synthetic.main.activity_message_details.*
|
|||
import kotlinx.android.synthetic.main.layout_message_details_activity_toolbar.*
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.util.kotlin.EMPTY_STRING
|
||||
import timber.log.Timber
|
||||
|
@ -196,6 +196,12 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
|
|||
viewModel.handleStarUnStar(messageOrConversationId, isChecked)
|
||||
}
|
||||
|
||||
viewModel.reloadMessageFlow
|
||||
.onEach { messageId ->
|
||||
messageExpandableAdapter.reloadMessage(messageId)
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
lifecycle.addObserver(viewModel)
|
||||
}
|
||||
|
||||
|
@ -251,13 +257,13 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
|
|||
|
||||
val showDecryptionError = messageBodyState is MessageBodyState.Error.DecryptionError
|
||||
val loadedMessage = messageBodyState.message
|
||||
val messageId = loadedMessage.messageId ?: return@mapLatest
|
||||
val parsedBody = viewModel.formatMessageHtmlBody(
|
||||
loadedMessage,
|
||||
renderDimensionsProvider.getRenderWidth(this),
|
||||
AppUtil.readTxt(this, R.raw.css_reset_with_custom_props),
|
||||
if (
|
||||
resources.configuration.uiMode and
|
||||
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
if (viewModel.isAppInDarkMode(this) &&
|
||||
viewModel.isWebViewInDarkModeBlocking(this, messageId)
|
||||
) {
|
||||
AppUtil.readTxt(this, R.raw.css_reset_dark_mode_only)
|
||||
} else {
|
||||
|
@ -266,7 +272,6 @@ internal class MessageDetailsActivity : BaseStoragePermissionActivity() {
|
|||
this.getString(R.string.request_timeout)
|
||||
)
|
||||
|
||||
val messageId = loadedMessage.messageId ?: return@mapLatest
|
||||
val showLoadEmbeddedImagesButton = handleEmbeddedImagesLoading(loadedMessage)
|
||||
messageExpandableAdapter.showMessageDetails(
|
||||
parsedBody,
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
package ch.protonmail.android.navigation.presentation
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
|
@ -284,7 +282,7 @@ internal abstract class NavigationActivity : BaseActivity() {
|
|||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (SHOULD_DRAW_DRAWER_BEHIND_SYSTEM_BARS)
|
||||
if (resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES) {
|
||||
if (navigationViewModel.isAppInDarkMode(this)) {
|
||||
setDarkStatusBar()
|
||||
} else {
|
||||
setLightStatusBar()
|
||||
|
@ -360,7 +358,7 @@ internal abstract class NavigationActivity : BaseActivity() {
|
|||
override fun onDrawerClosed(drawerView: View) {
|
||||
super.onDrawerClosed(drawerView)
|
||||
if (SHOULD_DRAW_DRAWER_BEHIND_SYSTEM_BARS)
|
||||
if (resources.configuration.uiMode and UI_MODE_NIGHT_MASK == UI_MODE_NIGHT_YES) {
|
||||
if (navigationViewModel.isAppInDarkMode(this@NavigationActivity)) {
|
||||
setDarkStatusBar()
|
||||
} else {
|
||||
setLightStatusBar()
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
|
||||
package ch.protonmail.android.navigation.presentation
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.feature.account.AccountStateManager
|
||||
import ch.protonmail.android.prefs.SecureSharedPreferences
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.utils.notifier.UserNotifier
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -34,6 +36,7 @@ import javax.inject.Inject
|
|||
|
||||
@HiltViewModel
|
||||
internal class NavigationViewModel @Inject constructor(
|
||||
private val isAppInDarkMode: IsAppInDarkMode,
|
||||
private val secureSharedPreferencesFactory: SecureSharedPreferences.Factory,
|
||||
private val accountStateManager: AccountStateManager,
|
||||
private val userNotifier: UserNotifier,
|
||||
|
@ -51,4 +54,6 @@ internal class NavigationViewModel @Inject constructor(
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun isAppInDarkMode(context: Context) = isAppInDarkMode.invoke(context)
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import ch.protonmail.android.data.ProtonStore
|
|||
import ch.protonmail.android.data.local.CounterDao
|
||||
import ch.protonmail.android.data.local.MessageDao
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity
|
||||
import ch.protonmail.android.domain.LoadMoreFlow
|
||||
import ch.protonmail.android.jobs.PostReadJob
|
||||
import ch.protonmail.android.jobs.PostStarJob
|
||||
|
@ -387,6 +388,19 @@ class MessageRepository @Inject constructor(
|
|||
messageDao.deleteMessagesByIds(messageIds)
|
||||
}
|
||||
|
||||
suspend fun saveViewInDarkModeMessagePreference(userId: UserId, messageId: String, viewInDarkMode: Boolean) {
|
||||
val messagePreferenceDao = databaseProvider.provideMessagePreferenceDao(userId)
|
||||
val messagePreference = messagePreferenceDao.findMessagePreference(messageId)
|
||||
?.copy(viewInDarkMode = viewInDarkMode)
|
||||
?: MessagePreferenceEntity(messageId = messageId, viewInDarkMode = viewInDarkMode)
|
||||
messagePreferenceDao.saveMessagePreference(messagePreference)
|
||||
}
|
||||
|
||||
suspend fun getViewInDarkModeMessagePreference(userId: UserId, messageId: String): Boolean? {
|
||||
val messagePreferenceDao = databaseProvider.provideMessagePreferenceDao(userId)
|
||||
return messagePreferenceDao.findMessagePreference(messageId)?.viewInDarkMode
|
||||
}
|
||||
|
||||
private suspend fun Message.saveBodyToFileIfNeeded() {
|
||||
withContext(dispatcherProvider.Io) {
|
||||
messageBody = messageBody?.let {
|
||||
|
|
|
@ -35,6 +35,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.activities.messageDetails.EXTRA_VIEW_HEADERS
|
||||
import ch.protonmail.android.activities.messageDetails.MessageViewHeadersActivity
|
||||
import ch.protonmail.android.activities.messageDetails.viewmodel.MessageDetailsViewModel
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.databinding.FragmentMessageActionSheetBinding
|
||||
import ch.protonmail.android.databinding.LayoutMessageDetailsActionsSheetButtonsBinding
|
||||
|
@ -63,6 +64,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
private var actionSheetHeader: ActionSheetHeader? = null
|
||||
private val viewModel: MessageActionSheetViewModel by viewModels()
|
||||
private val mailboxViewModel: MailboxViewModel by activityViewModels()
|
||||
private val messageDetailsViewModel: MessageDetailsViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -89,13 +91,18 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
.onEach { updateViewState(it, binding) }
|
||||
.launchIn(lifecycleScope)
|
||||
|
||||
viewModel.setupViewState(messageIds, messageLocation, mailboxLabelId, actionsTarget)
|
||||
viewModel.setupViewState(
|
||||
requireContext(),
|
||||
messageIds,
|
||||
messageLocation,
|
||||
mailboxLabelId,
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
setupHeaderBindings(binding.actionSheetHeaderDetailsActions, arguments)
|
||||
setupReplyActionsBindings(
|
||||
binding.includeLayoutActionSheetButtons, actionsTarget, messageIds, doesConversationHaveMoreThanOneMessage
|
||||
)
|
||||
setupManageSectionBindings(binding, viewModel, actionsTarget, messageIds, messageLocation, mailboxLabelId)
|
||||
setupMoreSectionBindings(binding, actionsTarget, messageIds)
|
||||
actionSheetHeader = binding.actionSheetHeaderDetailsActions
|
||||
|
||||
|
@ -157,6 +164,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
) {
|
||||
when (state) {
|
||||
is MessageActionSheetState.Data -> {
|
||||
setupManageSectionBindings(binding, state.manageSectionState)
|
||||
setupMoveSectionState(binding, state.moveSectionState)
|
||||
}
|
||||
MessageActionSheetState.Initial -> {
|
||||
|
@ -219,66 +227,66 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
|
||||
private fun setupManageSectionBindings(
|
||||
binding: FragmentMessageActionSheetBinding,
|
||||
viewModel: MessageActionSheetViewModel,
|
||||
actionsTarget: ActionSheetTarget,
|
||||
messageIds: List<String>,
|
||||
messageLocation: Constants.MessageLocationType,
|
||||
mailboxLabelId: String
|
||||
state: MessageActionSheetState.ManageSectionState
|
||||
) {
|
||||
with(binding) {
|
||||
val isStarred = arguments?.getBoolean(EXTRA_ARG_IS_STARED) ?: false
|
||||
|
||||
textViewDetailsActionsUnstar.apply {
|
||||
isVisible = actionsTarget in arrayOf(
|
||||
ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN,
|
||||
ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
) || isStarred
|
||||
isVisible = state.showUnstarAction
|
||||
setOnClickListener {
|
||||
viewModel.unStarMessage(
|
||||
messageIds,
|
||||
messageLocation,
|
||||
actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
state.mailboxItemIds,
|
||||
state.messageLocation,
|
||||
state.actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
textViewDetailsActionsStar.apply {
|
||||
isVisible = actionsTarget in arrayOf(
|
||||
ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN,
|
||||
ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
) || !isStarred
|
||||
isVisible = state.showStarAction
|
||||
setOnClickListener {
|
||||
viewModel.starMessage(
|
||||
messageIds,
|
||||
messageLocation,
|
||||
actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
state.mailboxItemIds,
|
||||
state.messageLocation,
|
||||
state.actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
textViewDetailsActionsMarkRead.apply {
|
||||
isVisible = actionsTarget == ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN
|
||||
isVisible = state.showMarkReadAction
|
||||
setOnClickListener {
|
||||
viewModel.markRead(
|
||||
messageIds,
|
||||
messageLocation,
|
||||
mailboxLabelId,
|
||||
actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
state.mailboxItemIds,
|
||||
state.messageLocation,
|
||||
state.currentLocationId,
|
||||
state.actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
)
|
||||
}
|
||||
}
|
||||
textViewDetailsActionsMarkUnread.setOnClickListener {
|
||||
viewModel.markUnread(
|
||||
messageIds,
|
||||
messageLocation,
|
||||
mailboxLabelId,
|
||||
actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
state.mailboxItemIds,
|
||||
state.messageLocation,
|
||||
state.currentLocationId,
|
||||
state.actionsTarget == ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
)
|
||||
}
|
||||
textViewDetailsActionsLabelAs.setOnClickListener {
|
||||
viewModel.showLabelsManager(messageIds, messageLocation, mailboxLabelId)
|
||||
viewModel.showLabelsManager(state.mailboxItemIds, state.messageLocation, state.currentLocationId)
|
||||
dismiss()
|
||||
}
|
||||
detailsActionsViewInLightModeTextView.apply {
|
||||
isVisible = state.showViewInLightModeAction
|
||||
setOnClickListener {
|
||||
viewModel.viewInLightMode(state.mailboxItemIds.first())
|
||||
}
|
||||
}
|
||||
detailsActionsViewInDarkModeTextView.apply {
|
||||
isVisible = state.showViewInDarkModeAction
|
||||
setOnClickListener {
|
||||
viewModel.viewInDarkMode(state.mailboxItemIds.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,6 +423,10 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
}
|
||||
is MessageActionSheetAction.CouldNotCompleteActionError ->
|
||||
showCouldNotCompleteActionError()
|
||||
is MessageActionSheetAction.ViewMessageInLightDarkMode -> {
|
||||
messageDetailsViewModel.reloadMessage(sheetAction.messageId)
|
||||
handleDismissBehavior(false)
|
||||
}
|
||||
else -> Timber.v("unhandled action $sheetAction")
|
||||
}
|
||||
}
|
||||
|
@ -473,7 +485,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
private const val EXTRA_ARG_MAILBOX_LABEL_ID = "arg_mailbox_label_id"
|
||||
private const val EXTRA_ARG_TITLE = "arg_message_details_actions_title"
|
||||
private const val EXTRA_ARG_SUBTITLE = "arg_message_details_actions_sub_title"
|
||||
private const val EXTRA_ARG_IS_STARED = "arg_extra_is_stared"
|
||||
const val EXTRA_ARG_IS_STARRED = "arg_extra_is_stared"
|
||||
private const val EXTRA_ARG_CONVERSATION_HAS_MORE_THAN_ONE_MESSAGE =
|
||||
"arg_conversation_has_more_than_one_message"
|
||||
private const val HEADER_SLIDE_THRESHOLD = 0.8f
|
||||
|
@ -505,7 +517,7 @@ class MessageActionSheet : BottomSheetDialogFragment() {
|
|||
EXTRA_ARG_MESSAGE_IDS to messagesIds.toTypedArray(),
|
||||
EXTRA_ARG_TITLE to title,
|
||||
EXTRA_ARG_SUBTITLE to subTitle,
|
||||
EXTRA_ARG_IS_STARED to isStarred,
|
||||
EXTRA_ARG_IS_STARRED to isStarred,
|
||||
EXTRA_ARG_CURRENT_FOLDER_LOCATION_ID to currentFolderLocationId,
|
||||
EXTRA_ARG_MAILBOX_LABEL_ID to mailboxLabelId,
|
||||
EXTRA_ARG_ACTION_TARGET to actionSheetTarget,
|
||||
|
|
|
@ -49,5 +49,9 @@ sealed class MessageActionSheetAction {
|
|||
val areMailboxItemsMovedFromLocation: Boolean
|
||||
) : MessageActionSheetAction()
|
||||
|
||||
data class ViewMessageInLightDarkMode(
|
||||
val messageId: String
|
||||
) : MessageActionSheetAction()
|
||||
|
||||
object CouldNotCompleteActionError : MessageActionSheetAction()
|
||||
}
|
||||
|
|
|
@ -26,9 +26,22 @@ sealed class MessageActionSheetState {
|
|||
object Initial : MessageActionSheetState()
|
||||
|
||||
data class Data(
|
||||
val manageSectionState: ManageSectionState,
|
||||
val moveSectionState: MoveSectionState
|
||||
) : MessageActionSheetState()
|
||||
|
||||
data class ManageSectionState(
|
||||
val mailboxItemIds: List<String>,
|
||||
val messageLocation: Constants.MessageLocationType,
|
||||
val currentLocationId: String,
|
||||
val actionsTarget: ActionSheetTarget,
|
||||
val showStarAction: Boolean,
|
||||
val showUnstarAction: Boolean,
|
||||
val showMarkReadAction: Boolean,
|
||||
val showViewInLightModeAction: Boolean,
|
||||
val showViewInDarkModeAction: Boolean
|
||||
)
|
||||
|
||||
data class MoveSectionState(
|
||||
val mailboxItemIds: List<String>,
|
||||
val messageLocation: Constants.MessageLocationType,
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package ch.protonmail.android.ui.actionsheet
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -33,9 +34,12 @@ import ch.protonmail.android.mailbox.domain.model.ConversationsActionResult
|
|||
import ch.protonmail.android.mailbox.presentation.ConversationModeEnabled
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import ch.protonmail.android.ui.actionsheet.model.ActionSheetTarget
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.usecase.delete.DeleteMessage
|
||||
import ch.protonmail.android.usecase.message.ChangeMessagesReadStatus
|
||||
import ch.protonmail.android.usecase.message.ChangeMessagesStarredStatus
|
||||
import ch.protonmail.android.details.domain.usecase.GetViewInDarkModeMessagePreference
|
||||
import ch.protonmail.android.details.domain.usecase.SetViewInDarkModeMessagePreference
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -60,6 +64,9 @@ internal class MessageActionSheetViewModel @Inject constructor(
|
|||
private val changeMessagesStarredStatus: ChangeMessagesStarredStatus,
|
||||
private val changeConversationsStarredStatus: ChangeConversationsStarredStatus,
|
||||
private val conversationModeEnabled: ConversationModeEnabled,
|
||||
private val isAppInDarkMode: IsAppInDarkMode,
|
||||
private val getViewInDarkModeMessagePreference: GetViewInDarkModeMessagePreference,
|
||||
private val setViewInDarkModeMessagePreference: SetViewInDarkModeMessagePreference,
|
||||
private val accountManager: AccountManager
|
||||
) : ViewModel() {
|
||||
|
||||
|
@ -73,13 +80,28 @@ internal class MessageActionSheetViewModel @Inject constructor(
|
|||
get() = actionsMutableFlow
|
||||
|
||||
fun setupViewState(
|
||||
context: Context,
|
||||
messageIds: List<String>,
|
||||
messageLocation: Constants.MessageLocationType,
|
||||
mailboxLocationId: String,
|
||||
actionsTarget: ActionSheetTarget
|
||||
) {
|
||||
val moveSectionState = computeMoveSectionState(actionsTarget, messageLocation, mailboxLocationId, messageIds)
|
||||
mutableStateFlow.value = MessageActionSheetState.Data(moveSectionState)
|
||||
viewModelScope.launch {
|
||||
val manageSectionState = computeManageSectionState(
|
||||
context,
|
||||
messageLocation,
|
||||
mailboxLocationId,
|
||||
messageIds,
|
||||
actionsTarget
|
||||
)
|
||||
val moveSectionState = computeMoveSectionState(
|
||||
actionsTarget,
|
||||
messageLocation,
|
||||
mailboxLocationId,
|
||||
messageIds
|
||||
)
|
||||
mutableStateFlow.value = MessageActionSheetState.Data(manageSectionState, moveSectionState)
|
||||
}
|
||||
}
|
||||
|
||||
fun showLabelsManager(
|
||||
|
@ -339,6 +361,30 @@ internal class MessageActionSheetViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun viewInLightMode(messageId: String) {
|
||||
viewModelScope.launch {
|
||||
accountManager.getPrimaryUserId().first()?.let {
|
||||
setViewInDarkModeMessagePreference(it, messageId, viewInDarkMode = false)
|
||||
}
|
||||
}.invokeOnCompletion {
|
||||
actionsMutableFlow.value = MessageActionSheetAction.ViewMessageInLightDarkMode(
|
||||
messageId = messageId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun viewInDarkMode(messageId: String) {
|
||||
viewModelScope.launch {
|
||||
accountManager.getPrimaryUserId().first()?.let {
|
||||
setViewInDarkModeMessagePreference(it, messageId, viewInDarkMode = true)
|
||||
}
|
||||
}.invokeOnCompletion {
|
||||
actionsMutableFlow.value = MessageActionSheetAction.ViewMessageInLightDarkMode(
|
||||
messageId = messageId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun moveMessagesToFolderAndDismiss(
|
||||
ids: List<String>,
|
||||
newFolderLocationId: Constants.MessageLocationType,
|
||||
|
@ -416,6 +462,59 @@ internal class MessageActionSheetViewModel @Inject constructor(
|
|||
MessageActionSheet.EXTRA_ARG_ACTION_TARGET
|
||||
) ?: ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
|
||||
private fun isAppInDarkMode(context: Context) = isAppInDarkMode.invoke(context)
|
||||
|
||||
private suspend fun isWebViewInDarkMode(context: Context, messageId: String): Boolean {
|
||||
accountManager.getPrimaryUserId().first()?.let {
|
||||
return getViewInDarkModeMessagePreference(context, it, messageId)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private suspend fun computeManageSectionState(
|
||||
context: Context,
|
||||
messageLocation: Constants.MessageLocationType,
|
||||
mailboxLocationId: String,
|
||||
messageIds: List<String>,
|
||||
actionsTarget: ActionSheetTarget
|
||||
): MessageActionSheetState.ManageSectionState {
|
||||
val isStarred = savedStateHandle.get<Boolean>(MessageActionSheet.EXTRA_ARG_IS_STARRED) ?: false
|
||||
|
||||
val showStarAction = actionsTarget in arrayOf(
|
||||
ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN,
|
||||
ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
) || !isStarred
|
||||
|
||||
val showUnstarAction = actionsTarget in arrayOf(
|
||||
ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN,
|
||||
ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
) || isStarred
|
||||
|
||||
val showMarkReadAction = actionsTarget == ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN
|
||||
|
||||
val showViewInLightModeAction = actionsTarget in arrayOf(
|
||||
ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN,
|
||||
ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
) && isAppInDarkMode(context) && isWebViewInDarkMode(context, messageIds.first())
|
||||
|
||||
val showViewInDarkModeAction = actionsTarget in arrayOf(
|
||||
ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN,
|
||||
ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
) && isAppInDarkMode(context) && !isWebViewInDarkMode(context, messageIds.first())
|
||||
|
||||
return MessageActionSheetState.ManageSectionState(
|
||||
messageIds,
|
||||
messageLocation,
|
||||
mailboxLocationId,
|
||||
actionsTarget,
|
||||
showStarAction,
|
||||
showUnstarAction,
|
||||
showMarkReadAction,
|
||||
showViewInLightModeAction,
|
||||
showViewInDarkModeAction
|
||||
)
|
||||
}
|
||||
|
||||
private fun computeMoveSectionState(
|
||||
actionsTarget: ActionSheetTarget,
|
||||
messageLocation: Constants.MessageLocationType,
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.usecase
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A use case that checks whether the app is in dark mode or not
|
||||
*/
|
||||
class IsAppInDarkMode @Inject constructor() {
|
||||
|
||||
operator fun invoke(context: Context): Boolean =
|
||||
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
|
||||
}
|
|
@ -20,26 +20,26 @@
|
|||
package ch.protonmail.android.utils.css
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.utils.AppUtil
|
||||
import ch.protonmail.android.details.domain.usecase.GetViewInDarkModeMessagePreference
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.util.kotlin.EMPTY_STRING
|
||||
import javax.inject.Inject
|
||||
|
||||
class MessageBodyCssProvider @Inject constructor(
|
||||
private val context: Context
|
||||
private val context: Context,
|
||||
private val isAppInDarkMode: IsAppInDarkMode,
|
||||
private val getViewInDarkModeMessagePreference: GetViewInDarkModeMessagePreference
|
||||
) {
|
||||
|
||||
fun getMessageBodyCss(): String {
|
||||
return AppUtil.readTxt(context, R.raw.css_reset_with_custom_props)
|
||||
}
|
||||
fun getMessageBodyCss(): String = AppUtil.readTxt(context, R.raw.css_reset_with_custom_props)
|
||||
|
||||
fun getMessageBodyDarkModeCss(): String {
|
||||
return if (context.resources.configuration.uiMode
|
||||
and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES) {
|
||||
suspend fun getMessageBodyDarkModeCss(userId: UserId, messageId: String): String =
|
||||
if (isAppInDarkMode(context) && getViewInDarkModeMessagePreference(context, userId, messageId)) {
|
||||
AppUtil.readTxt(context, R.raw.css_reset_dark_mode_only)
|
||||
} else {
|
||||
EMPTY_STRING
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,15 +24,25 @@ import android.content.res.Configuration
|
|||
import android.webkit.WebView
|
||||
import androidx.webkit.WebSettingsCompat
|
||||
import androidx.webkit.WebViewFeature
|
||||
import ch.protonmail.android.details.domain.usecase.GetViewInDarkModeMessagePreference
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetUpWebViewDarkModeHandlingIfSupported @Inject constructor() {
|
||||
class SetUpWebViewDarkModeHandlingIfSupported @Inject constructor(
|
||||
private val getViewInDarkModeMessagePreference: GetViewInDarkModeMessagePreference
|
||||
) {
|
||||
|
||||
operator fun invoke(context: Context, webView: WebView) {
|
||||
suspend operator fun invoke(context: Context, userId: UserId, webView: WebView, messageId: String) {
|
||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
|
||||
val viewInDarkModeMessagePreference = getViewInDarkModeMessagePreference(context, userId, messageId)
|
||||
|
||||
when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||
Configuration.UI_MODE_NIGHT_YES -> {
|
||||
WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
|
||||
if (viewInDarkModeMessagePreference) {
|
||||
WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
|
||||
} else {
|
||||
WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_OFF)
|
||||
}
|
||||
}
|
||||
Configuration.UI_MODE_NIGHT_NO, Configuration.UI_MODE_NIGHT_UNDEFINED -> {
|
||||
WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_OFF)
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
~ Copyright (c) 2020 Proton Technologies AG
|
||||
~
|
||||
~ This file is part of ProtonMail.
|
||||
~
|
||||
~ ProtonMail 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.
|
||||
~
|
||||
~ ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M11.1487,4C11.1454,4 11.142,4 11.1387,4C8.8955,4 7.0771,5.7909 7.0771,8C7.0771,10.2091 8.8955,12 11.1387,12C11.142,12 11.1454,12 11.1487,12C11.8158,11.9984 12.4452,11.8384 13,11.5562C12.5811,12.1159 12.0658,12.6014 11.4783,12.9889C10.51,13.6275 9.3454,14 8.0925,14C4.7277,14 2,11.3137 2,8C2,4.6863 4.7277,2 8.0925,2C9.3454,2 10.51,2.3725 11.4783,3.0111C12.0658,3.3986 12.5811,3.8841 13,4.4438C12.4452,4.1616 11.8158,4.0016 11.1487,4ZM9.6212,3.229C9.1389,3.0803 8.6255,3 8.0925,3C5.2655,3 3,5.2529 3,8C3,10.7471 5.2655,13 8.0925,13C8.6255,13 9.1389,12.9198 9.6212,12.771C7.574,12.1357 6.0771,10.2488 6.0771,8C6.0771,5.7512 7.574,3.8643 9.6212,3.229Z"
|
||||
android:fillColor="@color/icon_norm"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
~ Copyright (c) 2020 Proton Technologies AG
|
||||
~
|
||||
~ This file is part of ProtonMail.
|
||||
~
|
||||
~ ProtonMail 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.
|
||||
~
|
||||
~ ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:pathData="M7.5,1H8.5V3H7.5V1ZM7.5,15V13H8.5V15H7.5ZM13.3033,3.4038L12.5962,2.6967L11.182,4.1109L11.8891,4.818L13.3033,3.4038ZM4.1109,11.182L4.818,11.8891L3.4038,13.3033L2.6967,12.5962L4.1109,11.182ZM12.5962,13.3033L13.3033,12.5962L11.8891,11.182L11.182,11.8891L12.5962,13.3033ZM4.818,4.1109L4.1109,4.818L2.6967,3.4038L3.4038,2.6967L4.818,4.1109ZM3,8.5V7.5H1V8.5H3ZM13,7.5H15V8.5H13V7.5ZM11,8C11,9.6568 9.6568,11 8,11C6.3432,11 5,9.6568 5,8C5,6.3432 6.3432,5 8,5C9.6568,5 11,6.3432 11,8ZM12,8C12,10.2091 10.2091,12 8,12C5.7909,12 4,10.2091 4,8C4,5.7909 5.7909,4 8,4C10.2091,4 12,5.7909 12,8Z"
|
||||
android:fillColor="@color/icon_norm"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
|
@ -104,6 +104,26 @@
|
|||
android:text="@string/label_as"
|
||||
app:drawableStartCompat="@drawable/ic_label" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/details_actions_view_in_light_mode_text_view"
|
||||
style="@style/ListStandardItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/action_sheet_view_in_light_mode"
|
||||
android:visibility="gone"
|
||||
app:drawableStartCompat="@drawable/ic_sun"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/details_actions_view_in_dark_mode_text_view"
|
||||
style="@style/ListStandardItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/action_sheet_view_in_dark_mode"
|
||||
android:visibility="gone"
|
||||
app:drawableStartCompat="@drawable/ic_moon"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<View style="@style/ViewSeparatorHorizontal" />
|
||||
|
||||
<TextView
|
||||
|
|
|
@ -918,6 +918,8 @@
|
|||
<string name="manage">Manage</string>
|
||||
<string name="message_from">Message from %s</string>
|
||||
<string name="not_spam_move_to_inbox">Not spam (Move to Inbox)</string>
|
||||
<string name="action_sheet_view_in_light_mode">View in Light mode</string>
|
||||
<string name="action_sheet_view_in_dark_mode">View in Dark mode</string>
|
||||
<plurals name="x_messages_count">
|
||||
<item quantity="one">%d message</item>
|
||||
<item quantity="other">%d messages</item>
|
||||
|
|
|
@ -61,6 +61,7 @@ import ch.protonmail.android.mailbox.presentation.ConversationModeEnabled
|
|||
import ch.protonmail.android.repository.MessageRepository
|
||||
import ch.protonmail.android.testAndroid.lifecycle.testObserver
|
||||
import ch.protonmail.android.ui.model.LabelChipUiModel
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.usecase.delete.DeleteMessage
|
||||
import ch.protonmail.android.usecase.fetch.FetchVerificationKeys
|
||||
|
@ -68,6 +69,7 @@ import ch.protonmail.android.usecase.message.ChangeMessagesReadStatus
|
|||
import ch.protonmail.android.usecase.message.ChangeMessagesStarredStatus
|
||||
import ch.protonmail.android.util.ProtonCalendarUtil
|
||||
import ch.protonmail.android.utils.DownloadUtils
|
||||
import ch.protonmail.android.details.domain.usecase.GetViewInDarkModeMessagePreference
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
|
@ -111,6 +113,10 @@ private const val MESSAGE_SENDER_EMAIL_ADDRESS = "sender@protonmail.com"
|
|||
|
||||
class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
||||
|
||||
private val isAppInDarkMode: IsAppInDarkMode = mockk()
|
||||
|
||||
private val getViewInDarkModeMessagePreference: GetViewInDarkModeMessagePreference = mockk()
|
||||
|
||||
private val changeMessagesReadStatus: ChangeMessagesReadStatus = mockk()
|
||||
|
||||
private val changeConversationsReadStatus: ChangeConversationsReadStatus = mockk(relaxed = true)
|
||||
|
@ -207,6 +213,8 @@ class MessageDetailsViewModelTest : ArchTest, CoroutinesTest {
|
|||
mockkStatic(Color::class)
|
||||
every { Color.parseColor(any()) } returns testColorInt
|
||||
viewModel = MessageDetailsViewModel(
|
||||
isAppInDarkMode = isAppInDarkMode,
|
||||
getViewInDarkModeMessagePreference = getViewInDarkModeMessagePreference,
|
||||
messageDetailsRepository = messageDetailsRepository,
|
||||
messageRepository = messageRepository,
|
||||
userManager = userManager,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package ch.protonmail.android.compose
|
||||
|
||||
import androidx.work.WorkManager
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.activities.messageDetails.repository.MessageDetailsRepository
|
||||
import ch.protonmail.android.api.NetworkConfigurator
|
||||
|
@ -31,6 +30,7 @@ import ch.protonmail.android.core.UserManager
|
|||
import ch.protonmail.android.data.local.model.Message
|
||||
import ch.protonmail.android.testAndroid.lifecycle.testObserver
|
||||
import ch.protonmail.android.testAndroid.rx.TrampolineScheduler
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.usecase.VerifyConnection
|
||||
import ch.protonmail.android.usecase.compose.SaveDraft
|
||||
import ch.protonmail.android.usecase.compose.SaveDraftResult
|
||||
|
@ -38,6 +38,7 @@ import ch.protonmail.android.usecase.delete.DeleteMessage
|
|||
import ch.protonmail.android.usecase.fetch.FetchPublicKeys
|
||||
import ch.protonmail.android.utils.UiUtil
|
||||
import ch.protonmail.android.utils.resources.StringResourceResolver
|
||||
import ch.protonmail.android.utils.webview.SetUpWebViewDarkModeHandlingIfSupported
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
|
@ -62,6 +63,8 @@ class ComposeMessageViewModelTest : ArchTest, CoroutinesTest {
|
|||
@get:Rule
|
||||
val trampolineSchedulerRule = TrampolineScheduler()
|
||||
|
||||
private val isAppInDarkMode: IsAppInDarkMode = mockk()
|
||||
|
||||
private val stringResourceResolver: StringResourceResolver = mockk(relaxed = true)
|
||||
|
||||
private val composeMessageRepository: ComposeMessageRepository = mockk(relaxed = true)
|
||||
|
@ -89,13 +92,14 @@ class ComposeMessageViewModelTest : ArchTest, CoroutinesTest {
|
|||
|
||||
private val verifyConnection: VerifyConnection = mockk()
|
||||
|
||||
private val workManager: WorkManager = mockk(relaxed = true)
|
||||
|
||||
private val htmlToSpanned: HtmlToSpanned = mockk(relaxed = true)
|
||||
|
||||
private val addExpirationTimeToMessage: AddExpirationTimeToMessage = mockk()
|
||||
|
||||
private val setUpWebViewDarkModeHandlingIfSupported: SetUpWebViewDarkModeHandlingIfSupported = mockk()
|
||||
|
||||
private val viewModel = ComposeMessageViewModel(
|
||||
isAppInDarkMode = isAppInDarkMode,
|
||||
composeMessageRepository = composeMessageRepository,
|
||||
userManager = userManager,
|
||||
accountManager = accountManager,
|
||||
|
@ -109,7 +113,8 @@ class ComposeMessageViewModelTest : ArchTest, CoroutinesTest {
|
|||
verifyConnection = verifyConnection,
|
||||
networkConfigurator = networkConfigurator,
|
||||
htmlToSpanned = htmlToSpanned,
|
||||
addExpirationTimeToMessage = addExpirationTimeToMessage
|
||||
addExpirationTimeToMessage = addExpirationTimeToMessage,
|
||||
setUpWebViewDarkModeHandlingIfSupported = setUpWebViewDarkModeHandlingIfSupported
|
||||
)
|
||||
|
||||
@BeforeTest
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail 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.
|
||||
*
|
||||
* ProtonMail 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 ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.details.domain.usecase
|
||||
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* Tests the behaviour of [SetViewInDarkModeMessagePreference]
|
||||
*/
|
||||
class SetViewInDarkModeMessagePreferenceTest {
|
||||
|
||||
private val messageRepository: MessageRepository = mockk {
|
||||
coEvery { saveViewInDarkModeMessagePreference(any(), any(), any()) } just runs
|
||||
}
|
||||
|
||||
private val setViewInDarkModeMessagePreference = SetViewInDarkModeMessagePreference(
|
||||
messageRepository
|
||||
)
|
||||
|
||||
private val testUserId = UserId("testUserId")
|
||||
private val testMessageId = "messageId"
|
||||
|
||||
@Test
|
||||
fun `should call repository method for saving the view in dark mode message preference`() = runBlockingTest {
|
||||
// when
|
||||
setViewInDarkModeMessagePreference(testUserId, testMessageId, viewInDarkMode = true)
|
||||
|
||||
// then
|
||||
coVerify {
|
||||
messageRepository.saveViewInDarkModeMessagePreference(testUserId, testMessageId, viewInDarkMode = true)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -24,8 +24,6 @@ import ch.protonmail.android.R
|
|||
import ch.protonmail.android.core.UserManager
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
import ch.protonmail.android.details.domain.MessageBodyDecryptor
|
||||
import ch.protonmail.android.details.domain.MessageBodyParser
|
||||
import ch.protonmail.android.details.domain.model.MessageBodyParts
|
||||
import ch.protonmail.android.details.presentation.mapper.MessageToMessageDetailsListItemMapper
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import ch.protonmail.android.testdata.KeyInformationTestData
|
||||
|
@ -57,7 +55,7 @@ class MessageBodyLoaderTest {
|
|||
}
|
||||
private val messageBodyCssProviderMock = mockk<MessageBodyCssProvider> {
|
||||
every { getMessageBodyCss() } returns TestData.MESSAGE_BODY_CSS
|
||||
every { getMessageBodyDarkModeCss() } returns TestData.MESSAGE_BODY_DARK_MODE_CSS
|
||||
coEvery { getMessageBodyDarkModeCss(any(), any()) } returns TestData.MESSAGE_BODY_DARK_MODE_CSS
|
||||
}
|
||||
private val getStringResourceMock = mockk<StringResourceResolver> {
|
||||
every { this@mockk.invoke(R.string.request_timeout) } returns TestData.DEFAULT_ERROR_MESSAGE
|
||||
|
|
|
@ -25,6 +25,7 @@ import ch.protonmail.android.core.Constants
|
|||
import ch.protonmail.android.feature.account.AccountStateManager
|
||||
import ch.protonmail.android.prefs.SecureSharedPreferences
|
||||
import ch.protonmail.android.testdata.UserIdTestData
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.utils.notifier.UserNotifier
|
||||
import io.mockk.called
|
||||
import io.mockk.every
|
||||
|
@ -41,6 +42,8 @@ import kotlin.test.assertTrue
|
|||
|
||||
class NavigationViewModelTest : ArchTest, CoroutinesTest {
|
||||
|
||||
private val isAppInDarkMode: IsAppInDarkMode = mockk()
|
||||
|
||||
private val sharedPrefsMock = mockk<SharedPreferences>()
|
||||
private val sharedPreferencesFactoryMock = mockk<SecureSharedPreferences.Factory> {
|
||||
every { userPreferences(UserIdTestData.userId) } returns sharedPrefsMock
|
||||
|
@ -52,6 +55,7 @@ class NavigationViewModelTest : ArchTest, CoroutinesTest {
|
|||
every { showError(R.string.logged_out_description) } just runs
|
||||
}
|
||||
private val navigationViewModel = NavigationViewModel(
|
||||
isAppInDarkMode,
|
||||
sharedPreferencesFactoryMock,
|
||||
accountStateManagerMock,
|
||||
userNotifierMock,
|
||||
|
|
|
@ -30,7 +30,9 @@ import ch.protonmail.android.core.NetworkConnectivityManager
|
|||
import ch.protonmail.android.core.UserManager
|
||||
import ch.protonmail.android.data.local.CounterDao
|
||||
import ch.protonmail.android.data.local.MessageDao
|
||||
import ch.protonmail.android.data.local.MessagePreferenceDao
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
import ch.protonmail.android.data.local.model.MessagePreferenceEntity
|
||||
import ch.protonmail.android.domain.entity.user.User
|
||||
import ch.protonmail.android.labels.domain.LabelRepository
|
||||
import ch.protonmail.android.labels.domain.model.Label
|
||||
|
@ -93,10 +95,15 @@ class MessageRepositoryTest {
|
|||
coEvery { insertUnreadLocation(any()) } just runs
|
||||
}
|
||||
|
||||
private val messagePreferenceDao: MessagePreferenceDao = mockk {
|
||||
coEvery { saveMessagePreference(any()) } returns 123
|
||||
}
|
||||
|
||||
private val databaseProvider: DatabaseProvider = mockk {
|
||||
every { provideMessageDao(any()) } returns messageDao
|
||||
every { provideUnreadCounterDao(any()) } returns unreadCounterDao
|
||||
every { provideCounterDao(any()) } returns counterDao
|
||||
every { provideMessageDao(any()) } returns messageDao
|
||||
every { provideMessagePreferenceDao(any()) } returns messagePreferenceDao
|
||||
every { provideUnreadCounterDao(any()) } returns unreadCounterDao
|
||||
}
|
||||
|
||||
private val messageBodyFileManager: MessageBodyFileManager = mockk()
|
||||
|
@ -832,6 +839,37 @@ class MessageRepositoryTest {
|
|||
assertEquals(expectedLabelIds, savedMessageCaptor.captured.allLabelIDs)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should save message preference if it does not exist in DB when saving view in dark mode preference`() = runBlockingTest {
|
||||
// given
|
||||
val expectedResult = buildMessagePreference(viewInDarkMode = true)
|
||||
coEvery { messagePreferenceDao.findMessagePreference(messageId) } returns null
|
||||
|
||||
// when
|
||||
messageRepository.saveViewInDarkModeMessagePreference(testUserId, messageId, viewInDarkMode = true)
|
||||
|
||||
// then
|
||||
val result = slot<MessagePreferenceEntity>()
|
||||
coVerify { messagePreferenceDao.saveMessagePreference(capture(result)) }
|
||||
assertEquals(expectedResult, result.captured)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should update message preference if it already exists in DB when saving view in dark mode preference`() = runBlockingTest {
|
||||
// given
|
||||
val messagePreference = buildMessagePreference(viewInDarkMode = true)
|
||||
val expectedResult = buildMessagePreference(viewInDarkMode = false)
|
||||
coEvery { messagePreferenceDao.findMessagePreference(messageId) } returns messagePreference
|
||||
|
||||
// when
|
||||
messageRepository.saveViewInDarkModeMessagePreference(testUserId, messageId, viewInDarkMode = false)
|
||||
|
||||
// then
|
||||
val result = slot<MessagePreferenceEntity>()
|
||||
coVerify { messagePreferenceDao.saveMessagePreference(capture(result)) }
|
||||
assertEquals(expectedResult, result.captured)
|
||||
}
|
||||
|
||||
private fun setupUnreadCounterDaoToSimulateReplace() {
|
||||
|
||||
val counters = MutableStateFlow(emptyList<UnreadCounterEntity>())
|
||||
|
@ -872,4 +910,14 @@ class MessageRepositoryTest {
|
|||
path = path,
|
||||
parentId = parentId
|
||||
)
|
||||
|
||||
private fun buildMessagePreference(
|
||||
id: Long = 0,
|
||||
messageId: String = this.messageId,
|
||||
viewInDarkMode: Boolean = true
|
||||
) = MessagePreferenceEntity(
|
||||
id = id,
|
||||
messageId = messageId,
|
||||
viewInDarkMode = viewInDarkMode
|
||||
)
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package ch.protonmail.android.ui.dialog
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
|
@ -31,13 +32,17 @@ import ch.protonmail.android.mailbox.domain.MoveConversationsToFolder
|
|||
import ch.protonmail.android.mailbox.domain.model.ConversationsActionResult
|
||||
import ch.protonmail.android.mailbox.presentation.ConversationModeEnabled
|
||||
import ch.protonmail.android.repository.MessageRepository
|
||||
import ch.protonmail.android.ui.actionsheet.MessageActionSheet.Companion.EXTRA_ARG_IS_STARRED
|
||||
import ch.protonmail.android.ui.actionsheet.MessageActionSheetAction
|
||||
import ch.protonmail.android.ui.actionsheet.MessageActionSheetState
|
||||
import ch.protonmail.android.ui.actionsheet.MessageActionSheetViewModel
|
||||
import ch.protonmail.android.ui.actionsheet.model.ActionSheetTarget
|
||||
import ch.protonmail.android.usecase.IsAppInDarkMode
|
||||
import ch.protonmail.android.usecase.delete.DeleteMessage
|
||||
import ch.protonmail.android.usecase.message.ChangeMessagesReadStatus
|
||||
import ch.protonmail.android.usecase.message.ChangeMessagesStarredStatus
|
||||
import ch.protonmail.android.details.domain.usecase.GetViewInDarkModeMessagePreference
|
||||
import ch.protonmail.android.details.domain.usecase.SetViewInDarkModeMessagePreference
|
||||
import io.mockk.Called
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
|
@ -47,6 +52,7 @@ import io.mockk.every
|
|||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
|
@ -90,12 +96,21 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
@MockK
|
||||
private lateinit var deleteConversations: DeleteConversations
|
||||
|
||||
private val isAppInDarkMode: IsAppInDarkMode = mockk()
|
||||
|
||||
private val getViewInDarkModeMessagePreference: GetViewInDarkModeMessagePreference = mockk()
|
||||
|
||||
@MockK
|
||||
private lateinit var setViewInDarkModeMessagePreference: SetViewInDarkModeMessagePreference
|
||||
|
||||
@MockK
|
||||
private lateinit var accountManager: AccountManager
|
||||
|
||||
@MockK
|
||||
private lateinit var savedStateHandle: SavedStateHandle
|
||||
|
||||
private val context: Context = mockk()
|
||||
|
||||
private lateinit var viewModel: MessageActionSheetViewModel
|
||||
|
||||
private val testUserId = UserId("testUser")
|
||||
|
@ -116,6 +131,9 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
changeMessagesStarredStatus,
|
||||
changeConversationsStarredStatus,
|
||||
conversationModeEnabled,
|
||||
isAppInDarkMode,
|
||||
getViewInDarkModeMessagePreference,
|
||||
setViewInDarkModeMessagePreference,
|
||||
accountManager
|
||||
)
|
||||
}
|
||||
|
@ -611,14 +629,51 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify the use case for saving message preference is called when view in light mode action is clicked`() = runBlockingTest {
|
||||
// given
|
||||
val messageId = "messageId"
|
||||
val expectedActionsFlowValue = MessageActionSheetAction.ViewMessageInLightDarkMode(messageId)
|
||||
coEvery { setViewInDarkModeMessagePreference(testUserId, messageId, viewInDarkMode = false) } just runs
|
||||
|
||||
// when
|
||||
viewModel.viewInLightMode(messageId)
|
||||
|
||||
// then
|
||||
coVerify { setViewInDarkModeMessagePreference(testUserId, messageId, viewInDarkMode = false) }
|
||||
assertEquals(expectedActionsFlowValue, viewModel.actionsFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify the use case for saving message preference is called when view in dark mode action is clicked`() = runBlockingTest {
|
||||
// given
|
||||
val messageId = "messageId"
|
||||
val expectedActionsFlowValue = MessageActionSheetAction.ViewMessageInLightDarkMode(messageId)
|
||||
coEvery { setViewInDarkModeMessagePreference(testUserId, messageId, viewInDarkMode = true) } just runs
|
||||
|
||||
// when
|
||||
viewModel.viewInDarkMode(messageId)
|
||||
|
||||
// then
|
||||
coVerify { setViewInDarkModeMessagePreference(testUserId, messageId, viewInDarkMode = true) }
|
||||
assertEquals(expectedActionsFlowValue, viewModel.actionsFlow.value)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySetupViewStateReturnsMoveSectionStateWithShowMoveToInboxActionTrueWhenActionTargetIsMessageAndMessageLocationIsTrash() {
|
||||
val messageIds = listOf("messageId7")
|
||||
val currentFolder = Constants.MessageLocationType.TRASH
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -632,7 +687,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = false,
|
||||
showDeleteAction = true
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -640,9 +695,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId7")
|
||||
val currentFolder = Constants.MessageLocationType.SENT
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -656,7 +718,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = false,
|
||||
showDeleteAction = true
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -664,9 +726,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId8")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.CONVERSATION_ITEM_IN_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -680,7 +749,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = true,
|
||||
showDeleteAction = false
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -688,9 +757,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId8")
|
||||
val currentFolder = Constants.MessageLocationType.ARCHIVE
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -704,7 +780,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = true,
|
||||
showDeleteAction = false
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -712,9 +788,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId10")
|
||||
val currentFolder = Constants.MessageLocationType.TRASH
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -728,7 +811,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = false,
|
||||
showDeleteAction = true
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -736,9 +819,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId11")
|
||||
val currentFolder = Constants.MessageLocationType.LABEL
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -752,7 +842,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = true,
|
||||
showDeleteAction = false
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -760,9 +850,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId12")
|
||||
val currentFolder = Constants.MessageLocationType.SPAM
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -776,7 +873,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = false,
|
||||
showDeleteAction = true
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -784,9 +881,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId13")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -800,7 +904,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = true,
|
||||
showDeleteAction = false
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -808,9 +912,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId14")
|
||||
val currentFolder = Constants.MessageLocationType.DRAFT
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -824,7 +935,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = false,
|
||||
showDeleteAction = true
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -832,9 +943,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId15")
|
||||
val currentFolder = Constants.MessageLocationType.DRAFT
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -848,7 +966,7 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = false,
|
||||
showDeleteAction = true
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -856,9 +974,16 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
val messageIds = listOf("messageId16")
|
||||
val currentFolder = Constants.MessageLocationType.ARCHIVE
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_WITHIN_CONVERSATION_DETAIL_SCREEN
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
viewModel.setupViewState(
|
||||
messageIds, currentFolder, currentFolder.messageLocationTypeValue.toString(), actionsTarget
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.messageLocationTypeValue.toString(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
val expected = MessageActionSheetState.MoveSectionState(
|
||||
|
@ -872,7 +997,176 @@ class MessageActionSheetViewModelTest : ArchTest, CoroutinesTest {
|
|||
showMoveToSpamAction = true,
|
||||
showDeleteAction = false
|
||||
)
|
||||
assertEquals(MessageActionSheetState.Data(expected), viewModel.stateFlow.value)
|
||||
assertEquals(expected, (viewModel.stateFlow.value as MessageActionSheetState.Data).moveSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify star, unstar and mark read actions are visible when action sheet is summoned from mailbox`() {
|
||||
// given
|
||||
val messageIds = listOf("messageId")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.MAILBOX_ITEMS_IN_MAILBOX_SCREEN
|
||||
val expectedResult = MessageActionSheetState.ManageSectionState(
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget,
|
||||
showStarAction = true,
|
||||
showUnstarAction = true,
|
||||
showMarkReadAction = true,
|
||||
showViewInLightModeAction = false,
|
||||
showViewInDarkModeAction = false
|
||||
)
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
// when
|
||||
viewModel.setupViewState(
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
// then
|
||||
assertEquals(expectedResult, (viewModel.stateFlow.value as MessageActionSheetState.Data).manageSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify star action is visible when message is not starred in details screen`() {
|
||||
// given
|
||||
val messageIds = listOf("messageId")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
val expectedResult = MessageActionSheetState.ManageSectionState(
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget,
|
||||
showStarAction = true,
|
||||
showUnstarAction = false,
|
||||
showMarkReadAction = false,
|
||||
showViewInLightModeAction = false,
|
||||
showViewInDarkModeAction = false
|
||||
)
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
// when
|
||||
viewModel.setupViewState(
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
// then
|
||||
assertEquals(expectedResult, (viewModel.stateFlow.value as MessageActionSheetState.Data).manageSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify unstar action is visible when message is starred in details screen`() {
|
||||
// given
|
||||
val messageIds = listOf("messageId")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
val expectedResult = MessageActionSheetState.ManageSectionState(
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget,
|
||||
showStarAction = false,
|
||||
showUnstarAction = true,
|
||||
showMarkReadAction = false,
|
||||
showViewInLightModeAction = false,
|
||||
showViewInDarkModeAction = false
|
||||
)
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns true
|
||||
every { isAppInDarkMode(any()) } returns false
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
// when
|
||||
viewModel.setupViewState(
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
// then
|
||||
assertEquals(expectedResult, (viewModel.stateFlow.value as MessageActionSheetState.Data).manageSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify view in dark mode action is visible when app is in dark mode and web view is in light mode`() {
|
||||
// given
|
||||
val messageIds = listOf("messageId")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
val expectedResult = MessageActionSheetState.ManageSectionState(
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget,
|
||||
showStarAction = true,
|
||||
showUnstarAction = false,
|
||||
showMarkReadAction = false,
|
||||
showViewInLightModeAction = false,
|
||||
showViewInDarkModeAction = true
|
||||
)
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns true
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns false
|
||||
|
||||
// when
|
||||
viewModel.setupViewState(
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
// then
|
||||
assertEquals(expectedResult, (viewModel.stateFlow.value as MessageActionSheetState.Data).manageSectionState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify view in light mode action is visible when app and web view are in dark mode`() {
|
||||
// given
|
||||
val messageIds = listOf("messageId")
|
||||
val currentFolder = Constants.MessageLocationType.INBOX
|
||||
val actionsTarget = ActionSheetTarget.MESSAGE_ITEM_IN_DETAIL_SCREEN
|
||||
val expectedResult = MessageActionSheetState.ManageSectionState(
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget,
|
||||
showStarAction = true,
|
||||
showUnstarAction = false,
|
||||
showMarkReadAction = false,
|
||||
showViewInLightModeAction = true,
|
||||
showViewInDarkModeAction = false
|
||||
)
|
||||
every { savedStateHandle.get<Boolean>(EXTRA_ARG_IS_STARRED) } returns false
|
||||
every { isAppInDarkMode(any()) } returns true
|
||||
coEvery { getViewInDarkModeMessagePreference(any(), any(), any()) } returns true
|
||||
|
||||
// when
|
||||
viewModel.setupViewState(
|
||||
context,
|
||||
messageIds,
|
||||
currentFolder,
|
||||
currentFolder.asLabelId(),
|
||||
actionsTarget
|
||||
)
|
||||
|
||||
// then
|
||||
assertEquals(expectedResult, (viewModel.stateFlow.value as MessageActionSheetState.Data).manageSectionState)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue