Skip to content

Commit

Permalink
Merge pull request #12 from MateusRodCosta/dev
Browse files Browse the repository at this point in the history
Version 1.0.0
  • Loading branch information
MateusRodCosta authored Jan 11, 2024
2 parents 3e1a467 + 9ddaece commit 8bb0ad7
Show file tree
Hide file tree
Showing 44 changed files with 1,408 additions and 337 deletions.
7 changes: 3 additions & 4 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ android {
minSdk = 26
//noinspection OldTargetApi
targetSdk = 33
versionCode = 13
versionName = "0.9.0"
versionCode = 14
versionName = "1.0.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -86,8 +86,7 @@ dependencies {

implementation(libs.bundles.compose)
debugImplementation(libs.bundles.compose.debug)

implementation(libs.androidx.activity.compose)
implementation(libs.bundles.androidx.compose.integration)

testImplementation(libs.bundles.testing)
androidTestImplementation(libs.bundles.ui.testing)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

<activity
android:name=".SettingsActivity"
android:exported="true" />
android:exported="false" />

<activity
android:name=".DetailsActivity"
Expand Down
Binary file modified app/src/main/ic_launcher-playstore.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 - 2023 Mateus Rodrigues Costa
* Copyright (C) 2022 - 2024 Mateus Rodrigues Costa
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
Expand All @@ -21,24 +21,27 @@ import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.format.Formatter
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
Expand All @@ -49,38 +52,61 @@ import androidx.compose.material.icons.outlined.VideoFile
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import androidx.documentfile.provider.DocumentFile
import androidx.preference.PreferenceManager
import com.mateusrodcosta.apps.share2storage.model.SampleUriDataProvider
import com.mateusrodcosta.apps.share2storage.model.UriData
import com.mateusrodcosta.apps.share2storage.theme.AppTheme
import com.mateusrodcosta.apps.share2storage.ui.theme.AppTheme
import com.mateusrodcosta.apps.share2storage.utils.AppBasicDivider
import com.mateusrodcosta.apps.share2storage.utils.CreateDocumentWithInitialUri
import com.mateusrodcosta.apps.share2storage.utils.SharedPreferenceKeys
import com.mateusrodcosta.apps.share2storage.utils.getUriData
import com.mateusrodcosta.apps.share2storage.utils.saveFile
import com.mateusrodcosta.apps.share2storage.utils.spSkipFileDetailsKey

class DetailsActivity : ComponentActivity() {

private var createFile: ActivityResultLauncher<String>? = null
private var windowSizeClass: WindowSizeClass? = null

@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)

val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
val skipFileDetails = sharedPreferences.getBoolean(spSkipFileDetailsKey, false)
val skipFileDetails =
sharedPreferences.getBoolean(SharedPreferenceKeys.skipFileDetailsKey, false)
val defaultSaveLocationRaw =
sharedPreferences.getString(SharedPreferenceKeys.defaultSaveLocationKey, null)
Log.d("details] defaultSaveLocationRaw", defaultSaveLocationRaw.toString())
val defaultSaveLocation = if (defaultSaveLocationRaw != null) {
val parsedUri = Uri.parse(defaultSaveLocationRaw)
val file = DocumentFile.fromTreeUri(this, parsedUri)
file?.uri
} else {
null
}
Log.d("details] defaultSaveLocation", defaultSaveLocation.toString())

var uriData: UriData? = null

Expand All @@ -97,60 +123,80 @@ class DetailsActivity : ComponentActivity() {
uriData = getUriData(contentResolver, fileUri)

if (uriData != null) {
createFile = registerForActivityResult(CreateDocument(uriData.type ?: "*/*"),
fun(uri: Uri?) {
if (uri == null || fileUri == null) return
val isSuccess = saveFile(baseContext, uri, fileUri)
Toast.makeText(
baseContext, if (isSuccess) {
R.string.toast_saved_file_success
} else {
R.string.toast_saved_file_failure
}, Toast.LENGTH_LONG
).show()
if (skipFileDetails) finish()
})
createFile = registerForActivityResult(
CreateDocumentWithInitialUri(uriData.type ?: "*/*", defaultSaveLocation)
) { uri ->
if (uri == null || fileUri == null) return@registerForActivityResult
val isSuccess = saveFile(baseContext, uri, fileUri)
Toast.makeText(
baseContext, if (isSuccess) {
R.string.toast_saved_file_success
} else {
R.string.toast_saved_file_failure
}, Toast.LENGTH_LONG
).show()
if (skipFileDetails) finish()
}
}
}

setContent { DetailsScreen(uriData) }
setContent {
windowSizeClass = calculateWindowSizeClass(this)
DetailsScreen(uriData)
}

if (skipFileDetails) createFile?.launch(uriData?.displayName ?: "")
if (skipFileDetails) createFile!!.launch(uriData?.displayName ?: "")
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
@Preview
fun DetailsScreen(@PreviewParameter(SampleUriDataProvider::class) uriData: UriData?) {
val widthSizeClass = windowSizeClass?.widthSizeClass ?: WindowWidthSizeClass.Compact
val heightSizeClass = windowSizeClass?.heightSizeClass ?: WindowHeightSizeClass.Medium
AppTheme {
Scaffold(topBar = {
TopAppBar(title = { Text(stringResource(R.string.file_details)) })
}, floatingActionButton = {
FloatingActionButton(onClick = {
createFile?.launch(uriData?.displayName ?: "")
}, content = {
Image(
imageVector = Icons.Rounded.Download,
contentDescription = stringResource(R.string.save_button)
TopAppBar(
title = { Text(stringResource(R.string.file_details)) },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primary,
titleContentColor = MaterialTheme.colorScheme.onPrimary,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
),
)
}, floatingActionButton =

{
if (uriData != null) {
FloatingActionButton(
onClick = {
createFile!!.launch(uriData.displayName ?: "")
},
content = {
Icon(
Icons.Rounded.Download,
contentDescription = stringResource(R.string.save_button)
)
},
containerColor = MaterialTheme.colorScheme.secondary,
contentColor = MaterialTheme.colorScheme.onSecondary,
)
})
}
}) { paddingValues ->
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
val showLandscapePhone = heightSizeClass == WindowHeightSizeClass.Compact
val showLandscapeTablet = widthSizeClass == WindowWidthSizeClass.Expanded
if (uriData != null) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
FilePreview(mimeType = uriData.type ?: "*/*")
FileInfo(uriData)
if (showLandscapePhone || showLandscapeTablet) {
FileDetailsLandscape(uriData)
} else {
FileDetailsPortrait(uriData)
}
} else Text(
stringResource(R.string.no_file_found),
Expand All @@ -161,9 +207,43 @@ class DetailsActivity : ComponentActivity() {
}
}

@Composable
fun FileDetailsPortrait(uriData: UriData) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
Box(modifier = Modifier.weight(1.0f)) {
FilePreview(uriData)
}
Box(modifier = Modifier.weight(1.0f)) {
FileInfo(uriData)
}
}
}

@Composable
fun FileDetailsLandscape(uriData: UriData) {
Row(
modifier = Modifier.fillMaxSize(),
) {
Box(modifier = Modifier.weight(1.0f)) {
FilePreview(uriData)
}
Box(modifier = Modifier.weight(1.0f)) {
FileInfo(uriData)
}
}
}

@Composable
fun FileInfo(uriData: UriData) {
Column {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
AppBasicDivider()
FileInfoLine(
label = stringResource(R.string.file_name),
Expand All @@ -178,7 +258,7 @@ class DetailsActivity : ComponentActivity() {
FileInfoLine(
label = stringResource(R.string.file_size),
// TODO: Find code to calculate file size for previews
content = if (uriData.size != null && baseContext != null) android.text.format.Formatter.formatFileSize(
content = if (uriData.size != null && baseContext != null) Formatter.formatFileSize(
baseContext, uriData.size
) else stringResource(R.string.unknown)
)
Expand All @@ -204,8 +284,9 @@ class DetailsActivity : ComponentActivity() {
}

@Composable
fun FilePreview(mimeType: String) {
val fileIcon = if (mimeType.startsWith("image/")) {
fun FilePreview(uriData: UriData) {
val mimeType = uriData.type ?: "*/*"
val fallbackFileIcon = if (mimeType.startsWith("image/")) {
Icons.Outlined.Image
} else if (mimeType.startsWith("audio/")) {
Icons.Outlined.AudioFile
Expand All @@ -215,12 +296,28 @@ class DetailsActivity : ComponentActivity() {
Icons.Outlined.Description
}

Image(
modifier = Modifier.scale(5.0f),
imageVector = fileIcon,
contentDescription = stringResource(R.string.app_name),
contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.tertiary)
)
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
if (uriData.previewImage != null) {
Image(
modifier = Modifier.align(Alignment.Center),
bitmap = uriData.previewImage.asImageBitmap(),
contentDescription = stringResource(R.string.app_name),
contentScale = ContentScale.Fit,
)
} else {
Icon(
modifier = Modifier
.size(128.dp)
.align(Alignment.Center),
imageVector = fallbackFileIcon,
contentDescription = stringResource(R.string.app_name),
tint = MaterialTheme.colorScheme.tertiary
)
}
}
}
}
Loading

0 comments on commit 8bb0ad7

Please sign in to comment.