Skip to content

Commit f270605

Browse files
committed
Remove the support for Base64 because of its excessive size
1 parent 031361c commit f270605

4 files changed

Lines changed: 90 additions & 97 deletions

File tree

‎component-key/src/main/java/xyz/aprildown/timer/component/key/ImageActionMapper.kt‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,14 @@ import coil.map.Mapper
55
import coil.request.Options
66
import xyz.aprildown.timer.domain.entities.ImageAction
77
import xyz.aprildown.timer.domain.entities.ResourceContentType
8-
import xyz.aprildown.timer.domain.utils.Base64BitmapConverter.decodeBitmapByteArrayFromBase64
98
import java.io.File
10-
import java.nio.ByteBuffer
119

1210
class ImageActionMapper : Mapper<ImageAction, Any> {
13-
override fun map(data: ImageAction, options: Options): Any? {
11+
override fun map(data: ImageAction, options: Options): Any {
1412
return when (data.type) {
1513
ResourceContentType.CanonicalPath -> File(data.data)
1614
ResourceContentType.RelativePath -> error("Relative path can't be loaded")
1715
ResourceContentType.Uri -> data.data.toUri()
18-
ResourceContentType.Base64 -> {
19-
ByteBuffer.wrap(data.data.decodeBitmapByteArrayFromBase64())
20-
}
2116
}
2217
}
2318
}

‎data/src/main/java/xyz/aprildown/timer/data/repositories/TimerRepositoryImpl.kt‎

Lines changed: 89 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package xyz.aprildown.timer.data.repositories
22

3+
import android.content.ContentResolver
34
import android.content.Context
4-
import android.graphics.BitmapFactory
55
import android.webkit.MimeTypeMap
66
import androidx.core.net.toUri
77
import dagger.Reusable
@@ -21,8 +21,6 @@ import xyz.aprildown.timer.domain.entities.TimerInfo
2121
import xyz.aprildown.timer.domain.entities.inferResourcesContentType
2222
import xyz.aprildown.timer.domain.entities.toImageAction
2323
import xyz.aprildown.timer.domain.repositories.TimerRepository
24-
import xyz.aprildown.timer.domain.utils.Base64BitmapConverter.decodeBitmapByteArrayFromBase64
25-
import xyz.aprildown.timer.domain.utils.Base64BitmapConverter.encodeToBase64
2624
import xyz.aprildown.timer.domain.utils.ensureDirExistence
2725
import xyz.aprildown.timer.domain.utils.ensureNewFile
2826
import java.io.File
@@ -39,7 +37,9 @@ internal class TimerRepositoryImpl @Inject constructor(
3937
private val imageManager by lazy { TimerImageManager(context) }
4038

4139
override suspend fun items(): List<TimerEntity> {
42-
return timerDao.getTimers().map { timerMapper.mapFrom(it).withImageCanonicalPath() }
40+
return timerDao.getTimers().map {
41+
timerMapper.mapFrom(it).withImageCanonicalPath()
42+
}
4343
}
4444

4545
override suspend fun item(id: Int): TimerEntity? {
@@ -50,7 +50,13 @@ internal class TimerRepositoryImpl @Inject constructor(
5050

5151
private fun TimerEntity.withImageCanonicalPath(): TimerEntity {
5252
return transformImageActions { data ->
53-
ImageAction(data = data.convertImageDataToCanonicalPath(id))
53+
ImageAction(
54+
data = data.convertImageData(
55+
id = id,
56+
imageManager = imageManager,
57+
targetType = ResourceContentType.CanonicalPath,
58+
),
59+
)
5460
}
5561
}
5662

@@ -60,8 +66,13 @@ internal class TimerRepositoryImpl @Inject constructor(
6066
timerMapper.mapTo(
6167
item.transformImageActions {
6268
ImageAction(
63-
data = imageManager.save(timerId = item.id, data = it)
64-
.convertImageCanonicalPathToDataType(ResourceContentType.RelativePath)
69+
data = imageManager
70+
.save(id = item.id, data = it)
71+
.convertImageData(
72+
id = item.id,
73+
imageManager = imageManager,
74+
targetType = ResourceContentType.RelativePath,
75+
),
6576
)
6677
}
6778
)
@@ -75,11 +86,14 @@ internal class TimerRepositoryImpl @Inject constructor(
7586
}
7687

7788
override suspend fun save(item: TimerEntity): Boolean {
78-
val savedItem = imageManager.trackSaving(timerId = item.id) {
89+
val savedItem = imageManager.trackSaving(id = item.id) {
7990
item.transformImageActions {
8091
ImageAction(
81-
data = save(data = it)
82-
.convertImageCanonicalPathToDataType(ResourceContentType.RelativePath)
92+
data = save(data = it).convertImageData(
93+
id = item.id,
94+
imageManager = imageManager,
95+
targetType = ResourceContentType.RelativePath,
96+
),
8397
)
8498
}
8599
}
@@ -150,37 +164,27 @@ internal class TimerRepositoryImpl @Inject constructor(
150164
return timers.map { timer ->
151165
timer.transformImageActions { data ->
152166
ImageAction(
153-
data = data.convertImageDataToCanonicalPath(timerId = timer.id)
154-
.convertImageCanonicalPathToDataType(type = type),
167+
data = data.convertImageData(
168+
id = timer.id,
169+
imageManager = imageManager,
170+
targetType = type,
171+
)
155172
)
156173
}
157174
}
158175
}
159-
160-
private fun String.convertImageDataToCanonicalPath(timerId: Int): String {
161-
return imageManager.save(timerId = timerId, data = this)
162-
}
163-
164-
private fun String.convertImageCanonicalPathToDataType(type: ResourceContentType): String {
165-
return when (type) {
166-
ResourceContentType.CanonicalPath -> this
167-
ResourceContentType.RelativePath -> File(this).name
168-
ResourceContentType.Uri -> error("Unsupported")
169-
ResourceContentType.Base64 -> {
170-
BitmapFactory.decodeFile(this).encodeToBase64(quality = 90)
171-
}
172-
}
173-
}
174176
}
175177

176-
private class TimerImageManager(private val context: Context) {
178+
private class TimerImageManager(context: Context) {
177179
interface TrackingScope {
178180
/**
179181
* [TimerImageManager.save]
180182
*/
181183
fun save(data: String): String
182184
}
183185

186+
val contentResolver: ContentResolver = context.contentResolver
187+
184188
private val imageFolder = File(context.filesDir, FOLDER)
185189

186190
fun getTimerFolder(timerId: Int): File {
@@ -190,10 +194,10 @@ private class TimerImageManager(private val context: Context) {
190194
/**
191195
* @return [ResourceContentType.RelativePath], [File.getCanonicalPath]
192196
*/
193-
fun save(timerId: Int, data: String): String {
197+
fun save(id: Int, data: String): String {
194198
imageFolder.ensureDirExistence()
195199

196-
val timerFolder = getTimerFolder(timerId)
200+
val timerFolder = getTimerFolder(id)
197201
val imageFile: File
198202
when (data.inferResourcesContentType()) {
199203
ResourceContentType.CanonicalPath -> {
@@ -202,8 +206,8 @@ private class TimerImageManager(private val context: Context) {
202206
imageFile = existingFile
203207
} else {
204208
imageFile = File(
205-
imageFolder.ensureDirExistence(),
206-
"${UUID.randomUUID()}.${existingFile.extension}"
209+
timerFolder.ensureDirExistence(),
210+
"${generateImageName()}.${existingFile.extension}"
207211
).ensureNewFile()
208212
imageFile.inputStream().use { inputStream ->
209213
existingFile.outputStream().use { outputStream ->
@@ -218,45 +222,40 @@ private class TimerImageManager(private val context: Context) {
218222
ResourceContentType.Uri -> {
219223
val uri = data.toUri()
220224
val extension = MimeTypeMap.getSingleton()
221-
.getExtensionFromMimeType(context.contentResolver.getType(uri)) ?: "jpg"
222-
imageFile =
223-
File(timerFolder.ensureDirExistence(), "${UUID.randomUUID()}.$extension")
224-
.ensureNewFile()
225-
context.contentResolver.openInputStream(uri)?.use { inputStream ->
225+
.getExtensionFromMimeType(contentResolver.getType(uri)) ?: "jpg"
226+
imageFile = File(
227+
timerFolder.ensureDirExistence(),
228+
"${generateImageName()}.$extension"
229+
).ensureNewFile()
230+
contentResolver.openInputStream(uri)?.use { inputStream ->
226231
imageFile.outputStream().use { outputStream ->
227232
inputStream.copyTo(outputStream)
228233
}
229234
}
230-
require(imageFile.exists() && imageFile.length() > 0L)
231235
imageFile.canonicalPath
232236
}
233-
ResourceContentType.Base64 -> {
234-
imageFile = File(timerFolder.ensureDirExistence(), "${UUID.randomUUID()}.webp")
235-
.ensureNewFile()
236-
val byteArray = data.decodeBitmapByteArrayFromBase64()
237-
imageFile.outputStream().use {
238-
it.write(byteArray)
239-
it.flush()
240-
}
241-
}
242237
}
243238
require(imageFile.exists() && imageFile.length() > 0L)
244239
return imageFile.canonicalPath
245240
}
246241

247-
fun <T> trackSaving(timerId: Int, block: TrackingScope.() -> T): T {
242+
private fun generateImageName(): String {
243+
return UUID.randomUUID().toString()
244+
}
245+
246+
fun <T> trackSaving(id: Int, block: TrackingScope.() -> T): T {
248247
val saved = mutableListOf<String>()
249248
val scope = object : TrackingScope {
250249
override fun save(data: String): String {
251-
val savedData = save(timerId = timerId, data = data)
250+
val savedData = save(id = id, data = data)
252251
saved += savedData
253252
return savedData
254253
}
255254
}
256255
return try {
257256
block(scope)
258257
} finally {
259-
val timerFolder = getTimerFolder(timerId)
258+
val timerFolder = getTimerFolder(id)
260259
if (timerFolder.exists()) {
261260
timerFolder.listFiles()?.toList()?.forEach { file ->
262261
if (file.canonicalPath !in saved) {
@@ -267,8 +266,8 @@ private class TimerImageManager(private val context: Context) {
267266
}
268267
}
269268

270-
fun delete(timerId: Int) {
271-
val timerFolder = getTimerFolder(timerId)
269+
fun delete(id: Int) {
270+
val timerFolder = getTimerFolder(id)
272271
if (timerFolder.exists()) {
273272
timerFolder.deleteRecursively()
274273
}
@@ -278,3 +277,41 @@ private class TimerImageManager(private val context: Context) {
278277
private const val FOLDER = "timer_images"
279278
}
280279
}
280+
281+
private fun String.convertImageData(
282+
id: Int = TimerEntity.NULL_ID,
283+
imageManager: TimerImageManager,
284+
targetType: ResourceContentType,
285+
): String {
286+
fun String.save(): String {
287+
return imageManager.save(id = id, data = this)
288+
}
289+
290+
fun String.convert(targetType: ResourceContentType): String {
291+
return convertImageData(id = id, imageManager = imageManager, targetType)
292+
}
293+
294+
return when (inferResourcesContentType()) {
295+
ResourceContentType.CanonicalPath -> {
296+
when (targetType) {
297+
ResourceContentType.CanonicalPath -> save()
298+
ResourceContentType.RelativePath -> File(this).name
299+
ResourceContentType.Uri -> error("Unable to convert CanonicalPath to Uri")
300+
}
301+
}
302+
ResourceContentType.RelativePath -> {
303+
when (targetType) {
304+
ResourceContentType.CanonicalPath -> save()
305+
ResourceContentType.RelativePath -> return this
306+
ResourceContentType.Uri -> error("Unable to convert RelativePath to Uri")
307+
}
308+
}
309+
ResourceContentType.Uri -> {
310+
when (targetType) {
311+
ResourceContentType.CanonicalPath -> save()
312+
ResourceContentType.RelativePath -> save().convert(ResourceContentType.RelativePath)
313+
ResourceContentType.Uri -> return this
314+
}
315+
}
316+
}
317+
}

‎domain/src/main/java/xyz/aprildown/timer/domain/entities/TimerEntity.kt‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package xyz.aprildown.timer.domain.entities
22

33
import android.content.ContentResolver
44
import okio.Path
5-
import xyz.aprildown.timer.domain.utils.Base64BitmapConverter
65

76
/**
87
* Whenever you add a new more item, you need to check
@@ -36,14 +35,12 @@ enum class ResourceContentType {
3635
CanonicalPath,
3736
RelativePath,
3837
Uri,
39-
Base64;
4038
}
4139

4240
fun String.inferResourcesContentType(): ResourceContentType {
4341
return when {
4442
startsWith(Path.DIRECTORY_SEPARATOR) -> ResourceContentType.CanonicalPath
4543
startsWith(ContentResolver.SCHEME_CONTENT) -> ResourceContentType.Uri
46-
startsWith(Base64BitmapConverter.PREFIX) -> ResourceContentType.Base64
4744
else -> ResourceContentType.RelativePath
4845
}
4946
}

‎domain/src/main/java/xyz/aprildown/timer/domain/utils/Base64BitmapConverter.kt‎

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)