Skip to content

Commit

Permalink
Merge pull request #87 from Infomaniak/display-picked-files
Browse files Browse the repository at this point in the history
Adds and use the component to display picked files during a new transfer
  • Loading branch information
LunarX authored Oct 18, 2024
2 parents a79128f + da57824 commit 626df63
Show file tree
Hide file tree
Showing 21 changed files with 765 additions and 200 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
alias(libs.plugins.kapt)
alias(libs.plugins.hilt)
alias(libs.plugins.sentry)
kotlin("plugin.parcelize")
kotlin("plugin.serialization") version libs.versions.kotlin
}

Expand Down Expand Up @@ -69,7 +70,7 @@ sentry {
// If you disable this, you'll need to manually upload the mapping files with sentry-cli when you do a release.
// Default is enabled.
autoUploadProguardMapping = true

// Does or doesn't include the source code of native code for Sentry.
// This executes sentry-cli with the --include-sources param. automatically so you don't need to do it manually.
// Default is disabled.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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 android.content.res.Configuration
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.images.AppImages
import com.infomaniak.swisstransfer.ui.images.icons.CrossThick
import com.infomaniak.swisstransfer.ui.theme.Margin
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme

@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun BoxScope.CrossCircleButton(onClick: (() -> Unit)?, size: Dp = 48.dp) {
val buttonPadding = ((size - 24.dp) / 2f).coerceAtLeast(0.dp)

CompositionLocalProvider(LocalRippleConfiguration provides RippleConfiguration(color = Color.White)) {
Button(
modifier = Modifier
.size(size)
.padding(buttonPadding)
.align(Alignment.TopEnd),
contentPadding = PaddingValues(0.dp),
shape = CircleShape,
colors = ButtonDefaults.buttonColors(containerColor = SwissTransferTheme.colors.fileItemRemoveButtonBackground),
onClick = onClick ?: {},
) {
Icon(
modifier = Modifier.size(Margin.Small),
imageVector = AppImages.AppIcons.CrossThick,
contentDescription = stringResource(R.string.contentDescriptionButtonRemove),
tint = Color.White,
)
}
}
}


@Preview(name = "Light")
@Preview(name = "Dark", uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL)
@Composable
private fun CrossCircleButtonPreview() {
SwissTransferTheme {
Surface {
Box {
CrossCircleButton({})
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,21 @@
*/
package com.infomaniak.swisstransfer.ui.components

import android.net.Uri
import android.text.format.Formatter
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.infomaniak.library.filetypes.FileType
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.images.AppImages
import com.infomaniak.swisstransfer.ui.images.icons.CrossThick
import com.infomaniak.swisstransfer.ui.theme.CustomShapes
import com.infomaniak.swisstransfer.ui.theme.LocalIsDarkMode
import com.infomaniak.swisstransfer.ui.theme.Margin
import com.infomaniak.swisstransfer.ui.theme.SwissTransferTheme
import com.infomaniak.swisstransfer.ui.utils.PreviewAllWindows
import com.infomaniak.swisstransfer.ui.utils.fileType
import com.infomaniak.swisstransfer.ui.utils.hasPreview

// TODO: Get the interface from the shared kmp code
interface FileUiItem {
Expand All @@ -70,13 +53,11 @@ fun FileItem(
) {
FileItemContent(
content = {
var displayPreview by rememberSaveable { mutableStateOf(file.hasPreview) }

if (displayPreview) {
FileThumbnail(file.uri.toUri(), onError = { displayPreview = false })
} else {
FileIcon(file.fileType)
}
FilePreview(
file = file,
circleColor = SwissTransferTheme.materialColors.surface,
circleSize = 64.dp,
)
},
onClick = onClick,
isCheckboxVisible = isCheckboxVisible,
Expand Down Expand Up @@ -124,25 +105,7 @@ private fun FileItemContent(
)
}

if (isRemoveButtonVisible) {
Button(
modifier = Modifier
.size(Margin.XXLarge)
.padding(12.dp)
.align(Alignment.TopEnd),
contentPadding = PaddingValues(0.dp),
shape = CircleShape,
colors = ButtonDefaults.buttonColors(containerColor = SwissTransferTheme.colors.fileItemRemoveButtonBackground),
onClick = onRemove ?: {},
) {
Icon(
modifier = Modifier.size(Margin.Small),
imageVector = AppImages.AppIcons.CrossThick,
contentDescription = stringResource(R.string.contentDescriptionButtonRemove),
tint = Color.White
)
}
}
if (isRemoveButtonVisible) CrossCircleButton(onClick = onRemove)
}

Column(Modifier.padding(Margin.Small)) {
Expand All @@ -164,37 +127,6 @@ private fun FileItemContent(
}
}

@Composable
private fun FileThumbnail(uri: Uri, onError: () -> Unit) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(uri)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
onError = { onError() },
modifier = Modifier.fillMaxSize(),
)
}

@Composable
private fun FileIcon(fileType: FileType) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
val surfaceColor = SwissTransferTheme.materialColors.surface
Canvas(modifier = Modifier.size(64.dp)) {
drawCircle(color = surfaceColor)
}

Icon(
modifier = Modifier.size(32.dp),
imageVector = fileType.icon,
contentDescription = null,
tint = fileType.color(LocalIsDarkMode.current)
)
}
}

@PreviewAllWindows
@Composable
private fun FileItemPreview() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* 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.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.infomaniak.library.filetypes.FileType
import com.infomaniak.swisstransfer.ui.theme.LocalIsDarkMode
import com.infomaniak.swisstransfer.ui.utils.fileType
import com.infomaniak.swisstransfer.ui.utils.hasPreview

@Composable
fun FilePreview(
file: FileUiItem,
circleColor: Color,
circleSize: Dp,
) {
var displayPreview by rememberSaveable { mutableStateOf(file.hasPreview) }

if (displayPreview) {
FileThumbnail(file.uri, onError = { displayPreview = false })
} else {
FileIcon(file.fileType, circleColor, circleSize)
}
}

@Composable
private fun FileThumbnail(uri: String, onError: () -> Unit) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(uri)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
onError = { onError() },
modifier = Modifier.fillMaxSize(),
)
}

@Composable
private fun FileIcon(fileType: FileType, color: Color, circleSize: Dp) {
Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
Canvas(modifier = Modifier.size(circleSize)) {
drawCircle(color = color)
}

Icon(
modifier = Modifier.size(circleSize / 2),
imageVector = fileType.icon,
contentDescription = null,
tint = fileType.color(LocalIsDarkMode.current),
)
}
}
Loading

0 comments on commit 626df63

Please sign in to comment.