Skip to content

Commit 2904a59

Browse files
committed
Release version 1.1.0
1 parent b55063e commit 2904a59

File tree

64 files changed

+1942
-1672
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1942
-1672
lines changed

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

REUSE.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ version = 1
88
path = [
99
"android/src/main/**/ic_launcher*.png",
1010
".idea/**",
11-
".detekt/detekt-baseline.xml"
11+
".detekt/detekt-baseline.xml",
12+
".github/ISSUE_TEMPLATE/**",
13+
"gradle/wrapper/gradle-wrapper.properties"
1214
]
1315
SPDX-FileCopyrightText = "2022 Lukas Pieper"
1416
SPDX-License-Identifier = "GPL-3.0-or-later"

android/build.gradle.kts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ android {
2424

2525
defaultConfig {
2626
minSdk = 29
27-
targetSdk = 34
28-
versionCode = 15
29-
versionName = "1.0.1"
27+
targetSdk = 35
28+
versionCode = 16
29+
versionName = "1.1.0"
3030

3131
ndk {
3232
// Tink does not support 32-bit architectures (https://developers.google.com/tink/faq/support_for_32bit)

android/src/main/java/de/lukaspieper/truvark/data/io/AndroidFileSystem.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,17 +280,17 @@ class AndroidFileSystem(private val context: Context) : FileSystem() {
280280
* A simple wrapper around [ContentResolver.query] that automatically closes the cursor. Note that `selection` is
281281
* not available because [Android's FileSystemProvider does not support it](https://stackoverflow.com/a/61214849).
282282
*/
283-
inline fun <R> ContentResolver.query(uri: Uri, projection: Array<String>, block: (Cursor?) -> R): R {
283+
private inline fun <R> ContentResolver.query(uri: Uri, projection: Array<String>, block: (Cursor?) -> R): R {
284284
return query(uri, projection, null, null, null).use(block)
285285
}
286286

287287
@Throws(IllegalArgumentException::class)
288-
fun Cursor.getString(columnName: String): String {
288+
private fun Cursor.getString(columnName: String): String {
289289
return getString(getColumnIndexOrThrow(columnName))
290290
}
291291

292292
@Throws(IllegalArgumentException::class)
293-
fun Cursor.getLong(columnName: String): Long {
293+
private fun Cursor.getLong(columnName: String): Long {
294294
return getLong(getColumnIndexOrThrow(columnName))
295295
}
296296

android/src/main/java/de/lukaspieper/truvark/domain/AndroidThumbnailProvider.kt

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ package de.lukaspieper.truvark.domain
88

99
import android.content.Context
1010
import android.graphics.Bitmap
11-
import android.graphics.drawable.BitmapDrawable
12-
import coil.ImageLoader
13-
import coil.decode.SvgDecoder
14-
import coil.decode.VideoFrameDecoder
15-
import coil.request.CachePolicy
16-
import coil.request.ErrorResult
17-
import coil.request.ImageRequest
18-
import coil.request.SuccessResult
19-
import coil.request.videoFramePercent
11+
import coil3.ImageLoader
12+
import coil3.imageDecoderEnabled
13+
import coil3.request.CachePolicy
14+
import coil3.request.ErrorResult
15+
import coil3.request.ImageRequest
16+
import coil3.request.SuccessResult
17+
import coil3.request.allowConversionToBitmap
18+
import coil3.toBitmap
19+
import coil3.video.videoFramePercent
2020
import de.lukaspieper.truvark.common.data.io.FileInfo
2121
import de.lukaspieper.truvark.common.domain.ThumbnailProvider
2222
import de.lukaspieper.truvark.common.logging.LogPriority
@@ -32,12 +32,11 @@ class AndroidThumbnailProvider(private val context: Context) : ThumbnailProvider
3232
}
3333

3434
private val imageLoader = ImageLoader.Builder(context)
35-
.components {
36-
add(SvgDecoder.Factory())
37-
add(VideoFrameDecoder.Factory())
38-
}
3935
.diskCachePolicy(CachePolicy.DISABLED)
4036
.memoryCachePolicy(CachePolicy.DISABLED)
37+
// https://github.com/coil-kt/coil/issues/2808
38+
// for some reason the quality decreases is only noticeable for thumbnail generation (not AsyncImage).
39+
.imageDecoderEnabled(false)
4140
.build()
4241

4342
override suspend fun createThumbnail(file: FileInfo): ByteArray? {
@@ -61,7 +60,7 @@ class AndroidThumbnailProvider(private val context: Context) : ThumbnailProvider
6160

6261
val result = imageLoader.execute(request)
6362
when (result) {
64-
is SuccessResult -> return (result.drawable as BitmapDrawable).bitmap
63+
is SuccessResult -> return result.image.toBitmap()
6564
is ErrorResult -> throw result.throwable
6665
}
6766
}

android/src/main/java/de/lukaspieper/truvark/domain/crypto/decryption/coil/CipherFileFetcher.kt

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,44 @@
66

77
package de.lukaspieper.truvark.domain.crypto.decryption.coil
88

9-
import android.content.Context
109
import android.net.Uri
11-
import coil.ImageLoader
12-
import coil.decode.DataSource
13-
import coil.decode.ImageSource
14-
import coil.fetch.FetchResult
15-
import coil.fetch.Fetcher
16-
import coil.fetch.SourceResult
17-
import coil.request.Options
10+
import coil3.ImageLoader
11+
import coil3.decode.DataSource
12+
import coil3.decode.ImageSource
13+
import coil3.fetch.FetchResult
14+
import coil3.fetch.Fetcher
15+
import coil3.fetch.SourceFetchResult
16+
import coil3.request.Options
1817
import de.lukaspieper.truvark.common.data.io.FileInfo
1918
import de.lukaspieper.truvark.common.domain.vault.Vault
2019
import de.lukaspieper.truvark.domain.crypto.decryption.DecryptingFileHandle
2120
import okio.buffer
2221

2322
class CipherFileFetcher(
2423
private val fileInfo: FileInfo,
25-
private val context: Context,
26-
private val vault: Vault
24+
private val options: Options,
25+
private val vault: Vault,
2726
) : Fetcher {
2827

2928
override suspend fun fetch(): FetchResult {
3029
// TODO: Make this FileSystem-agnostic, e.g. by splitting file access and decryption and adding a method
3130
// returning a FileHandle
3231
require(fileInfo.uri is Uri)
3332

34-
val decryptingFileHandle = DecryptingFileHandle(context.contentResolver, vault, fileInfo.uri as Uri)
35-
val imageSource = ImageSource(decryptingFileHandle.singleSource().buffer(), context)
33+
val decryptingFileHandle = DecryptingFileHandle(options.context.contentResolver, vault, fileInfo.uri as Uri)
34+
val imageSource = ImageSource(decryptingFileHandle.singleSource().buffer(), options.fileSystem)
3635

37-
return SourceResult(
36+
return SourceFetchResult(
3837
source = imageSource,
3938
mimeType = decryptingFileHandle.header.mimeType,
4039
dataSource = DataSource.DISK
4140
)
4241
}
4342

44-
class Factory(
45-
private val context: Context,
46-
private val vault: Vault
47-
) : Fetcher.Factory<FileInfo> {
43+
class Factory(private val vault: Vault) : Fetcher.Factory<FileInfo> {
4844

4945
override fun create(data: FileInfo, options: Options, imageLoader: ImageLoader): Fetcher {
50-
return CipherFileFetcher(data, context, vault)
46+
return CipherFileFetcher(data, options, vault)
5147
}
5248
}
5349
}

android/src/main/java/de/lukaspieper/truvark/domain/crypto/decryption/coil/CipherZoomableImageSource.kt

Lines changed: 22 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,48 @@
77
package de.lukaspieper.truvark.domain.crypto.decryption.coil
88

99
import android.content.ContentResolver
10-
import android.graphics.drawable.BitmapDrawable
11-
import android.graphics.drawable.Drawable
1210
import android.net.Uri
1311
import androidx.compose.runtime.Composable
14-
import androidx.compose.runtime.RememberObserver
12+
import androidx.compose.runtime.Immutable
1513
import androidx.compose.runtime.getValue
1614
import androidx.compose.runtime.mutableStateOf
1715
import androidx.compose.runtime.remember
1816
import androidx.compose.runtime.setValue
1917
import androidx.compose.ui.geometry.Size
20-
import androidx.compose.ui.graphics.painter.Painter
2118
import androidx.compose.ui.platform.LocalContext
22-
import coil.ImageLoader
23-
import coil.request.ImageRequest
24-
import coil.request.ImageResult
25-
import coil.request.SuccessResult
26-
import coil.size.Dimension
27-
import com.google.accompanist.drawablepainter.DrawablePainter
19+
import coil3.ImageLoader
20+
import coil3.compose.asPainter
21+
import coil3.request.CachePolicy
22+
import coil3.request.ImageRequest
23+
import coil3.request.ImageResult
24+
import coil3.request.SuccessResult
25+
import coil3.request.maxBitmapSize
26+
import coil3.size.Dimension
27+
import coil3.toBitmap
2828
import de.lukaspieper.truvark.common.data.io.FileInfo
2929
import de.lukaspieper.truvark.common.domain.vault.Vault
3030
import de.lukaspieper.truvark.domain.crypto.decryption.DecryptingFileHandle
31-
import kotlinx.coroutines.CoroutineScope
32-
import kotlinx.coroutines.Dispatchers
33-
import kotlinx.coroutines.SupervisorJob
34-
import kotlinx.coroutines.cancel
3531
import kotlinx.coroutines.flow.Flow
3632
import kotlinx.coroutines.flow.first
37-
import kotlinx.coroutines.launch
3833
import me.saket.telephoto.subsamplingimage.ImageBitmapOptions
3934
import me.saket.telephoto.subsamplingimage.SubSamplingImageSource
4035
import me.saket.telephoto.zoomable.ZoomableImageSource
4136
import me.saket.telephoto.zoomable.ZoomableImageSource.ResolveResult
37+
import me.saket.telephoto.zoomable.copy
4238
import kotlin.math.roundToInt
43-
import kotlin.time.Duration
44-
import coil.size.Size as CoilSize
39+
import coil3.size.Size as CoilSize
4540

4641
/**
4742
* A [ZoomableImageSource] for Telephoto to load encrypted images with support for subsampling while keeping fallback to
4843
* Coil for other image types like GIFs, SVGs, etc.
4944
*/
45+
@Immutable
5046
internal class CipherZoomableImageSource(
5147
private val model: FileInfo,
48+
private val imageLoader: ImageLoader,
5249
private val mimeType: String,
5350
private val vault: Vault,
5451
private val contentResolver: ContentResolver,
55-
private val imageLoader: ImageLoader,
5652
) : ZoomableImageSource {
5753

5854
@Composable
@@ -63,9 +59,11 @@ internal class CipherZoomableImageSource(
6359
request = ImageRequest.Builder(context)
6460
.data(model)
6561
.size { canvasSize.first().toCoilSize() }
62+
.memoryCachePolicy(CachePolicy.ENABLED)
63+
.maxBitmapSize(CoilSize.ORIGINAL)
6664
.build(),
67-
mimeType = mimeType,
6865
imageLoader = imageLoader,
66+
mimeType = mimeType,
6967
vault = vault,
7068
contentResolver = contentResolver
7169
)
@@ -81,12 +79,11 @@ internal class CipherZoomableImageSource(
8179

8280
private class Resolver(
8381
private val request: ImageRequest,
84-
private val mimeType: String,
8582
private val imageLoader: ImageLoader,
83+
private val mimeType: String,
8684
private val vault: Vault,
8785
private val contentResolver: ContentResolver,
88-
) : RememberObserver {
89-
private var scope: CoroutineScope? = null
86+
) : RememberWorker() {
9087
private val subSamplingMimeTypes = listOf(
9188
"image/jpeg",
9289
"image/png",
@@ -99,19 +96,19 @@ private class Resolver(
9996
ResolveResult(delegate = null)
10097
)
10198

102-
private suspend fun work() {
99+
override suspend fun work() {
103100
val result = imageLoader.execute(request)
104101
val imageSource = result.toSubSamplingImageSource()
105102

106103
resolved = resolved.copy(
107104
delegate = if (result is SuccessResult && imageSource != null) {
108105
ZoomableImageSource.SubSamplingDelegate(
109106
source = imageSource,
110-
imageOptions = ImageBitmapOptions(from = (result.drawable as BitmapDrawable).bitmap)
107+
imageOptions = ImageBitmapOptions(from = result.image.toBitmap())
111108
)
112109
} else {
113110
ZoomableImageSource.PainterDelegate(
114-
painter = result.drawable?.asPainter()
111+
painter = result.image?.asPainter(request.context)
115112
)
116113
}
117114
)
@@ -132,35 +129,4 @@ private class Resolver(
132129

133130
return null
134131
}
135-
136-
private fun Drawable.asPainter(): Painter {
137-
return DrawablePainter(mutate())
138-
}
139-
140-
//region RememberWorker and extension
141-
142-
override fun onRemembered() {
143-
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
144-
scope!!.launch { work() }
145-
}
146-
147-
override fun onAbandoned() {
148-
scope?.cancel()
149-
}
150-
151-
override fun onForgotten() {
152-
scope?.cancel()
153-
}
154-
155-
private fun ResolveResult.copy(
156-
delegate: ZoomableImageSource.ImageDelegate? = this.delegate,
157-
crossfadeDuration: Duration = this.crossfadeDuration,
158-
placeholder: Painter? = this.placeholder,
159-
) = ResolveResult(
160-
delegate = delegate,
161-
crossfadeDuration = crossfadeDuration,
162-
placeholder = placeholder,
163-
)
164-
165-
//endregion
166132
}

0 commit comments

Comments
 (0)