Skip to content

Commit

Permalink
OverwriteFiles setting introduced by #578
Browse files Browse the repository at this point in the history
  • Loading branch information
T8RIN committed Dec 31, 2023
1 parent be2ca85 commit 27f22a8
Show file tree
Hide file tree
Showing 15 changed files with 219 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ object Keys {
val DRAW_APPBAR_SHADOWS = booleanPreferencesKey("DRAW_APPBAR_SHADOWS")
val COPY_TO_CLIPBOARD = booleanPreferencesKey("COPY_TO_CLIPBOARD")
val VIBRATION_STRENGTH = intPreferencesKey("VIBRATION_STRENGTH")
val OVERWRITE_FILE = booleanPreferencesKey("OVERWRITE_FILE")
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import ru.tech.imageresizershrinker.data.keys.Keys.IMAGE_PICKER_MODE
import ru.tech.imageresizershrinker.data.keys.Keys.INVERT_THEME
import ru.tech.imageresizershrinker.data.keys.Keys.LOCK_DRAW_ORIENTATION
import ru.tech.imageresizershrinker.data.keys.Keys.NIGHT_MODE
import ru.tech.imageresizershrinker.data.keys.Keys.OVERWRITE_FILE
import ru.tech.imageresizershrinker.data.keys.Keys.PRESETS
import ru.tech.imageresizershrinker.data.keys.Keys.RANDOMIZE_FILENAME
import ru.tech.imageresizershrinker.data.keys.Keys.SAVE_FOLDER_URI
Expand Down Expand Up @@ -131,7 +132,8 @@ class SettingsRepositoryImpl @Inject constructor(
isInvertThemeColors = prefs[INVERT_THEME] ?: default.isInvertThemeColors,
screensSearchEnabled = prefs[SCREEN_SEARCH_ENABLED] ?: default.screensSearchEnabled,
autoCopyToClipBoard = prefs[COPY_TO_CLIPBOARD] ?: default.autoCopyToClipBoard,
hapticsStrength = prefs[VIBRATION_STRENGTH] ?: default.hapticsStrength
hapticsStrength = prefs[VIBRATION_STRENGTH] ?: default.hapticsStrength,
overwriteFiles = prefs[OVERWRITE_FILE] ?: default.overwriteFiles
)
}

Expand Down Expand Up @@ -456,7 +458,7 @@ class SettingsRepositoryImpl @Inject constructor(

override suspend fun toggleAutoPinClipboard() {
dataStore.edit {
val v = it[COPY_TO_CLIPBOARD] ?: true
val v = it[COPY_TO_CLIPBOARD] ?: false
it[COPY_TO_CLIPBOARD] = !v
}
}
Expand All @@ -467,6 +469,15 @@ class SettingsRepositoryImpl @Inject constructor(
}
}

override suspend fun toggleOverwriteFiles() {
dataStore.edit {
val v = it[OVERWRITE_FILE] ?: false
it[OVERWRITE_FILE] = !v

it[IMAGE_PICKER_MODE] = 2
}
}

private fun InputStream.toByteArray(): ByteArray {
val bytes = ByteArray(this.available())
val dis = DataInputStream(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import ru.tech.imageresizershrinker.data.keys.Keys.ADD_SEQ_NUM_TO_FILENAME
import ru.tech.imageresizershrinker.data.keys.Keys.ADD_SIZE_TO_FILENAME
import ru.tech.imageresizershrinker.data.keys.Keys.COPY_TO_CLIPBOARD
import ru.tech.imageresizershrinker.data.keys.Keys.FILENAME_PREFIX
import ru.tech.imageresizershrinker.data.keys.Keys.IMAGE_PICKER_MODE
import ru.tech.imageresizershrinker.data.keys.Keys.OVERWRITE_FILE
import ru.tech.imageresizershrinker.data.keys.Keys.RANDOMIZE_FILENAME
import ru.tech.imageresizershrinker.data.keys.Keys.SAVE_FOLDER_URI
import ru.tech.imageresizershrinker.domain.image.Metadata
Expand Down Expand Up @@ -63,7 +65,8 @@ class FileControllerImpl @Inject constructor(
addOriginalFilename = false,
addSequenceNumber = false,
randomizeFilename = false,
copyToClipBoard = false
copyToClipBoard = false,
overwriteFile = false
)

init {
Expand All @@ -77,6 +80,7 @@ class FileControllerImpl @Inject constructor(
addSequenceNumber = preferences[ADD_SEQ_NUM_TO_FILENAME] ?: true,
randomizeFilename = preferences[RANDOMIZE_FILENAME] ?: false,
copyToClipBoard = preferences[COPY_TO_CLIPBOARD] ?: false,
overwriteFile = preferences[OVERWRITE_FILE] ?: false
)
}
}
Expand Down Expand Up @@ -139,49 +143,15 @@ class FileControllerImpl @Inject constructor(
return SaveResult.Error.MissingPermissions
}

fileParams.treeUri.takeIf {
it != null
}?.let { treeUri ->
val hasDir: Boolean = treeUri.toUri().let {
DocumentFile.fromTreeUri(context, it)
}?.exists() == true

if (!hasDir) {
dataStore.edit {
it[SAVE_FOLDER_URI] = ""
}
return SaveResult.Error.Exception(
Exception(
context.getString(
R.string.no_such_directory,
treeUri.toUri().toUiPath(context, defaultPrefix())
)
)
)
}
}

var filename = ""

kotlin.runCatching {
var initialExif: ExifInterface? = null

val newSaveTarget = if (saveTarget is ImageSaveTarget<*>) {
initialExif = saveTarget.metadata as ExifInterface?

saveTarget.copy(
filename = constructImageFilename(saveTarget)
if (fileParams.copyToClipBoard) {
val clipboardManager = ContextCompat.getSystemService(
context,
ClipboardManager::class.java
)
} else saveTarget

filename = newSaveTarget.filename ?: ""

val clipboardManager = ContextCompat.getSystemService(
context,
ClipboardManager::class.java
)

if (fileParams.copyToClipBoard) {
cacheImage(saveTarget)?.toUri()?.let { uri ->
clipboardManager?.setPrimaryClip(
ClipData.newUri(
Expand All @@ -193,35 +163,85 @@ class FileControllerImpl @Inject constructor(
}
}

if (fileParams.overwriteFile) {
val originalUri = saveTarget.originalUri.toUri()
runCatching {
context.contentResolver.openFileDescriptor(originalUri, "w")
}.onFailure {
dataStore.edit {
it[IMAGE_PICKER_MODE] = 2
}
return SaveResult.Error.Exception(
Exception(
context.getString(
R.string.overwrite_file_requirements
)
)
)
}.getOrNull()?.use { parcel ->
filename = DocumentFile.fromSingleUri(context, originalUri)?.name.toString()
FileOutputStream(parcel.fileDescriptor).use { out ->
out.write(saveTarget.data)
copyMetadata(
initialExif = (saveTarget as? ImageSaveTarget<*>)?.metadata as ExifInterface?,
fileUri = originalUri,
keepMetadata = keepMetadata,
originalUri = originalUri
)
}
}
} else {
fileParams.treeUri.takeIf {
it != null
}?.let { treeUri ->
val hasDir: Boolean = treeUri.toUri().let {
DocumentFile.fromTreeUri(context, it)
}?.exists() == true

if (!hasDir) {
dataStore.edit {
it[SAVE_FOLDER_URI] = ""
}
return SaveResult.Error.Exception(
Exception(
context.getString(
R.string.no_such_directory,
treeUri.toUri().toUiPath(context, defaultPrefix())
)
)
)
}
}

var initialExif: ExifInterface? = null

val savingFolder = context.getSavingFolder(
treeUri = fileParams.treeUri?.takeIf { it.isNotEmpty() }?.toUri(),
saveTarget = newSaveTarget
)
val newSaveTarget = if (saveTarget is ImageSaveTarget<*>) {
initialExif = saveTarget.metadata as ExifInterface?

savingFolder.outputStream?.use {
it.write(saveTarget.data)
}
saveTarget.copy(
filename = constructImageFilename(saveTarget)
)
} else saveTarget

kotlin.runCatching {
if (initialExif != null) {
getFileDescriptorFor(savingFolder.fileUri)?.use {
val ex = ExifInterface(it.fileDescriptor)
initialExif.copyTo(ex)
ex.saveAttributes()
}
} else if (keepMetadata) {
val exif = context
.contentResolver
.openFileDescriptor(saveTarget.originalUri.toUri(), "r")
?.use { ExifInterface(it.fileDescriptor) }

getFileDescriptorFor(savingFolder.fileUri)?.use {
val ex = ExifInterface(it.fileDescriptor)
exif?.copyTo(ex)
ex.saveAttributes()
}
} else Unit
filename = newSaveTarget.filename ?: ""

val savingFolder = context.getSavingFolder(
treeUri = fileParams.treeUri?.takeIf { it.isNotEmpty() }?.toUri(),
saveTarget = newSaveTarget
)

savingFolder.outputStream?.use {
it.write(saveTarget.data)
}

kotlin.runCatching {
copyMetadata(
initialExif = initialExif,
fileUri = savingFolder.fileUri,
keepMetadata = keepMetadata,
originalUri = saveTarget.originalUri.toUri()
)
}
}
}.let { result ->
if (result.isFailure) {
Expand All @@ -232,6 +252,32 @@ class FileControllerImpl @Inject constructor(
}
}

private fun copyMetadata(
initialExif: ExifInterface?,
fileUri: Uri?,
keepMetadata: Boolean,
originalUri: Uri
) {
if (initialExif != null) {
getFileDescriptorFor(fileUri)?.use {
val ex = ExifInterface(it.fileDescriptor)
initialExif.copyTo(ex)
ex.saveAttributes()
}
} else if (keepMetadata) {
val exif = context
.contentResolver
.openFileDescriptor(originalUri, "r")
?.use { ExifInterface(it.fileDescriptor) }

getFileDescriptorFor(fileUri)?.use {
val ex = ExifInterface(it.fileDescriptor)
exif?.copyTo(ex)
ex.saveAttributes()
}
} else Unit
}

private suspend fun cacheImage(
saveTarget: SaveTarget
): String? = withContext(Dispatchers.IO) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ data class SettingsState(
val isInvertThemeColors: Boolean,
val screensSearchEnabled: Boolean,
val autoCopyToClipBoard: Boolean,
val hapticsStrength: Int
val hapticsStrength: Int,
val overwriteFiles: Boolean
) : Domain {

companion object {
Expand Down Expand Up @@ -90,7 +91,8 @@ data class SettingsState(
isInvertThemeColors = false,
screensSearchEnabled = false,
autoCopyToClipBoard = false,
hapticsStrength = 1
hapticsStrength = 1,
overwriteFiles = false
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,6 @@ interface SettingsRepository {
suspend fun toggleAutoPinClipboard()

suspend fun setVibrationStrength(strength: Int)

suspend fun toggleOverwriteFiles()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ data class FileParams(
val addOriginalFilename: Boolean,
val addSequenceNumber: Boolean,
val randomizeFilename: Boolean,
val copyToClipBoard: Boolean
val copyToClipBoard: Boolean,
val overwriteFile: Boolean
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.tech.imageresizershrinker.domain.use_case.edit_settings

import ru.tech.imageresizershrinker.domain.repository.SettingsRepository
import javax.inject.Inject

class ToggleOverwriteFilesUseCase @Inject constructor(
private val settingsRepository: SettingsRepository
) {
suspend operator fun invoke() = settingsRepository.toggleAddOriginalFilename()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ru.tech.imageresizershrinker.presentation.main_screen.components.settings

import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Architecture
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import ru.tech.imageresizershrinker.R
import ru.tech.imageresizershrinker.presentation.root.widget.modifier.ContainerShapeDefaults
import ru.tech.imageresizershrinker.presentation.root.widget.preferences.PreferenceRowSwitch
import ru.tech.imageresizershrinker.presentation.root.widget.utils.LocalSettingsState

@Composable
fun OverwriteFilesSettingItem(
onClick: (Boolean) -> Unit,
shape: Shape = ContainerShapeDefaults.centerShape,
modifier: Modifier = Modifier.padding(start = 8.dp, end = 8.dp)
) {
val settingsState = LocalSettingsState.current
PreferenceRowSwitch(
shape = shape,
modifier = modifier,
applyHorPadding = false,
onClick = onClick,
enabled = !settingsState.randomizeFilename,
title = stringResource(R.string.overwrite_files),
subtitle = stringResource(R.string.overwrite_files_sub),
checked = settingsState.addSequenceNumber,
resultModifier = Modifier.padding(
horizontal = 16.dp,
vertical = 8.dp
),
startContent = {
Icon(
imageVector = Icons.Outlined.Architecture,
contentDescription = null,
modifier = Modifier.padding(end = 16.dp)
)
}
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,9 @@ sealed class Setting(
title = R.string.vibration_strength,
subtitle = null
)

data object OverwriteFiles : Setting(
title = R.string.overwrite_files,
subtitle = R.string.overwrite_files_sub
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ fun SettingItem(
)
}

Setting.OverwriteFiles -> {
OverwriteFilesSettingItem(
onClick = { viewModel.toggleOverwriteFiles() }
)
}

Setting.Reset -> {
ResetSettingsSettingItem(
onReset = viewModel::resetSettings
Expand Down
Loading

0 comments on commit 27f22a8

Please sign in to comment.