Use dynamic/rule-based size calculations for transfer control view.

This commit is contained in:
Cody Henthorne 2025-01-17 13:29:26 -05:00 committed by Greyson Parrelli
parent e945efac8b
commit d7c2e6844b
3 changed files with 37 additions and 27 deletions

View file

@ -17,6 +17,8 @@ import androidx.core.view.updateLayoutParams
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.signal.core.util.ByteSize
import org.signal.core.util.bytes
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.attachments.Attachment
@ -529,7 +531,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
val existingEvent = mutableMap[attachment]
if (existingEvent == null || updateEvent.completed > existingEvent.completed) {
mutableMap[attachment] = updateEvent
} else if (updateEvent.completed < 0) {
} else if (updateEvent.completed < 0.bytes) {
mutableMap.remove(attachment)
}
verboseLog("onEventAsync compression update")
@ -540,7 +542,7 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
val existingEvent = mutableMap[attachment]
if (existingEvent == null || updateEvent.completed > existingEvent.completed) {
mutableMap[attachment] = updateEvent
} else if (updateEvent.completed < 0) {
} else if (updateEvent.completed < 0.bytes) {
mutableMap.remove(attachment)
}
verboseLog("onEventAsync network update")
@ -556,14 +558,14 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
val isNewSlideSet = !isUpdateToExistingSet(state, slides)
val networkProgress: MutableMap<Attachment, Progress> = if (isNewSlideSet) HashMap() else state.networkProgress.toMutableMap()
if (isNewSlideSet) {
slides.forEach { networkProgress[it.asAttachment()] = Progress(0L, it.fileSize) }
slides.forEach { networkProgress[it.asAttachment()] = Progress(0.bytes, it.fileSize.bytes) }
}
val compressionProgress: MutableMap<Attachment, Progress> = if (isNewSlideSet) HashMap() else state.compressionProgress.toMutableMap()
var allStreamableOrDone = true
for (slide in slides) {
val attachment = slide.asAttachment()
if (attachment.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE) {
networkProgress[attachment] = Progress(attachment.size, attachment.size)
networkProgress[attachment] = Progress(attachment.size.bytes, attachment.size.bytes)
} else if (!MediaUtil.isInstantVideoSupported(slide)) {
allStreamableOrDone = false
}
@ -657,12 +659,12 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
private fun isCompressing(state: TransferControlViewState): Boolean {
val total = state.compressionProgress.sumTotal()
return total > 0 && state.compressionProgress.sumCompleted() / total < 0.99f
return total > 0.bytes && state.compressionProgress.sumCompleted().percentageOf(total) < 0.99f
}
private fun calculateProgress(state: TransferControlViewState): Float {
val totalCompressionProgress: Float = state.compressionProgress.values.map { it.completed.toFloat() / it.total }.sum()
val totalDownloadProgress: Float = state.networkProgress.values.map { it.completed.toFloat() / it.total }.sum()
val totalCompressionProgress: Float = state.compressionProgress.values.map { it.completed.percentageOf(it.total) }.sum()
val totalDownloadProgress: Float = state.networkProgress.values.map { it.completed.percentageOf(it.total) }.sum()
val weightedProgress = UPLOAD_TASK_WEIGHT * totalDownloadProgress + COMPRESSION_TASK_WEIGHT * totalCompressionProgress
val weightedTotal = (UPLOAD_TASK_WEIGHT * state.networkProgress.size + COMPRESSION_TASK_WEIGHT * state.compressionProgress.size).toFloat()
return weightedProgress / weightedTotal
@ -677,35 +679,35 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
val remainingSlides = currentState.slides.filterNot { it.transferState == AttachmentTable.TRANSFER_PROGRESS_DONE }
val downloadCount = remainingSlides.size
binding.primaryDetailsText.text = context.resources.getQuantityString(R.plurals.TransferControlView_n_items, downloadCount, downloadCount)
val mebibyteCount = (currentState.networkProgress.sumTotal() - currentState.networkProgress.sumCompleted()) / MEBIBYTE
binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__filesize, mebibyteCount)
val size = currentState.networkProgress.sumTotal() - currentState.networkProgress.sumCompleted()
binding.secondaryDetailsText.text = size.toUnitString()
}
Mode.PENDING_GALLERY_CONTAINS_PLAYABLE -> {
binding.secondaryDetailsText.updateLayoutParams {
width = ViewGroup.LayoutParams.WRAP_CONTENT
}
val mebibyteCount = (currentState.networkProgress.sumTotal() - currentState.networkProgress.sumCompleted()) / MEBIBYTE
binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__filesize, mebibyteCount)
val size = currentState.networkProgress.sumTotal() - currentState.networkProgress.sumCompleted()
binding.secondaryDetailsText.text = size.toUnitString()
}
Mode.PENDING_SINGLE_ITEM, Mode.PENDING_VIDEO_PLAYABLE -> {
binding.secondaryDetailsText.updateLayoutParams {
width = ViewGroup.LayoutParams.WRAP_CONTENT
}
val mebibyteCount = (currentState.slides.sumOf { it.asAttachment().size }) / MEBIBYTE
binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__filesize, mebibyteCount)
val size: ByteSize = (currentState.slides.sumOf { it.asAttachment().size }).bytes
binding.secondaryDetailsText.text = size.toUnitString()
}
Mode.DOWNLOADING_GALLERY, Mode.DOWNLOADING_SINGLE_ITEM, Mode.DOWNLOADING_VIDEO_PLAYABLE, Mode.UPLOADING_GALLERY, Mode.UPLOADING_SINGLE_ITEM -> {
if (currentState.isUpload && (currentState.networkProgress.sumCompleted() == 0L || isCompressing(currentState))) {
if (currentState.isUpload && (currentState.networkProgress.sumCompleted() == 0.bytes || isCompressing(currentState))) {
binding.secondaryDetailsText.updateLayoutParams {
width = ViewGroup.LayoutParams.WRAP_CONTENT
}
binding.secondaryDetailsText.text = context.getString(R.string.TransferControlView__processing)
} else {
val progressMiB = currentState.networkProgress.sumCompleted() / MEBIBYTE
val totalMiB = currentState.networkProgress.sumTotal() / MEBIBYTE
val progressMiB = currentState.networkProgress.sumCompleted().toUnitString()
val totalMiB = currentState.networkProgress.sumTotal().toUnitString()
val completedLabel = context.resources.getString(R.string.TransferControlView__download_progress, totalMiB, totalMiB)
val desiredWidth = StaticLayout.getDesiredWidth(completedLabel, binding.secondaryDetailsText.paint)
binding.secondaryDetailsText.text = context.resources.getString(R.string.TransferControlView__download_progress, progressMiB, totalMiB)
@ -743,7 +745,6 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
private const val SECONDARY_TEXT_OFFSET_DP = 6
private const val RETRY_SECONDARY_TEXT_OFFSET_DP = 6
private const val PRIMARY_TEXT_OFFSET_DP = 4
private const val MEBIBYTE = 1048576f
/**
* A weighting compared to [UPLOAD_TASK_WEIGHT]
@ -773,20 +774,20 @@ class TransferControlView @JvmOverloads constructor(context: Context, attrs: Att
}
}
data class Progress(val completed: Long, val total: Long) {
data class Progress(val completed: ByteSize, val total: ByteSize) {
companion object {
fun fromEvent(event: PartProgressEvent): Progress {
return Progress(event.progress, event.total)
return Progress(event.progress.bytes, event.total.bytes)
}
}
}
private fun Map<Attachment, Progress>.sumCompleted(): Long {
return this.values.sumOf { it.completed }
private fun Map<Attachment, Progress>.sumCompleted(): ByteSize {
return this.values.sumOf { it.completed.inWholeBytes }.bytes
}
private fun Map<Attachment, Progress>.sumTotal(): Long {
return this.values.sumOf { it.total }
private fun Map<Attachment, Progress>.sumTotal(): ByteSize {
return this.values.sumOf { it.total.inWholeBytes }.bytes
}
enum class Mode {

View file

@ -3033,10 +3033,7 @@
<!-- Status update label used while the device is transcoding video as a prerequisite to uploading -->
<string name="TransferControlView__processing">Processing…</string>
<!-- Status update label used while the device is transmitting data over the network. Will take the form of "1.0 MB/2.0 MB" -->
<string name="TransferControlView__download_progress">%1.1f MB/%2.1f MB</string>
<!-- Attachment file size label for not-yet-downloaded images and video. Will take the form of "1.0 MB" -->
<string name="TransferControlView__filesize">%1.1f MB</string>
<string name="TransferControlView__download_progress">%1$s/%2$s</string>
<!-- UnauthorizedReminder -->
<!-- Message shown in a reminder banner when the user\'s device is no longer registered -->

View file

@ -99,6 +99,18 @@ class ByteSize(val bytes: Long) {
return bytes.compareTo(other.bytes)
}
operator fun plus(other: ByteSize): ByteSize {
return ByteSize(this.inWholeBytes + other.inWholeBytes)
}
fun percentageOf(other: ByteSize): Float {
return this.inWholeBytes.toFloat() / other.inWholeBytes.toFloat()
}
operator fun minus(other: ByteSize): ByteSize {
return ByteSize(this.inWholeBytes - other.inWholeBytes)
}
enum class Size(val label: String) {
BYTE("B"),
KIBIBYTE("KB"),