Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add highlighted text in the title to the upload progress screen #158

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*
* Infomaniak SwissTransfer - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.swisstransfer.ui.components

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme
import com.infomaniak.swisstransfer.ui.utils.PreviewLightAndDark
import kotlin.math.cos
import kotlin.math.sin

private val VERTICAL_PADDING @Composable get() = with(LocalDensity.current) { 3.sp.toPx() }
private val HORIZONTAL_PADDING @Composable get() = with(LocalDensity.current) { 10.sp.toPx() }
private const val ROTATION_ANGLE_DEGREE = -3.0f

@Composable
fun HighlightedText(
templateRes: Int,
argumentRes: Int,
style: TextStyle,
verticalPadding: Float = VERTICAL_PADDING,
horizontalPadding: Float = HORIZONTAL_PADDING,
angleDegrees: Float = ROTATION_ANGLE_DEGREE,
) {
val template = stringResource(templateRes)
val argument = stringResource(argumentRes)
val text = String.format(template, argument)

val highlightedColor = SwissTransferTheme.colors.highlightedColor

var highlightedPath by remember { mutableStateOf<Path?>(null) }

Text(
text = text,
style = style,
onTextLayout = { layoutResult ->
val boundingBoxes = layoutResult.getArgumentBoundingBoxes(text, argument)
highlightedPath = boundingBoxes.transformForHighlightedStyle(verticalPadding, horizontalPadding, angleDegrees)
},
modifier = Modifier.drawBehind {
highlightedPath?.let {
drawPath(path = it, style = Fill, color = highlightedColor)
}
},
)
}

private fun TextLayoutResult.getArgumentBoundingBoxes(text: String, argument: String): List<Rect> {
val startIndex = text.indexOf(argument)
return getBoundingBoxesForRange(start = startIndex, end = startIndex + argument.count())
}

private fun List<Rect>.transformForHighlightedStyle(
verticalPadding: Float,
horizontalPadding: Float,
angleDegrees: Float,
): Path = Path().apply {
forEach { boundingBox ->
addPath(boundingBox.transformForHighlightedStyle(verticalPadding, horizontalPadding, angleDegrees))
}
}

private fun Rect.transformForHighlightedStyle(verticalPadding: Float, horizontalPadding: Float, angleDegrees: Float): Path {
return getRotatedRectanglePath(
Rect(
left = left - horizontalPadding,
top = top - verticalPadding,
right = right + horizontalPadding,
bottom = bottom + verticalPadding,
),
angleDegrees = angleDegrees,
)
}

private fun getRotatedRectanglePath(rect: Rect, angleDegrees: Float): Path {
val centerX = rect.left + (rect.width / 2)
val centerY = rect.top + (rect.height / 2)

val angleRadians = Math.toRadians(angleDegrees.toDouble())

// Function to rotate a point around the center
fun rotatePoint(x: Float, y: Float, centerX: Float, centerY: Float, angleRadians: Double): Offset {
val dx = x - centerX
val dy = y - centerY
val cosAngle = cos(angleRadians)
val sinAngle = sin(angleRadians)
val newX = (dx * cosAngle - dy * sinAngle + centerX).toFloat()
val newY = (dx * sinAngle + dy * cosAngle + centerY).toFloat()
return Offset(newX, newY)
}

val topLeft = rotatePoint(rect.left, rect.top, centerX, centerY, angleRadians)
val topRight = rotatePoint(rect.right, rect.top, centerX, centerY, angleRadians)
val bottomRight = rotatePoint(rect.right, rect.bottom, centerX, centerY, angleRadians)
val bottomLeft = rotatePoint(rect.left, rect.bottom, centerX, centerY, angleRadians)

return Path().apply {
moveTo(topLeft.x, topLeft.y)
lineTo(topRight.x, topRight.y)
lineTo(bottomRight.x, bottomRight.y)
lineTo(bottomLeft.x, bottomLeft.y)
close()
}
}

private fun TextLayoutResult.getBoundingBoxesForRange(start: Int, end: Int): List<Rect> {
var previousRect: Rect? = null
var firstLineCharRect: Rect? = null
val boundingBoxes = mutableListOf<Rect>()

for (index in start..end) {
val rect = getBoundingBox(index)
val isLastRect = index == end

// Single char case
if (isLastRect && firstLineCharRect == null) {
firstLineCharRect = rect
previousRect = rect
}

// `rect.right` is zero for the last space in each line
// Might be an issue: https://issuetracker.google.com/issues/197146630
if (!isLastRect && rect.right == 0.0f) continue

if (firstLineCharRect == null) {
firstLineCharRect = rect
} else if (previousRect != null && (previousRect.bottom != rect.bottom || isLastRect)) {
boundingBoxes.add(firstLineCharRect.copy(right = previousRect.right))
firstLineCharRect = rect
}
previousRect = rect
}
return boundingBoxes
}

@PreviewLightAndDark
@Preview(locale = "fr")
@Preview(locale = "de")
@Preview(locale = "es")
@Preview(locale = "it")
@Composable
private fun Preview() {
SwissTransferTheme {
Surface {
Box(modifier = Modifier.padding(20.dp)) {
HighlightedText(
templateRes = R.string.uploadProgressTitleTemplate,
argumentRes = R.string.uploadProgressTitleArgument,
style = SwissTransferTheme.typography.bodyMedium
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private fun getShowPasswordButton(shouldShowPassword: Boolean, onClick: () -> Un

@Composable
@Preview
fun Preview() {
private fun Preview() {
SwissTransferTheme {
Surface {
Column(Modifier.padding(Margin.Medium)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.components.HighlightedText
import com.infomaniak.swisstransfer.ui.screen.newtransfer.upload.UploadProgressAdType
import com.infomaniak.swisstransfer.ui.theme.Dimens
import com.infomaniak.swisstransfer.ui.theme.Margin
Expand All @@ -45,7 +45,13 @@ fun ColumnScope.AdHeader(adScreenType: UploadProgressAdType) {
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.height(Margin.Giant))
Text(stringResource(R.string.uploadSuccessTitle), style = SwissTransferTheme.typography.bodyMedium)

HighlightedText(
templateRes = R.string.uploadProgressTitleTemplate,
argumentRes = R.string.uploadProgressTitleArgument,
style = SwissTransferTheme.typography.bodyMedium,
)

Spacer(modifier = Modifier.height(Margin.Huge))
Text(
text = adScreenType.description(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,5 @@ val CustomDarkColorScheme = CustomColorScheme(
transferFilePreviewOverflow = Color(dark3),
onTransferFilePreviewOverflow = Color(green_main),
transferListStroke = Color(green_main),
highlightedColor = Color(green_dark),
)
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ val CustomLightColorScheme = CustomColorScheme(
transferFilePreviewOverflow = Color(green_dark),
onTransferFilePreviewOverflow = Color(green_contrast),
transferListStroke = Color(green_dark),
highlightedColor = Color(green_secondary),
)
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ data class CustomColorScheme(
val transferFilePreviewOverflow: Color = Color.Unspecified,
val onTransferFilePreviewOverflow: Color = Color.Unspecified,
val transferListStroke: Color = Color.Unspecified,
val highlightedColor: Color = Color.Unspecified,
)

private val Shapes = Shapes(
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@
<string name="uploadProgressDescriptionTemplateIndependence">Wir entwickeln %s Ohne Kompromisse bei Ökologie, Privatsphäre und Menschen.</string>
<string name="uploadErrorDescription">Die Übertragung hat nicht funktioniert, gib ihr noch eine Chance. Wenn das Problem weiterhin besteht, wende dich an unseren Support.</string>
<string name="uploadErrorTitle">Was ist das denn?</string>
<string name="uploadProgressTitleArgument">zeichnet uns aus</string>
<string name="uploadProgressTitleTemplate">Was %s?</string>
<plurals name="uploadSuccessEmailDescription">
<item quantity="one">Der Download-Link wurde erfolgreich an den folgenden Empfänger gesendet:</item>
<item quantity="other">Der Download-Link wurde an die folgenden Empfänger gesendet:</item>
Expand All @@ -134,7 +136,6 @@
<string name="uploadSuccessLinkDescription">Lass diesen QR-Code in deiner Umgebung scannen, um deine Dateien weiterzugeben, oder kopiere den untenstehenden Link.</string>
<string name="uploadSuccessLinkTitle">Deine Überweisung ist fertig!</string>
<string name="uploadSuccessQrTitle">Dein QR-Code ist fertig!</string>
<string name="uploadSuccessTitle">Was zeichnet uns aus?</string>
<string name="uploadSuccessTransferInProgress">Transfer wird durchgeführt…</string>
<string name="urlAbout">https://www.infomaniak.com/de/about</string>
<string name="urlUserReportAndroid">https://feedback.userreport.com/9abc7665-a78e-4fd2-aa41-a47a8b867fcd/#ideas/popular</string>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@
<string name="uploadProgressDescriptionTemplateIndependence">Desarrollamos %s Sin comprometer la ecología, la privacidad ni a las personas.</string>
<string name="uploadErrorDescription">La transferencia no ha funcionado, dale otra oportunidad. Si el problema persiste, ponte en contacto con nuestro equipo de asistencia.</string>
<string name="uploadErrorTitle">¿Pero qué, qué?</string>
<string name="uploadProgressTitleArgument">diferencia</string>
<string name="uploadProgressTitleTemplate">¿Qué nos %s?</string>
<plurals name="uploadSuccessEmailDescription">
<item quantity="one">El enlace de descarga se ha enviado al siguiente destinatario:</item>
<item quantity="other">El enlace de descarga se ha enviado a los siguientes destinatarios:</item>
Expand All @@ -134,7 +136,6 @@
<string name="uploadSuccessLinkDescription">Escanea este código QR a tu alrededor para compartir tus archivos o copia el enlace que aparece a continuación.</string>
<string name="uploadSuccessLinkTitle">Su transferencia está lista</string>
<string name="uploadSuccessQrTitle">Su código QR está listo</string>
<string name="uploadSuccessTitle">¿Qué nos diferencia?</string>
<string name="uploadSuccessTransferInProgress">Transferencia en curso…</string>
<string name="urlAbout">https://www.infomaniak.com/es/about</string>
<string name="urlUserReportAndroid">https://feedback.userreport.com/1c462a20-7559-415e-a6e0-4b624dc38877/#ideas/popular</string>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@
<string name="uploadProgressDescriptionTemplateIndependence">Nous développons %s Sans compromis sur l’écologie,
la vie privée et l’humain.</string>
<string name="uploadErrorDescription">Le transfert n’a pas fonctionné, donnes-lui une autre chance. Si le problème persiste, contacte notre support.</string>
<string name="uploadErrorTitle">Qu’est-ce que quoi ?</string>
<string name="uploadProgressTitleArgument">différent</string>
<string name="uploadProgressTitleTemplate">Ce qui nous rend %s ?</string>
<plurals name="uploadSuccessEmailDescription">
<item quantity="one">Le lien de téléchargement a bien été envoyé au destinataire suivant :</item>
<item quantity="other">Le lien de téléchargement a été envoyé aux destinataires suivants :</item>
Expand All @@ -136,7 +138,6 @@
<string name="uploadSuccessLinkDescription">Fais scanner ce QR code autour de toi pour partager tes fichiers ou copie le lien ci-dessous.</string>
<string name="uploadSuccessLinkTitle">Ton transfert est prêt !</string>
<string name="uploadSuccessQrTitle">Ton QR code est prêt !</string>
<string name="uploadSuccessTitle">Ce qui nous rend différent ?</string>
<string name="uploadSuccessTransferInProgress">Transfert en cours…</string>
<string name="urlAbout">https://www.infomaniak.com/fr/a-propos</string>
<string name="urlUserReportAndroid">https://feedback.userreport.com/1c462a20-7559-415e-a6e0-4b624dc38877/#ideas/popular</string>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values-it/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@
<string name="uploadProgressDescriptionTemplateIndependence">Stiamo sviluppando %s Senza compromettere l’ecologia, la privacy e le persone.</string>
<string name="uploadErrorDescription">Il trasferimento non ha funzionato, da un’altra possibilità. Se il problema persiste, contatta il nostro team di assistenza.</string>
<string name="uploadErrorTitle">Che cosa che… cosa?</string>
<string name="uploadProgressTitleArgument">diversi</string>
<string name="uploadProgressTitleTemplate">Cosa ci rende %s?</string>
<plurals name="uploadSuccessEmailDescription">
<item quantity="one">Il link per il download è stato inviato al seguente destinatario:</item>
<item quantity="other">Il link per il download è stato inviato ai seguenti destinatari:</item>
Expand All @@ -134,7 +136,6 @@
<string name="uploadSuccessLinkDescription">Scansiona questo codice QR intorno a voi per condividere i vostri file o copiate il link qui sotto.</string>
<string name="uploadSuccessLinkTitle">Il trasferimento è pronto!</string>
<string name="uploadSuccessQrTitle">Il codice QR è pronto!</string>
<string name="uploadSuccessTitle">Cosa ci differenzia?</string>
<string name="uploadSuccessTransferInProgress">Trasferimento in corso…</string>
<string name="urlAbout">https://www.infomaniak.com/it/about</string>
<string name="urlUserReportAndroid">https://feedback.userreport.com/c85aa792-0f76-4923-8fe2-fae976cac9c2/#ideas/popular</string>
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@
<string name="uploadProgressDescriptionTemplateIndependence">We develop %s Without compromising on ecology, privacy and people.</string>
<string name="uploadErrorDescription">The transfer didn’t work, give it another chance. If the problem persists, contact our support team.</string>
<string name="uploadErrorTitle">Wait, what just happened?</string>
<string name="uploadProgressTitleArgument">apart</string>
<string name="uploadProgressTitleTemplate">What sets us %s?</string>
<plurals name="uploadSuccessEmailDescription">
<item quantity="one">The download link has been sent to the following recipient:</item>
<item quantity="other">The download link has been sent to the following recipients:</item>
Expand All @@ -139,7 +141,6 @@
<string name="uploadSuccessLinkDescription">Scan this QR code around you to share your files, or copy the link below.</string>
<string name="uploadSuccessLinkTitle">Your transfer is ready!</string>
<string name="uploadSuccessQrTitle">Your QR code is ready!</string>
<string name="uploadSuccessTitle">What sets us apart?</string>
<string name="uploadSuccessTransferInProgress">Transfer in progress…</string>
<string name="urlAbout">https://www.infomaniak.com/en/about</string>
<string name="urlUserReportAndroid">https://feedback.userreport.com/f12466ad-db5b-4f5c-b24c-a54b0a5117ca/#ideas/popular</string>
Expand Down
Loading