android: rcv & snd files progress, distinguish XFTP and SMP; ios: files UI improvements (#2096)

This commit is contained in:
spaced4ndy
2023-03-29 15:48:00 +04:00
committed by GitHub
parent 67961180c9
commit 08dd321311
11 changed files with 145 additions and 52 deletions

View File

@@ -1711,7 +1711,8 @@ class CIFile(
val fileName: String, val fileName: String,
val fileSize: Long, val fileSize: Long,
val filePath: String? = null, val filePath: String? = null,
val fileStatus: CIFileStatus val fileStatus: CIFileStatus,
val fileProtocol: FileProtocol
) { ) {
val loaded: Boolean = when (fileStatus) { val loaded: Boolean = when (fileStatus) {
is CIFileStatus.SndStored -> true is CIFileStatus.SndStored -> true
@@ -1733,19 +1734,25 @@ class CIFile(
filePath: String? = "test.txt", filePath: String? = "test.txt",
fileStatus: CIFileStatus = CIFileStatus.RcvComplete fileStatus: CIFileStatus = CIFileStatus.RcvComplete
): CIFile = ): CIFile =
CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, filePath = filePath, fileStatus = fileStatus) CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, filePath = filePath, fileStatus = fileStatus, fileProtocol = FileProtocol.XFTP)
} }
} }
@Serializable
enum class FileProtocol {
@SerialName("smp") SMP,
@SerialName("xftp") XFTP;
}
@Serializable @Serializable
sealed class CIFileStatus { sealed class CIFileStatus {
@Serializable @SerialName("sndStored") object SndStored: CIFileStatus() @Serializable @SerialName("sndStored") object SndStored: CIFileStatus()
@Serializable @SerialName("sndTransfer") class SndTransfer(val sndProgress: Int, val sndTotal: Int): CIFileStatus() @Serializable @SerialName("sndTransfer") class SndTransfer(val sndProgress: Long, val sndTotal: Long): CIFileStatus()
@Serializable @SerialName("sndComplete") object SndComplete: CIFileStatus() @Serializable @SerialName("sndComplete") object SndComplete: CIFileStatus()
@Serializable @SerialName("sndCancelled") object SndCancelled: CIFileStatus() @Serializable @SerialName("sndCancelled") object SndCancelled: CIFileStatus()
@Serializable @SerialName("rcvInvitation") object RcvInvitation: CIFileStatus() @Serializable @SerialName("rcvInvitation") object RcvInvitation: CIFileStatus()
@Serializable @SerialName("rcvAccepted") object RcvAccepted: CIFileStatus() @Serializable @SerialName("rcvAccepted") object RcvAccepted: CIFileStatus()
@Serializable @SerialName("rcvTransfer") class RcvTransfer(val rcvProgress: Int, val rcvTotal: Int): CIFileStatus() @Serializable @SerialName("rcvTransfer") class RcvTransfer(val rcvProgress: Long, val rcvTotal: Long): CIFileStatus()
@Serializable @SerialName("rcvComplete") object RcvComplete: CIFileStatus() @Serializable @SerialName("rcvComplete") object RcvComplete: CIFileStatus()
@Serializable @SerialName("rcvCancelled") object RcvCancelled: CIFileStatus() @Serializable @SerialName("rcvCancelled") object RcvCancelled: CIFileStatus()
} }

View File

@@ -1402,6 +1402,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
chatItemSimpleUpdate(r.user, r.chatItem) chatItemSimpleUpdate(r.user, r.chatItem)
is CR.RcvFileComplete -> is CR.RcvFileComplete ->
chatItemSimpleUpdate(r.user, r.chatItem) chatItemSimpleUpdate(r.user, r.chatItem)
is CR.RcvFileProgressXFTP ->
chatItemSimpleUpdate(r.user, r.chatItem)
is CR.SndFileStart -> is CR.SndFileStart ->
chatItemSimpleUpdate(r.user, r.chatItem) chatItemSimpleUpdate(r.user, r.chatItem)
is CR.SndFileComplete -> { is CR.SndFileComplete -> {
@@ -1417,6 +1419,8 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a
removeFile(appContext, fileName) removeFile(appContext, fileName)
} }
} }
is CR.SndFileProgressXFTP ->
chatItemSimpleUpdate(r.user, r.chatItem)
is CR.CallInvitation -> { is CR.CallInvitation -> {
chatModel.callManager.reportNewIncomingCall(r.callInvitation) chatModel.callManager.reportNewIncomingCall(r.callInvitation)
} }
@@ -3044,12 +3048,14 @@ sealed class CR {
@Serializable @SerialName("rcvFileAcceptedSndCancelled") class RcvFileAcceptedSndCancelled(val user: User, val rcvFileTransfer: RcvFileTransfer): CR() @Serializable @SerialName("rcvFileAcceptedSndCancelled") class RcvFileAcceptedSndCancelled(val user: User, val rcvFileTransfer: RcvFileTransfer): CR()
@Serializable @SerialName("rcvFileStart") class RcvFileStart(val user: User, val chatItem: AChatItem): CR() @Serializable @SerialName("rcvFileStart") class RcvFileStart(val user: User, val chatItem: AChatItem): CR()
@Serializable @SerialName("rcvFileComplete") class RcvFileComplete(val user: User, val chatItem: AChatItem): CR() @Serializable @SerialName("rcvFileComplete") class RcvFileComplete(val user: User, val chatItem: AChatItem): CR()
@Serializable @SerialName("rcvFileProgressXFTP") class RcvFileProgressXFTP(val user: User, val chatItem: AChatItem, val receivedSize: Long, val totalSize: Long): CR()
// sending file events // sending file events
@Serializable @SerialName("sndFileStart") class SndFileStart(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileStart") class SndFileStart(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR()
@Serializable @SerialName("sndFileComplete") class SndFileComplete(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileComplete") class SndFileComplete(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR()
@Serializable @SerialName("sndFileCancelled") class SndFileCancelled(val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileCancelled") class SndFileCancelled(val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR()
@Serializable @SerialName("sndFileRcvCancelled") class SndFileRcvCancelled(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR() @Serializable @SerialName("sndFileRcvCancelled") class SndFileRcvCancelled(val user: User, val chatItem: AChatItem, val sndFileTransfer: SndFileTransfer): CR()
@Serializable @SerialName("sndGroupFileCancelled") class SndGroupFileCancelled(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sndFileTransfers: List<SndFileTransfer>): CR() @Serializable @SerialName("sndGroupFileCancelled") class SndGroupFileCancelled(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sndFileTransfers: List<SndFileTransfer>): CR()
@Serializable @SerialName("sndFileProgressXFTP") class SndFileProgressXFTP(val user: User, val chatItem: AChatItem, val fileTransferMeta: FileTransferMeta, val sentSize: Long, val totalSize: Long): CR()
@Serializable @SerialName("callInvitation") class CallInvitation(val callInvitation: RcvCallInvitation): CR() @Serializable @SerialName("callInvitation") class CallInvitation(val callInvitation: RcvCallInvitation): CR()
@Serializable @SerialName("callOffer") class CallOffer(val user: User, val contact: Contact, val callType: CallType, val offer: WebRTCSession, val sharedKey: String? = null, val askConfirmation: Boolean): CR() @Serializable @SerialName("callOffer") class CallOffer(val user: User, val contact: Contact, val callType: CallType, val offer: WebRTCSession, val sharedKey: String? = null, val askConfirmation: Boolean): CR()
@Serializable @SerialName("callAnswer") class CallAnswer(val user: User, val contact: Contact, val answer: WebRTCSession): CR() @Serializable @SerialName("callAnswer") class CallAnswer(val user: User, val contact: Contact, val answer: WebRTCSession): CR()
@@ -3146,11 +3152,13 @@ sealed class CR {
is RcvFileAccepted -> "rcvFileAccepted" is RcvFileAccepted -> "rcvFileAccepted"
is RcvFileStart -> "rcvFileStart" is RcvFileStart -> "rcvFileStart"
is RcvFileComplete -> "rcvFileComplete" is RcvFileComplete -> "rcvFileComplete"
is RcvFileProgressXFTP -> "rcvFileProgressXFTP"
is SndFileCancelled -> "sndFileCancelled" is SndFileCancelled -> "sndFileCancelled"
is SndFileComplete -> "sndFileComplete" is SndFileComplete -> "sndFileComplete"
is SndFileRcvCancelled -> "sndFileRcvCancelled" is SndFileRcvCancelled -> "sndFileRcvCancelled"
is SndFileStart -> "sndFileStart" is SndFileStart -> "sndFileStart"
is SndGroupFileCancelled -> "sndGroupFileCancelled" is SndGroupFileCancelled -> "sndGroupFileCancelled"
is SndFileProgressXFTP -> "sndFileProgressXFTP"
is CallInvitation -> "callInvitation" is CallInvitation -> "callInvitation"
is CallOffer -> "callOffer" is CallOffer -> "callOffer"
is CallAnswer -> "callAnswer" is CallAnswer -> "callAnswer"
@@ -3249,11 +3257,13 @@ sealed class CR {
is RcvFileAccepted -> withUser(user, json.encodeToString(chatItem)) is RcvFileAccepted -> withUser(user, json.encodeToString(chatItem))
is RcvFileStart -> withUser(user, json.encodeToString(chatItem)) is RcvFileStart -> withUser(user, json.encodeToString(chatItem))
is RcvFileComplete -> withUser(user, json.encodeToString(chatItem)) is RcvFileComplete -> withUser(user, json.encodeToString(chatItem))
is RcvFileProgressXFTP -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\nreceivedSize: $receivedSize\ntotalSize: $totalSize")
is SndFileCancelled -> json.encodeToString(chatItem) is SndFileCancelled -> json.encodeToString(chatItem)
is SndFileComplete -> withUser(user, json.encodeToString(chatItem)) is SndFileComplete -> withUser(user, json.encodeToString(chatItem))
is SndFileRcvCancelled -> withUser(user, json.encodeToString(chatItem)) is SndFileRcvCancelled -> withUser(user, json.encodeToString(chatItem))
is SndFileStart -> withUser(user, json.encodeToString(chatItem)) is SndFileStart -> withUser(user, json.encodeToString(chatItem))
is SndGroupFileCancelled -> withUser(user, json.encodeToString(chatItem)) is SndGroupFileCancelled -> withUser(user, json.encodeToString(chatItem))
is SndFileProgressXFTP -> withUser(user, "chatItem: ${json.encodeToString(chatItem)}\nsentSize: $sentSize\ntotalSize: $totalSize")
is CallInvitation -> "contact: ${callInvitation.contact.id}\ncallType: $callInvitation.callType\nsharedKey: ${callInvitation.sharedKey ?: ""}" is CallInvitation -> "contact: ${callInvitation.contact.id}\ncallType: $callInvitation.callType\nsharedKey: ${callInvitation.sharedKey ?: ""}"
is CallOffer -> withUser(user, "contact: ${contact.id}\ncallType: $callType\nsharedKey: ${sharedKey ?: ""}\naskConfirmation: $askConfirmation\noffer: ${json.encodeToString(offer)}") is CallOffer -> withUser(user, "contact: ${contact.id}\ncallType: $callType\nsharedKey: ${sharedKey ?: ""}\naskConfirmation: $askConfirmation\noffer: ${json.encodeToString(offer)}")
is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}") is CallAnswer -> withUser(user, "contact: ${contact.id}\nanswer: ${json.encodeToString(answer)}")

View File

@@ -182,6 +182,8 @@ fun ComposeView(
val pendingLinkUrl = rememberSaveable { mutableStateOf<String?>(null) } val pendingLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
val cancelledLinks = rememberSaveable { mutableSetOf<String>() } val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get() val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
val xftpSendEnabled = chatModel.controller.appPrefs.xftpSendEnabled.get()
val maxFileSize = getMaxFileSize(fileProtocol = if (xftpSendEnabled) FileProtocol.XFTP else FileProtocol.SMP)
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground) val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
val textStyle = remember { mutableStateOf(smallFont) } val textStyle = remember { mutableStateOf(smallFont) }
val cameraLauncher = rememberCameraLauncher { uri: Uri? -> val cameraLauncher = rememberCameraLauncher { uri: Uri? ->
@@ -212,13 +214,13 @@ fun ComposeView(
if (isAnimNewApi || isAnimOldApi) { if (isAnimNewApi || isAnimOldApi) {
// It's a gif or webp // It's a gif or webp
val fileSize = getFileSize(context, uri) val fileSize = getFileSize(context, uri)
if (fileSize != null && fileSize <= MAX_FILE_SIZE) { if (fileSize != null && fileSize <= maxFileSize) {
content.add(UploadContent.AnimatedImage(uri)) content.add(UploadContent.AnimatedImage(uri))
} else { } else {
bitmap = null bitmap = null
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
generalGetString(R.string.large_file), generalGetString(R.string.large_file),
String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(MAX_FILE_SIZE)) String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(maxFileSize))
) )
} }
} else { } else {
@@ -236,7 +238,7 @@ fun ComposeView(
val processPickedFile = { uri: Uri?, text: String? -> val processPickedFile = { uri: Uri?, text: String? ->
if (uri != null) { if (uri != null) {
val fileSize = getFileSize(context, uri) val fileSize = getFileSize(context, uri)
if (fileSize != null && fileSize <= MAX_FILE_SIZE) { if (fileSize != null && fileSize <= maxFileSize) {
val fileName = getFileName(SimplexApp.context, uri) val fileName = getFileName(SimplexApp.context, uri)
if (fileName != null) { if (fileName != null) {
composeState.value = composeState.value.copy(message = text ?: composeState.value.message, preview = ComposePreview.FilePreview(fileName, uri)) composeState.value = composeState.value.copy(message = text ?: composeState.value.message, preview = ComposePreview.FilePreview(fileName, uri))
@@ -244,7 +246,7 @@ fun ComposeView(
} else { } else {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
generalGetString(R.string.large_file), generalGetString(R.string.large_file),
String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(MAX_FILE_SIZE)) String.format(generalGetString(R.string.maximum_supported_file_size), formatBytes(maxFileSize))
) )
} }
} }

View File

@@ -3,6 +3,7 @@ package chat.simplex.app.views.chat.item
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -16,6 +17,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.* import androidx.compose.ui.tooling.preview.*
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -64,7 +66,7 @@ fun CIFileView(
fun fileSizeValid(): Boolean { fun fileSizeValid(): Boolean {
if (file != null) { if (file != null) {
return file.fileSize <= MAX_FILE_SIZE return file.fileSize <= getMaxFileSize(file.fileProtocol)
} }
return false return false
} }
@@ -78,15 +80,23 @@ fun CIFileView(
} else { } else {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
generalGetString(R.string.large_file), generalGetString(R.string.large_file),
String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(MAX_FILE_SIZE)) String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(getMaxFileSize(file.fileProtocol)))
) )
} }
} }
is CIFileStatus.RcvAccepted -> is CIFileStatus.RcvAccepted ->
AlertManager.shared.showAlertMsg( when (file.fileProtocol) {
generalGetString(R.string.waiting_for_file), FileProtocol.XFTP ->
String.format(generalGetString(R.string.file_will_be_received_when_contact_is_online), MAX_FILE_SIZE) AlertManager.shared.showAlertMsg(
) generalGetString(R.string.waiting_for_file),
generalGetString(R.string.file_will_be_received_when_contact_completes_uploading)
)
FileProtocol.SMP ->
AlertManager.shared.showAlertMsg(
generalGetString(R.string.waiting_for_file),
generalGetString(R.string.file_will_be_received_when_contact_is_online)
)
}
is CIFileStatus.RcvComplete -> { is CIFileStatus.RcvComplete -> {
val filePath = getLoadedFilePath(context, file) val filePath = getLoadedFilePath(context, file)
if (filePath != null) { if (filePath != null) {
@@ -109,6 +119,20 @@ fun CIFileView(
) )
} }
@Composable
fun progressCircle(progress: Long, total: Long) {
val angle = 360f * (progress.toDouble() / total.toDouble()).toFloat()
val strokeWidth = with(LocalDensity.current) { 3.dp.toPx() }
val strokeColor = if (isInDarkTheme()) FileDark else FileLight
Surface(
Modifier.drawRingModifier(angle, strokeColor, strokeWidth),
color = Color.Transparent,
shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50))
) {
Box(Modifier.size(32.dp))
}
}
@Composable @Composable
fun fileIndicator() { fun fileIndicator() {
Box( Box(
@@ -120,8 +144,16 @@ fun CIFileView(
) { ) {
if (file != null) { if (file != null) {
when (file.fileStatus) { when (file.fileStatus) {
is CIFileStatus.SndStored -> fileIcon() is CIFileStatus.SndStored ->
is CIFileStatus.SndTransfer -> progressIndicator() when (file.fileProtocol) {
FileProtocol.XFTP -> progressIndicator()
FileProtocol.SMP -> fileIcon()
}
is CIFileStatus.SndTransfer ->
when (file.fileProtocol) {
FileProtocol.XFTP -> progressCircle(file.fileStatus.sndProgress, file.fileStatus.sndTotal)
FileProtocol.SMP -> progressIndicator()
}
is CIFileStatus.SndComplete -> fileIcon(innerIcon = Icons.Filled.Check) is CIFileStatus.SndComplete -> fileIcon(innerIcon = Icons.Filled.Check)
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = Icons.Outlined.Close) is CIFileStatus.SndCancelled -> fileIcon(innerIcon = Icons.Outlined.Close)
is CIFileStatus.RcvInvitation -> is CIFileStatus.RcvInvitation ->
@@ -130,7 +162,12 @@ fun CIFileView(
else else
fileIcon(innerIcon = Icons.Outlined.PriorityHigh, color = WarningOrange) fileIcon(innerIcon = Icons.Outlined.PriorityHigh, color = WarningOrange)
is CIFileStatus.RcvAccepted -> fileIcon(innerIcon = Icons.Outlined.MoreHoriz) is CIFileStatus.RcvAccepted -> fileIcon(innerIcon = Icons.Outlined.MoreHoriz)
is CIFileStatus.RcvTransfer -> progressIndicator() is CIFileStatus.RcvTransfer ->
if (file.fileProtocol == FileProtocol.XFTP && file.fileStatus.rcvProgress < file.fileStatus.rcvTotal) {
progressCircle(file.fileStatus.rcvProgress, file.fileStatus.rcvTotal)
} else {
progressIndicator()
}
is CIFileStatus.RcvComplete -> fileIcon() is CIFileStatus.RcvComplete -> fileIcon()
is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = Icons.Outlined.Close) is CIFileStatus.RcvCancelled -> fileIcon(innerIcon = Icons.Outlined.Close)
} }

View File

@@ -27,8 +27,7 @@ import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import chat.simplex.app.* import chat.simplex.app.*
import chat.simplex.app.R import chat.simplex.app.R
import chat.simplex.app.model.CIFile import chat.simplex.app.model.*
import chat.simplex.app.model.CIFileStatus
import chat.simplex.app.views.helpers.* import chat.simplex.app.views.helpers.*
import coil.ImageLoader import coil.ImageLoader
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
@@ -45,6 +44,15 @@ fun CIImageView(
showMenu: MutableState<Boolean>, showMenu: MutableState<Boolean>,
receiveFile: (Long) -> Unit receiveFile: (Long) -> Unit
) { ) {
@Composable
fun progressIndicator() {
CircularProgressIndicator(
Modifier.size(16.dp),
color = Color.White,
strokeWidth = 2.dp
)
}
@Composable @Composable
fun loadingIndicator() { fun loadingIndicator() {
if (file != null) { if (file != null) {
@@ -55,12 +63,13 @@ fun CIImageView(
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
when (file.fileStatus) { when (file.fileStatus) {
is CIFileStatus.SndStored ->
when (file.fileProtocol) {
FileProtocol.XFTP -> progressIndicator()
FileProtocol.SMP -> {}
}
is CIFileStatus.SndTransfer -> is CIFileStatus.SndTransfer ->
CircularProgressIndicator( progressIndicator()
Modifier.size(16.dp),
color = Color.White,
strokeWidth = 2.dp
)
is CIFileStatus.SndComplete -> is CIFileStatus.SndComplete ->
Icon( Icon(
Icons.Filled.Check, Icons.Filled.Check,
@@ -76,11 +85,7 @@ fun CIImageView(
tint = Color.White tint = Color.White
) )
is CIFileStatus.RcvTransfer -> is CIFileStatus.RcvTransfer ->
CircularProgressIndicator( progressIndicator()
Modifier.size(16.dp),
color = Color.White,
strokeWidth = 2.dp
)
is CIFileStatus.RcvInvitation -> is CIFileStatus.RcvInvitation ->
Icon( Icon(
Icons.Outlined.ArrowDownward, Icons.Outlined.ArrowDownward,
@@ -136,7 +141,7 @@ fun CIImageView(
fun fileSizeValid(): Boolean { fun fileSizeValid(): Boolean {
if (file != null) { if (file != null) {
return file.fileSize <= MAX_FILE_SIZE return file.fileSize <= getMaxFileSize(file.fileProtocol)
} }
return false return false
} }
@@ -179,14 +184,22 @@ fun CIImageView(
} else { } else {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
generalGetString(R.string.large_file), generalGetString(R.string.large_file),
String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(MAX_FILE_SIZE)) String.format(generalGetString(R.string.contact_sent_large_file), formatBytes(getMaxFileSize(file.fileProtocol)))
) )
} }
CIFileStatus.RcvAccepted -> CIFileStatus.RcvAccepted ->
AlertManager.shared.showAlertMsg( when (file.fileProtocol) {
generalGetString(R.string.waiting_for_image), FileProtocol.XFTP ->
generalGetString(R.string.image_will_be_received_when_contact_is_online) AlertManager.shared.showAlertMsg(
) generalGetString(R.string.waiting_for_image),
generalGetString(R.string.image_will_be_received_when_contact_completes_uploading)
)
FileProtocol.SMP ->
AlertManager.shared.showAlertMsg(
generalGetString(R.string.waiting_for_image),
generalGetString(R.string.image_will_be_received_when_contact_is_online)
)
}
CIFileStatus.RcvTransfer(rcvProgress = 7, rcvTotal = 10) -> {} // ? CIFileStatus.RcvTransfer(rcvProgress = 7, rcvTotal = 10) -> {} // ?
CIFileStatus.RcvComplete -> {} // ? CIFileStatus.RcvComplete -> {} // ?
CIFileStatus.RcvCancelled -> {} // TODO CIFileStatus.RcvCancelled -> {} // TODO

View File

@@ -228,7 +228,7 @@ private fun VoiceMsgIndicator(
} }
} }
private fun Modifier.drawRingModifier(angle: Float, color: Color, strokeWidth: Float) = drawWithCache { fun Modifier.drawRingModifier(angle: Float, color: Color, strokeWidth: Float) = drawWithCache {
val brush = Brush.linearGradient( val brush = Brush.linearGradient(
0f to Color.Transparent, 0f to Color.Transparent,
0f to color, 0f to color,

View File

@@ -237,8 +237,9 @@ const val MAX_VOICE_SIZE_AUTO_RCV: Long = MAX_IMAGE_SIZE
const val MAX_VOICE_SIZE_FOR_SENDING: Long = 94680 // 6 chunks * 15780 bytes per chunk const val MAX_VOICE_SIZE_FOR_SENDING: Long = 94680 // 6 chunks * 15780 bytes per chunk
const val MAX_VOICE_MILLIS_FOR_SENDING: Int = 43_000 const val MAX_VOICE_MILLIS_FOR_SENDING: Int = 43_000
//const val MAX_FILE_SIZE_SMP: Long = 8000000 // TODO distinguish between XFTP and SMP files const val MAX_FILE_SIZE_SMP: Long = 8000000
const val MAX_FILE_SIZE: Long = 1_073_741_824
const val MAX_FILE_SIZE_XFTP: Long = 1_073_741_824
fun getFilesDirectory(context: Context): String { fun getFilesDirectory(context: Context): String {
return context.filesDir.toString() return context.filesDir.toString()
@@ -494,9 +495,9 @@ fun formatBytes(bytes: Long): String {
return "0 bytes" return "0 bytes"
} }
val bytesDouble = bytes.toDouble() val bytesDouble = bytes.toDouble()
val k = 1000.toDouble() val k = 1024.toDouble()
val units = arrayOf("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB") val units = arrayOf("bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
val i = kotlin.math.floor(log2(bytesDouble) / log2(k)) val i = floor(log2(bytesDouble) / log2(k))
val size = bytesDouble / k.pow(i) val size = bytesDouble / k.pow(i)
val unit = units[i.toInt()] val unit = units[i.toInt()]
@@ -541,6 +542,13 @@ fun directoryFileCountAndSize(dir: String): Pair<Int, Long> { // count, size in
return fileCount to bytes return fileCount to bytes
} }
fun getMaxFileSize(fileProtocol: FileProtocol): Long {
return when (fileProtocol) {
FileProtocol.XFTP -> MAX_FILE_SIZE_XFTP
FileProtocol.SMP -> MAX_FILE_SIZE_SMP
}
}
fun Color.darker(factor: Float = 0.1f): Color = fun Color.darker(factor: Float = 0.1f): Color =
Color(max(red * (1 - factor), 0f), max(green * (1 - factor), 0f), max(blue * (1 - factor), 0f), alpha) Color(max(red * (1 - factor), 0f), max(green * (1 - factor), 0f), max(blue * (1 - factor), 0f), alpha)

View File

@@ -238,6 +238,7 @@
<string name="icon_descr_asked_to_receive">Asked to receive the image</string> <string name="icon_descr_asked_to_receive">Asked to receive the image</string>
<string name="icon_descr_image_snd_complete">Image sent</string> <string name="icon_descr_image_snd_complete">Image sent</string>
<string name="waiting_for_image">Waiting for image</string> <string name="waiting_for_image">Waiting for image</string>
<string name="image_will_be_received_when_contact_completes_uploading">Image will be received when your contact completes uploading it.</string>
<string name="image_will_be_received_when_contact_is_online">Image will be received when your contact is online, please wait or check later!</string> <string name="image_will_be_received_when_contact_is_online">Image will be received when your contact is online, please wait or check later!</string>
<string name="image_saved">Image saved to Gallery</string> <string name="image_saved">Image saved to Gallery</string>
@@ -247,6 +248,7 @@
<string name="contact_sent_large_file">Your contact sent a file that is larger than currently supported maximum size (<xliff:g id="maxFileSize">%1$s</xliff:g>).</string> <string name="contact_sent_large_file">Your contact sent a file that is larger than currently supported maximum size (<xliff:g id="maxFileSize">%1$s</xliff:g>).</string>
<string name="maximum_supported_file_size">Currently maximum supported file size is <xliff:g id="maxFileSize">%1$s</xliff:g>.</string> <string name="maximum_supported_file_size">Currently maximum supported file size is <xliff:g id="maxFileSize">%1$s</xliff:g>.</string>
<string name="waiting_for_file">Waiting for file</string> <string name="waiting_for_file">Waiting for file</string>
<string name="file_will_be_received_when_contact_completes_uploading">File will be received when your contact completes uploading it.</string>
<string name="file_will_be_received_when_contact_is_online">File will be received when your contact is online, please wait or check later!</string> <string name="file_will_be_received_when_contact_is_online">File will be received when your contact is online, please wait or check later!</string>
<string name="file_saved">File saved</string> <string name="file_saved">File saved</string>
<string name="file_not_found">File not found</string> <string name="file_not_found">File not found</string>

View File

@@ -56,7 +56,7 @@ struct CIFileView: View {
case .sndComplete: return false case .sndComplete: return false
case .sndCancelled: return false case .sndCancelled: return false
case .rcvInvitation: return true case .rcvInvitation: return true
case .rcvAccepted: return file.isSMP case .rcvAccepted: return true
case .rcvTransfer: return false case .rcvTransfer: return false
case .rcvComplete: return true case .rcvComplete: return true
case .rcvCancelled: return false case .rcvCancelled: return false
@@ -92,7 +92,13 @@ struct CIFileView: View {
) )
} }
case .rcvAccepted: case .rcvAccepted:
if file.isSMP { switch file.fileProtocol {
case .xftp:
AlertManager.shared.showAlertMsg(
title: "Waiting for file",
message: "File will be received when your contact completes uploading it."
)
case .smp:
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title: "Waiting for file", title: "Waiting for file",
message: "File will be received when your contact is online, please wait or check later!" message: "File will be received when your contact is online, please wait or check later!"
@@ -112,7 +118,11 @@ struct CIFileView: View {
@ViewBuilder private func fileIndicator() -> some View { @ViewBuilder private func fileIndicator() -> some View {
if let file = file { if let file = file {
switch file.fileStatus { switch file.fileStatus {
case .sndStored: fileIcon("doc.fill") case .sndStored:
switch file.fileProtocol {
case .xftp: progressView()
case .smp: fileIcon("doc.fill")
}
case let .sndTransfer(sndProgress, sndTotal): case let .sndTransfer(sndProgress, sndTotal):
switch file.fileProtocol { switch file.fileProtocol {
case .xftp: progressCircle(sndProgress, sndTotal) case .xftp: progressCircle(sndProgress, sndTotal)
@@ -169,7 +179,7 @@ struct CIFileView: View {
Circle() Circle()
.trim(from: 0, to: Double(progress) / Double(total)) .trim(from: 0, to: Double(progress) / Double(total))
.stroke( .stroke(
Color.accentColor, Color(uiColor: .tertiaryLabel),
style: StrokeStyle(lineWidth: 3) style: StrokeStyle(lineWidth: 3)
) )
.rotationEffect(.degrees(-90)) .rotationEffect(.degrees(-90))

View File

@@ -41,7 +41,13 @@ struct CIImageView: View {
// TODO image accepted alert? // TODO image accepted alert?
} }
case .rcvAccepted: case .rcvAccepted:
if file.isSMP { switch file.fileProtocol {
case .xftp:
AlertManager.shared.showAlertMsg(
title: "Waiting for image",
message: "Image will be received when your contact completes uploading it."
)
case .smp:
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title: "Waiting for image", title: "Waiting for image",
message: "Image will be received when your contact is online, please wait or check later!" message: "Image will be received when your contact is online, please wait or check later!"
@@ -79,6 +85,11 @@ struct CIImageView: View {
@ViewBuilder private func loadingIndicator() -> some View { @ViewBuilder private func loadingIndicator() -> some View {
if let file = chatItem.file { if let file = chatItem.file {
switch file.fileStatus { switch file.fileStatus {
case .sndStored:
switch file.fileProtocol {
case .xftp: progressView()
case .smp: EmptyView()
}
case .sndTransfer: case .sndTransfer:
progressView() progressView()
case .sndComplete: case .sndComplete:

View File

@@ -2221,13 +2221,6 @@ public struct CIFile: Decodable {
CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, filePath: filePath, fileStatus: fileStatus, fileProtocol: .xftp) CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, filePath: filePath, fileStatus: fileStatus, fileProtocol: .xftp)
} }
public var isSMP: Bool {
switch self.fileProtocol {
case .smp: return true
default: return false
}
}
public var loaded: Bool { public var loaded: Bool {
get { get {
switch self.fileStatus { switch self.fileStatus {