Merge branch 'develop'
4
.github/workflows/dependency-review.yml
vendored
|
@ -17,11 +17,11 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
- name: 'Checkout Repository'
|
- name: 'Checkout Repository'
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
- name: 'Dependency Review'
|
- name: 'Dependency Review'
|
||||||
uses: actions/dependency-review-action@3b139cfc5fae8b618d3eae3675e383bb1769c019 # v4.5.0
|
uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0
|
||||||
|
|
4
.github/workflows/quality-check.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ jobs:
|
||||||
build-mode: manual
|
build-mode: manual
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|
13
.github/workflows/release.yml
vendored
|
@ -18,7 +18,7 @@ jobs:
|
||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
@ -43,20 +43,15 @@ jobs:
|
||||||
# `KEY_JKS` should contain the result of:
|
# `KEY_JKS` should contain the result of:
|
||||||
# gpg -c --armor keystore.jks
|
# gpg -c --armor keystore.jks
|
||||||
# `KEY_JKS_PASSPHRASE` should contain the passphrase used for the command above
|
# `KEY_JKS_PASSPHRASE` should contain the passphrase used for the command above
|
||||||
# The SkSL bundle must be produced with the same Flutter engine as the one used to build the artifact
|
|
||||||
# flutter build <subcommand> --bundle-sksl-path shaders.sksl.json
|
|
||||||
# do not bundle shaders for izzy/libre flavours, to avoid crashes in some environments:
|
|
||||||
# cf https://github.com/deckerst/aves/issues/388
|
|
||||||
# cf https://github.com/deckerst/aves/issues/398
|
|
||||||
run: |
|
run: |
|
||||||
echo "${{ secrets.KEY_JKS }}" > release.keystore.asc
|
echo "${{ secrets.KEY_JKS }}" > release.keystore.asc
|
||||||
gpg -d --passphrase "${{ secrets.KEY_JKS_PASSPHRASE }}" --batch release.keystore.asc > $AVES_STORE_FILE
|
gpg -d --passphrase "${{ secrets.KEY_JKS_PASSPHRASE }}" --batch release.keystore.asc > $AVES_STORE_FILE
|
||||||
rm release.keystore.asc
|
rm release.keystore.asc
|
||||||
mkdir outputs
|
mkdir outputs
|
||||||
scripts/apply_flavor_play.sh
|
scripts/apply_flavor_play.sh
|
||||||
./flutterw build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders.sksl.json
|
./flutterw build appbundle -t lib/main_play.dart --flavor play
|
||||||
cp build/app/outputs/bundle/playRelease/*.aab outputs
|
cp build/app/outputs/bundle/playRelease/*.aab outputs
|
||||||
./flutterw build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders.sksl.json
|
./flutterw build apk -t lib/main_play.dart --flavor play
|
||||||
cp build/app/outputs/apk/play/release/*.apk outputs
|
cp build/app/outputs/apk/play/release/*.apk outputs
|
||||||
scripts/apply_flavor_izzy.sh
|
scripts/apply_flavor_izzy.sh
|
||||||
./flutterw build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi
|
./flutterw build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi
|
||||||
|
@ -98,7 +93,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|
2
.github/workflows/scorecards.yml
vendored
|
@ -31,7 +31,7 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0
|
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||||
with:
|
with:
|
||||||
egress-policy: audit
|
egress-policy: audit
|
||||||
|
|
||||||
|
|
14
CHANGELOG.md
|
@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## <a id="unreleased"></a>[Unreleased]
|
## <a id="unreleased"></a>[Unreleased]
|
||||||
|
|
||||||
|
## <a id="v1.12.9"></a>[v1.12.9] - 2025-04-06
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Kannada translation (thanks Chethan, Prasannakumar T Bhat)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- enable Impeller rendering engine
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- memory pressure during browsing
|
||||||
|
|
||||||
## <a id="v1.12.8"></a>[v1.12.8] - 2025-03-25
|
## <a id="v1.12.8"></a>[v1.12.8] - 2025-03-25
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -329,12 +329,8 @@
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
<!--
|
|
||||||
Screenshot driver scenario is not supported by Impeller: "Compressed screenshots not supported for Impeller".
|
|
||||||
As of Flutter v3.29.2, switching pages with alpha transition yields artifacts when Impeller is enabled.
|
|
||||||
-->
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||||
android:value="false" />
|
android:value="true" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package deckers.thibault.aves.channel.calls
|
package deckers.thibault.aves.channel.calls
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||||
|
@ -21,7 +23,8 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler
|
||||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"getEntry" -> ioScope.launch { safe(call, result, ::getEntry) }
|
"getEntry" -> ioScope.launch { safe(call, result, ::getEntry) }
|
||||||
"clearSizedThumbnailDiskCache" -> ioScope.launch { safe(call, result, ::clearSizedThumbnailDiskCache) }
|
"clearImageDiskCache" -> ioScope.launch { safe(call, result, ::clearImageDiskCache) }
|
||||||
|
"clearImageMemoryCache" -> ioScope.launch { safe(call, result, ::clearImageMemoryCache) }
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,11 +50,18 @@ class MediaFetchObjectHandler(private val context: Context) : MethodCallHandler
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearSizedThumbnailDiskCache(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
private fun clearImageDiskCache(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||||
Glide.get(context).clearDiskCache()
|
Glide.get(context).clearDiskCache()
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun clearImageMemoryCache(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
Handler(Looper.getMainLooper()).post {
|
||||||
|
Glide.get(context).clearMemory()
|
||||||
|
}
|
||||||
|
result.success(null)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val CHANNEL = "deckers.thibault/aves/media_fetch_object"
|
const val CHANNEL = "deckers.thibault/aves/media_fetch_object"
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,6 @@ import kotlin.math.roundToInt
|
||||||
class RegionFetcher internal constructor(
|
class RegionFetcher internal constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
) {
|
) {
|
||||||
private var lastDecoderRef: LastDecoderRef? = null
|
|
||||||
|
|
||||||
private val exportUris = HashMap<Pair<Uri, Int?>, Uri>()
|
|
||||||
|
|
||||||
// return decoded bytes in ARGB_8888, with trailer bytes:
|
// return decoded bytes in ARGB_8888, with trailer bytes:
|
||||||
// - width (int32)
|
// - width (int32)
|
||||||
// - height (int32)
|
// - height (int32)
|
||||||
|
@ -63,24 +59,12 @@ class RegionFetcher internal constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentDecoderRef = lastDecoderRef
|
|
||||||
if (currentDecoderRef != null && currentDecoderRef.requestKey != requestKey) {
|
|
||||||
currentDecoderRef = null
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (currentDecoderRef == null) {
|
val decoder = getOrCreateDecoder(uri, requestKey)
|
||||||
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
if (decoder == null) {
|
||||||
BitmapRegionDecoderCompat.newInstance(input)
|
|
||||||
}
|
|
||||||
if (newDecoder == null) {
|
|
||||||
result.error("fetch-read-null", "failed to open file for mimeType=$mimeType uri=$uri regionRect=$regionRect", null)
|
result.error("fetch-read-null", "failed to open file for mimeType=$mimeType uri=$uri regionRect=$regionRect", null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
currentDecoderRef = LastDecoderRef(requestKey, newDecoder)
|
|
||||||
}
|
|
||||||
val decoder = currentDecoderRef.decoder
|
|
||||||
lastDecoderRef = currentDecoderRef
|
|
||||||
|
|
||||||
// with raw images, the known image size may not match the decoded image size
|
// with raw images, the known image size may not match the decoded image size
|
||||||
// so we scale the requested region accordingly
|
// so we scale the requested region accordingly
|
||||||
|
@ -159,6 +143,26 @@ class RegionFetcher internal constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOrCreateDecoder(uri: Uri, requestKey: Pair<Uri, Int?>): BitmapRegionDecoder? {
|
||||||
|
var decoderRef = decoderPool.firstOrNull { it.requestKey == requestKey }
|
||||||
|
if (decoderRef == null) {
|
||||||
|
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||||
|
BitmapRegionDecoderCompat.newInstance(input)
|
||||||
|
}
|
||||||
|
if (newDecoder == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
decoderRef = DecoderRef(requestKey, newDecoder)
|
||||||
|
} else {
|
||||||
|
decoderPool.remove(decoderRef)
|
||||||
|
}
|
||||||
|
decoderPool.add(0, decoderRef)
|
||||||
|
while (decoderPool.size > DECODER_POOL_SIZE) {
|
||||||
|
decoderPool.removeAt(decoderPool.size - 1)
|
||||||
|
}
|
||||||
|
return decoderRef.decoder
|
||||||
|
}
|
||||||
|
|
||||||
private fun createTemporaryJpegExport(uri: Uri, mimeType: String, pageId: Int?): Uri {
|
private fun createTemporaryJpegExport(uri: Uri, mimeType: String, pageId: Int?): Uri {
|
||||||
Log.d(LOG_TAG, "create JPEG export for uri=$uri mimeType=$mimeType pageId=$pageId")
|
Log.d(LOG_TAG, "create JPEG export for uri=$uri mimeType=$mimeType pageId=$pageId")
|
||||||
val target = Glide.with(context)
|
val target = Glide.with(context)
|
||||||
|
@ -180,7 +184,7 @@ class RegionFetcher internal constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class LastDecoderRef(
|
private data class DecoderRef(
|
||||||
val requestKey: Pair<Uri, Int?>,
|
val requestKey: Pair<Uri, Int?>,
|
||||||
val decoder: BitmapRegionDecoder,
|
val decoder: BitmapRegionDecoder,
|
||||||
)
|
)
|
||||||
|
@ -188,5 +192,8 @@ class RegionFetcher internal constructor(
|
||||||
companion object {
|
companion object {
|
||||||
private val LOG_TAG = LogUtils.createTag<RegionFetcher>()
|
private val LOG_TAG = LogUtils.createTag<RegionFetcher>()
|
||||||
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
|
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
|
||||||
|
private const val DECODER_POOL_SIZE = 3
|
||||||
|
private val decoderPool = ArrayList<DecoderRef>()
|
||||||
|
private val exportUris = HashMap<Pair<Uri, Int?>, Uri>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@ import kotlin.math.ceil
|
||||||
class SvgRegionFetcher internal constructor(
|
class SvgRegionFetcher internal constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
) {
|
) {
|
||||||
private var lastSvgRef: LastSvgRef? = null
|
|
||||||
|
|
||||||
fun fetch(
|
fun fetch(
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
sizeBytes: Long?,
|
sizeBytes: Long?,
|
||||||
|
@ -39,33 +37,13 @@ class SvgRegionFetcher internal constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentSvgRef = lastSvgRef
|
|
||||||
if (currentSvgRef != null && currentSvgRef.uri != uri) {
|
|
||||||
currentSvgRef = null
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (currentSvgRef == null) {
|
val svg = getOrCreateDecoder(uri)
|
||||||
val newSvg = StorageUtils.openInputStream(context, uri)?.use { input ->
|
if (svg == null) {
|
||||||
try {
|
|
||||||
SVG.getFromInputStream(SVGParserBufferedInputStream(input))
|
|
||||||
} catch (ex: SVGParseException) {
|
|
||||||
result.error("fetch-parse", "failed to parse SVG for uri=$uri regionRect=$regionRect", null)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newSvg == null) {
|
|
||||||
result.error("fetch-read-null", "failed to open file for uri=$uri regionRect=$regionRect", null)
|
result.error("fetch-read-null", "failed to open file for uri=$uri regionRect=$regionRect", null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newSvg.normalizeSize()
|
|
||||||
currentSvgRef = LastSvgRef(uri, newSvg)
|
|
||||||
}
|
|
||||||
val svg = currentSvgRef.svg
|
|
||||||
lastSvgRef = currentSvgRef
|
|
||||||
|
|
||||||
// we scale the requested region accordingly to the viewbox size
|
// we scale the requested region accordingly to the viewbox size
|
||||||
val viewBox = svg.documentViewBox
|
val viewBox = svg.documentViewBox
|
||||||
val svgWidth = viewBox.width()
|
val svgWidth = viewBox.width()
|
||||||
|
@ -110,17 +88,42 @@ class SvgRegionFetcher internal constructor(
|
||||||
bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight)
|
bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight)
|
||||||
val bytes = BitmapUtils.getRawBytes(bitmap, recycle = true)
|
val bytes = BitmapUtils.getRawBytes(bitmap, recycle = true)
|
||||||
result.success(bytes)
|
result.success(bytes)
|
||||||
|
} catch (e: SVGParseException) {
|
||||||
|
result.error("fetch-parse", "failed to parse SVG for uri=$uri regionRect=$regionRect", null)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message)
|
result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class LastSvgRef(
|
private fun getOrCreateDecoder(uri: Uri): SVG? {
|
||||||
|
var decoderRef = decoderPool.firstOrNull { it.uri == uri }
|
||||||
|
if (decoderRef == null) {
|
||||||
|
val newDecoder = StorageUtils.openInputStream(context, uri)?.use { input ->
|
||||||
|
SVG.getFromInputStream(SVGParserBufferedInputStream(input))
|
||||||
|
}
|
||||||
|
if (newDecoder == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
newDecoder.normalizeSize()
|
||||||
|
decoderRef = DecoderRef(uri, newDecoder)
|
||||||
|
} else {
|
||||||
|
decoderPool.remove(decoderRef)
|
||||||
|
}
|
||||||
|
decoderPool.add(0, decoderRef)
|
||||||
|
while (decoderPool.size > DECODER_POOL_SIZE) {
|
||||||
|
decoderPool.removeAt(decoderPool.size - 1)
|
||||||
|
}
|
||||||
|
return decoderRef.decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class DecoderRef(
|
||||||
val uri: Uri,
|
val uri: Uri,
|
||||||
val svg: SVG,
|
val decoder: SVG,
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
|
private val PREFERRED_CONFIG = Bitmap.Config.ARGB_8888
|
||||||
|
private const val DECODER_POOL_SIZE = 3
|
||||||
|
private val decoderPool = ArrayList<DecoderRef>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package deckers.thibault.aves.decoder
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.text.format.Formatter
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.GlideBuilder
|
import com.bumptech.glide.GlideBuilder
|
||||||
|
@ -10,9 +11,17 @@ import com.bumptech.glide.annotation.GlideModule
|
||||||
import com.bumptech.glide.load.DecodeFormat
|
import com.bumptech.glide.load.DecodeFormat
|
||||||
import com.bumptech.glide.load.ImageHeaderParser
|
import com.bumptech.glide.load.ImageHeaderParser
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPoolAdapter
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.LruArrayPool
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool
|
||||||
|
import com.bumptech.glide.load.engine.cache.DiskCache
|
||||||
|
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
|
||||||
|
import com.bumptech.glide.load.engine.cache.LruResourceCache
|
||||||
|
import com.bumptech.glide.load.engine.cache.MemorySizeCalculator
|
||||||
import com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser
|
import com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser
|
||||||
import com.bumptech.glide.module.AppGlideModule
|
import com.bumptech.glide.module.AppGlideModule
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import deckers.thibault.aves.utils.LogUtils
|
||||||
import deckers.thibault.aves.utils.MimeTypes
|
import deckers.thibault.aves.utils.MimeTypes
|
||||||
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
||||||
import deckers.thibault.aves.utils.StorageUtils
|
import deckers.thibault.aves.utils.StorageUtils
|
||||||
|
@ -23,6 +32,30 @@ class AvesAppGlideModule : AppGlideModule() {
|
||||||
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
override fun applyOptions(context: Context, builder: GlideBuilder) {
|
||||||
// hide noisy warning (e.g. for images that can't be decoded)
|
// hide noisy warning (e.g. for images that can't be decoded)
|
||||||
builder.setLogLevel(Log.ERROR)
|
builder.setLogLevel(Log.ERROR)
|
||||||
|
|
||||||
|
// sizing
|
||||||
|
val memorySizeCalculator = MemorySizeCalculator.Builder(context).build()
|
||||||
|
builder.setMemorySizeCalculator(memorySizeCalculator)
|
||||||
|
val size: Int = memorySizeCalculator.bitmapPoolSize
|
||||||
|
if (size > 0) {
|
||||||
|
builder.setBitmapPool(LruBitmapPool(size.toLong()))
|
||||||
|
} else {
|
||||||
|
builder.setBitmapPool(BitmapPoolAdapter())
|
||||||
|
}
|
||||||
|
builder.setArrayPool(LruArrayPool(memorySizeCalculator.arrayPoolSizeInBytes))
|
||||||
|
builder.setMemoryCache(LruResourceCache(memorySizeCalculator.memoryCacheSize.toLong()))
|
||||||
|
|
||||||
|
val diskCacheSize = DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE
|
||||||
|
val internalCacheDiskCacheFactory = InternalCacheDiskCacheFactory(context, DiskCache.Factory.DEFAULT_DISK_CACHE_DIR, diskCacheSize.toLong())
|
||||||
|
builder.setDiskCache(internalCacheDiskCacheFactory)
|
||||||
|
|
||||||
|
fun toMb(bytes: Int) = Formatter.formatFileSize(context, bytes.toLong())
|
||||||
|
Log.d(
|
||||||
|
LOG_TAG, "Glide disk cache size=${toMb(diskCacheSize)}" +
|
||||||
|
", memory cache size=${toMb(memorySizeCalculator.memoryCacheSize)}" +
|
||||||
|
", bitmap pool size=${toMb(memorySizeCalculator.bitmapPoolSize)}" +
|
||||||
|
", array pool size=${toMb(memorySizeCalculator.arrayPoolSizeInBytes)}"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
|
||||||
|
@ -34,6 +67,8 @@ class AvesAppGlideModule : AppGlideModule() {
|
||||||
override fun isManifestParsingEnabled(): Boolean = false
|
override fun isManifestParsingEnabled(): Boolean = false
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private val LOG_TAG = LogUtils.createTag<AvesAppGlideModule>()
|
||||||
|
|
||||||
// request a fresh image with the highest quality format
|
// request a fresh image with the highest quality format
|
||||||
val uncachedFullImageOptions = RequestOptions()
|
val uncachedFullImageOptions = RequestOptions()
|
||||||
.format(DecodeFormat.PREFER_ARGB_8888)
|
.format(DecodeFormat.PREFER_ARGB_8888)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Aves</string>
|
<string name="app_name">ಎವೀಸ್</string>
|
||||||
<string name="app_widget_label">ಫೋಟೋ ಫ್ರೇಮ್</string>
|
<string name="app_widget_label">ಫೋಟೋ ಫ್ರೇಮ್</string>
|
||||||
<string name="wallpaper">ವಾಲ್ಪೇಪರ್</string>
|
<string name="wallpaper">ವಾಲ್ಪೇಪರ್</string>
|
||||||
<string name="videos_shortcut_short_label">ವೀಡಿಯೊಗಳು</string>
|
<string name="videos_shortcut_short_label">ವೀಡಿಯೊಗಳು</string>
|
||||||
|
@ -8,4 +8,5 @@
|
||||||
<string name="analysis_notification_default_title">ಮೀಡಿಯಾ ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ</string>
|
<string name="analysis_notification_default_title">ಮೀಡಿಯಾ ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ</string>
|
||||||
<string name="analysis_notification_action_stop">ನಿಲ್ಲಿಸಿ</string>
|
<string name="analysis_notification_action_stop">ನಿಲ್ಲಿಸಿ</string>
|
||||||
<string name="search_shortcut_short_label">ಹುಡುಕಿ</string>
|
<string name="search_shortcut_short_label">ಹುಡುಕಿ</string>
|
||||||
|
<string name="map_shortcut_short_label">ನಕ್ಷೆ</string>
|
||||||
</resources>
|
</resources>
|
35
assets/terms.txt
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
Terms of Service
|
||||||
|
================
|
||||||
|
|
||||||
|
“Aves Gallery” is an open-source gallery and metadata explorer app allowing you to access and manage your local photos and videos.
|
||||||
|
|
||||||
|
The app is designed for legal, authorized and acceptable purposes.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Disclaimer
|
||||||
|
==========
|
||||||
|
|
||||||
|
The app is released “as-is”, without any warranty, responsibility or liability. Use of the app is at your own risk.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Privacy Policy
|
||||||
|
==============
|
||||||
|
|
||||||
|
The app does not collect any personal data. We never have access to your photos and videos. This also means that we cannot get them back for you if you delete them without backing them up.
|
||||||
|
|
||||||
|
Optionally, with your consent, the app accesses the inventory of installed apps to improve album display.
|
||||||
|
|
||||||
|
Optionally, with your consent, the app collects anonymous error and diagnostic data to improve the app quality. We use Firebase Crashlytics, and the anonymous data are stored on their servers. Please note that those are anonymous data, there is absolutely nothing personal about those data.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Contact
|
||||||
|
=======
|
||||||
|
|
||||||
|
Developer: Thibault Deckers
|
||||||
|
|
||||||
|
Email: gallery.aves@gmail.com
|
||||||
|
|
||||||
|
Website: https://github.com/deckerst/aves
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
<b>Navigation und Suche</b> ist ein wichtiger Bestandteil von <i>Aves</i>. Das Ziel besteht darin, dass Benutzer problemlos von Alben zu Fotos zu Tags zu Karten usw. wechseln können.
|
<b>Navigation und Suche</b> ist ein wichtiger Bestandteil von <i>Aves</i>. Das Ziel besteht darin, dass Benutzer problemlos von Alben zu Fotos zu Tags zu Karten usw. wechseln können.
|
||||||
|
|
||||||
<i>Aves</i> lässt sich mit Android mit Funktionen wie <b>App-Verknüpfungen</b> und <b>globaler Suche</b> integrieren. Es funktioniert auch als <b>Medienbetrachter und -auswahl</b>.
|
<i>Aves</i> integriert sich in Android (einschließlich Android TV) mit Funktionen wie <b>Widgets</b>, <b>App-Shortcuts</b>, <b>Bildschirmschoner</b> und der <b>globalen Suche</b> integrieren. Sie funktioniert auch als <b>Medienbetrachter und -Picker</b>.
|
||||||
|
|
4
fastlane/metadata/android/en-US/changelogs/149.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
In v1.12.9:
|
||||||
|
- play more kinds of motion photos
|
||||||
|
- enjoy the app in Galician and Kannada
|
||||||
|
Full changelog available on GitHub
|
4
fastlane/metadata/android/en-US/changelogs/14901.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
In v1.12.9:
|
||||||
|
- play more kinds of motion photos
|
||||||
|
- enjoy the app in Galician and Kannada
|
||||||
|
Full changelog available on GitHub
|
|
@ -1,5 +1,5 @@
|
||||||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
<i>ಏವೀಸ್</i> ನಿಮ್ಮ JPEG ಗಳು ಮತ್ತು MP4 ಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ಎಲ್ಲಾ ರೀತಿಯ ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ನಿಭಾಯಿಸಬಲ್ಲದು, ಅಲ್ಲದೆ ವಿಶಿಷ್ಟವಾದ <b>ಬಹು-ಪುಟ TIFFಗಳು, SVGಗಳು, ಹಳೆಯ AVIಗಳು ಮತ್ತು ಹಲವು ಪ್ರಕಾರಗಳನ್ನು ಕೂಡ ಬೆಂಬಲಿಸುತ್ತದೆ</b> ಇದು <b>ಚಲನೆಯ ಫೋಟೋಗಳು</b>, <b>ಪನೋರಮಾಗಳು</b> (ಫೋಟೋ ಗೋಳಗಳು) <b>360° ವೀಡಿಯೊಗಳು</b>, ಹಾಗೆಯೇ <b>GeoTIFF</b> ಕಡತಗಳನ್ನು ಗುರುತಿಸಲು ನಿಮ್ಮ ಮಾಧ್ಯಮ ಸಂಗ್ರಹವನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡುತ್ತದೆ.
|
||||||
|
|
||||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
<b>ನ್ಯಾವಿಗೇಷನ್ ಮತ್ತು ಹುಡುಕಾಟ</b> <i>ಏವೀಸ್</i>ನ ಒಂದು ಪ್ರಮುಖ ಭಾಗವಾಗಿದೆ. ಬಳಕೆದಾರರು ಆಲ್ಬಮ್ಗಳಿಂದ ಫೋಟೋಗಳಿಂದ ಟ್ಯಾಗ್ಗಳಿಗೆ ನಕ್ಷೆಗಳಿಗೆ ಸುಲಭವಾಗಿ ಹರಿಯುವುದು ಗುರಿಯಾಗಿದೆ.
|
||||||
|
|
||||||
<i>Aves</i> integrates with Android (including Android TV) with features such as <b>widgets</b>, <b>app shortcuts</b>, <b>screen saver</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
<i>ಎವೀಸ್</i> ಆಂಡ್ರಾಯ್ಡ್ (ಟಿವಿ ಸೇರಿದಂತೆ) ನೊಂದಿಗೆ ಸಂಯೋಜಿಸುತ್ತದೆ, ಉದಾಹರಣೆಗೆ <b>ವಿಜೆಟ್ಗಳು</b>, <b>ಆ್ಯಪ್ ಶಾರ್ಟ್ಕಟ್ಗಳು</b>, <b>ಸ್ಕ್ರೀನ್ ಸೇವರ್</b> ಮತ್ತು <b>ಜಾಗತಿಕ ಹುಡುಕಾಟ</b> ನಿರ್ವಹಣೆ. ಇದು <b>ಮೀಡಿಯಾ ವೀಕ್ಷಕ ಮತ್ತು ಪಿಕ್ಕರ್</b> ಆಗಿಯೂ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ.
|
||||||
|
|
BIN
fastlane/metadata/android/kn/images/featureGraphic.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/1.png
Normal file
After Width: | Height: | Size: 308 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/2.png
Normal file
After Width: | Height: | Size: 556 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/3.png
Normal file
After Width: | Height: | Size: 150 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/4.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/5.png
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/6.png
Normal file
After Width: | Height: | Size: 376 KiB |
BIN
fastlane/metadata/android/kn/images/phoneScreenshots/7.png
Normal file
After Width: | Height: | Size: 381 KiB |
|
@ -1 +1 @@
|
||||||
Gallery and metadata explorer
|
ಗ್ಯಾಲರಿ ಮತ್ತು ಮೆಟಾಡೇಟಾ ಎಕ್ಸ್ಪ್ಲೋರರ್
|
|
@ -10,7 +10,7 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class UriImage extends ImageProvider<UriImage> with EquatableMixin {
|
class FullImage extends ImageProvider<FullImage> with EquatableMixin {
|
||||||
final String uri, mimeType;
|
final String uri, mimeType;
|
||||||
final int? pageId, rotationDegrees, sizeBytes;
|
final int? pageId, rotationDegrees, sizeBytes;
|
||||||
final bool isFlipped, isAnimated;
|
final bool isFlipped, isAnimated;
|
||||||
|
@ -19,7 +19,7 @@ class UriImage extends ImageProvider<UriImage> with EquatableMixin {
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [uri, pageId, rotationDegrees, isFlipped, isAnimated, scale];
|
List<Object?> get props => [uri, pageId, rotationDegrees, isFlipped, isAnimated, scale];
|
||||||
|
|
||||||
const UriImage({
|
const FullImage({
|
||||||
required this.uri,
|
required this.uri,
|
||||||
required this.mimeType,
|
required this.mimeType,
|
||||||
required this.pageId,
|
required this.pageId,
|
||||||
|
@ -31,12 +31,12 @@ class UriImage extends ImageProvider<UriImage> with EquatableMixin {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<UriImage> obtainKey(ImageConfiguration configuration) {
|
Future<FullImage> obtainKey(ImageConfiguration configuration) {
|
||||||
return SynchronousFuture<UriImage>(this);
|
return SynchronousFuture<FullImage>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImageStreamCompleter loadImage(UriImage key, ImageDecoderCallback decode) {
|
ImageStreamCompleter loadImage(FullImage key, ImageDecoderCallback decode) {
|
||||||
final chunkEvents = StreamController<ImageChunkEvent>();
|
final chunkEvents = StreamController<ImageChunkEvent>();
|
||||||
|
|
||||||
return MultiFrameImageStreamCompleter(
|
return MultiFrameImageStreamCompleter(
|
||||||
|
@ -59,11 +59,11 @@ class UriImage extends ImageProvider<UriImage> with EquatableMixin {
|
||||||
case MimeTypes.svg:
|
case MimeTypes.svg:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return !isAnimated;
|
return !isAnimated && !MimeTypes.isVideo(mimeType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ui.Codec> _loadAsync(UriImage key, ImageDecoderCallback decode, StreamController<ImageChunkEvent> chunkEvents) async {
|
Future<ui.Codec> _loadAsync(FullImage key, ImageDecoderCallback decode, StreamController<ImageChunkEvent> chunkEvents) async {
|
||||||
assert(key == this);
|
assert(key == this);
|
||||||
|
|
||||||
final request = ImageRequest(
|
final request = ImageRequest(
|
|
@ -1552,5 +1552,87 @@
|
||||||
"settingsViewerQuickActionEmpty": "Ingen knapper",
|
"settingsViewerQuickActionEmpty": "Ingen knapper",
|
||||||
"@settingsViewerQuickActionEmpty": {},
|
"@settingsViewerQuickActionEmpty": {},
|
||||||
"chipActionFilterOut": "Filtrer ud",
|
"chipActionFilterOut": "Filtrer ud",
|
||||||
"@chipActionFilterOut": {}
|
"@chipActionFilterOut": {},
|
||||||
|
"mapStyleOsmHot": "Humanitært OSM",
|
||||||
|
"@mapStyleOsmHot": {},
|
||||||
|
"collectionDeselectSectionTooltip": "Fravælg sektion",
|
||||||
|
"@collectionDeselectSectionTooltip": {},
|
||||||
|
"editEntryLocationDialogImportGpx": "Importér GPX",
|
||||||
|
"@editEntryLocationDialogImportGpx": {},
|
||||||
|
"editEntryLocationDialogTimeShift": "Tidsskift",
|
||||||
|
"@editEntryLocationDialogTimeShift": {},
|
||||||
|
"videoStreamSelectionDialogTrack": "Spor",
|
||||||
|
"@videoStreamSelectionDialogTrack": {},
|
||||||
|
"albumGroupTier": "Efter kategori",
|
||||||
|
"@albumGroupTier": {},
|
||||||
|
"settingsVideoEnableHardwareAcceleration": "Hardwareacceleration",
|
||||||
|
"@settingsVideoEnableHardwareAcceleration": {},
|
||||||
|
"settingsViewerSectionTitle": "Fremviser",
|
||||||
|
"@settingsViewerSectionTitle": {},
|
||||||
|
"openMapPageTooltip": "Se på kortside",
|
||||||
|
"@openMapPageTooltip": {},
|
||||||
|
"settingsCollectionBurstPatternsTile": "Filnavnmønstre",
|
||||||
|
"@settingsCollectionBurstPatternsTile": {},
|
||||||
|
"wallpaperUseScrollEffect": "Brug rulleeffekt på startside",
|
||||||
|
"@wallpaperUseScrollEffect": {},
|
||||||
|
"editEntryDateDialogSourceFileModifiedDate": "Filens ændringsdato",
|
||||||
|
"@editEntryDateDialogSourceFileModifiedDate": {},
|
||||||
|
"editEntryDateDialogShift": "Skift",
|
||||||
|
"@editEntryDateDialogShift": {},
|
||||||
|
"chipActionDecompose": "Split",
|
||||||
|
"@chipActionDecompose": {},
|
||||||
|
"coordinateFormatDdm": "DDM",
|
||||||
|
"@coordinateFormatDdm": {},
|
||||||
|
"videoActionShowNextFrame": "Vis næste frame",
|
||||||
|
"@videoActionShowNextFrame": {},
|
||||||
|
"mapStyleStamenWatercolor": "Stamen Watercolor",
|
||||||
|
"@mapStyleStamenWatercolor": {},
|
||||||
|
"drawerCollectionAll": "Alle samlinger",
|
||||||
|
"@drawerCollectionAll": {},
|
||||||
|
"settingsThumbnailShowVideoDuration": "Vis videovarighed",
|
||||||
|
"@settingsThumbnailShowVideoDuration": {},
|
||||||
|
"settingsThemeColorHighlights": "Farvemarkeringer",
|
||||||
|
"@settingsThemeColorHighlights": {},
|
||||||
|
"viewerInfoSearchEmpty": "Ingen matchende nøgler",
|
||||||
|
"@viewerInfoSearchEmpty": {},
|
||||||
|
"removeEntryMetadataDialogAll": "Alle",
|
||||||
|
"@removeEntryMetadataDialogAll": {},
|
||||||
|
"aboutCreditsSectionTitle": "Kreditering",
|
||||||
|
"@aboutCreditsSectionTitle": {},
|
||||||
|
"settingsCollectionQuickActionTabSelecting": "Valg",
|
||||||
|
"@settingsCollectionQuickActionTabSelecting": {},
|
||||||
|
"settingsCollectionQuickActionTabBrowsing": "Browsing",
|
||||||
|
"@settingsCollectionQuickActionTabBrowsing": {},
|
||||||
|
"settingsViewerQuickActionEditorBanner": "Tryk og hold for at flytte knapper og vælge, hvilke handlinger der vises i fremviseren.",
|
||||||
|
"@settingsViewerQuickActionEditorBanner": {},
|
||||||
|
"settingsAllowInstalledAppAccess": "Tillad adgang til app-lager",
|
||||||
|
"@settingsAllowInstalledAppAccess": {},
|
||||||
|
"viewerInfoBackToViewerTooltip": "Tilbage til fremviser",
|
||||||
|
"@viewerInfoBackToViewerTooltip": {},
|
||||||
|
"videoActionShowPreviousFrame": "Vis forrige frame",
|
||||||
|
"@videoActionShowPreviousFrame": {},
|
||||||
|
"collectionSelectSectionTooltip": "Vælg sektion",
|
||||||
|
"@collectionSelectSectionTooltip": {},
|
||||||
|
"videoStreamSelectionDialogNoSelection": "Der er ingen andre spor.",
|
||||||
|
"@videoStreamSelectionDialogNoSelection": {},
|
||||||
|
"addShortcutDialogLabel": "Genvejsetiket",
|
||||||
|
"@addShortcutDialogLabel": {},
|
||||||
|
"moveUndatedConfirmationDialogMessage": "Gem elementdatoer, før du fortsætter?",
|
||||||
|
"@moveUndatedConfirmationDialogMessage": {},
|
||||||
|
"albumMimeTypeMixed": "Blandet",
|
||||||
|
"@albumMimeTypeMixed": {},
|
||||||
|
"videoActionCaptureFrame": "Tag billede af frame",
|
||||||
|
"@videoActionCaptureFrame": {},
|
||||||
|
"videoActionSelectStreams": "Vælg spor",
|
||||||
|
"@videoActionSelectStreams": {},
|
||||||
|
"videoActionABRepeat": "A-B gentagelse",
|
||||||
|
"@videoActionABRepeat": {},
|
||||||
|
"viewerActionLock": "Lås fremviser",
|
||||||
|
"@viewerActionLock": {},
|
||||||
|
"viewerActionUnlock": "Oplås fremviser",
|
||||||
|
"@viewerActionUnlock": {},
|
||||||
|
"keepScreenOnViewerOnly": "Kun fremvisningsside",
|
||||||
|
"@keepScreenOnViewerOnly": {},
|
||||||
|
"widgetOpenPageViewer": "Åbn fremviser",
|
||||||
|
"@widgetOpenPageViewer": {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1410,5 +1410,9 @@
|
||||||
"chipActionDecompose": "Aufschlüsseln",
|
"chipActionDecompose": "Aufschlüsseln",
|
||||||
"@chipActionDecompose": {},
|
"@chipActionDecompose": {},
|
||||||
"editEntryLocationDialogImportGpx": "GPX importieren",
|
"editEntryLocationDialogImportGpx": "GPX importieren",
|
||||||
"@editEntryLocationDialogImportGpx": {}
|
"@editEntryLocationDialogImportGpx": {},
|
||||||
|
"editEntryLocationDialogTimeShift": "Zeitverschiebung",
|
||||||
|
"@editEntryLocationDialogTimeShift": {},
|
||||||
|
"removeEntryMetadataDialogAll": "Alle",
|
||||||
|
"@removeEntryMetadataDialogAll": {}
|
||||||
}
|
}
|
||||||
|
|
1588
lib/l10n/app_kn.arb
|
@ -1605,7 +1605,7 @@
|
||||||
"@mapStyleOsmLiberty": {},
|
"@mapStyleOsmLiberty": {},
|
||||||
"mapStyleOpenTopoMap": "OpenTopoMap",
|
"mapStyleOpenTopoMap": "OpenTopoMap",
|
||||||
"@mapStyleOpenTopoMap": {},
|
"@mapStyleOpenTopoMap": {},
|
||||||
"mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | 地圖由 [OpenTopoMap](https://opentopomap.org/),以 [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/) 授權",
|
"mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | 地圖由 [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)",
|
||||||
"@mapAttributionOpenTopoMap": {},
|
"@mapAttributionOpenTopoMap": {},
|
||||||
"sortByDuration": "按時長",
|
"sortByDuration": "按時長",
|
||||||
"@sortByDuration": {}
|
"@sortByDuration": {}
|
||||||
|
|
|
@ -315,7 +315,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get entryActionRemoveFavourite => 'Fjern fra favoritter';
|
String get entryActionRemoveFavourite => 'Fjern fra favoritter';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionCaptureFrame => 'Capture frame';
|
String get videoActionCaptureFrame => 'Tag billede af frame';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionMute => 'Slå lyden fra';
|
String get videoActionMute => 'Slå lyden fra';
|
||||||
|
@ -336,19 +336,19 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get videoActionSkip10 => 'Spol 10 sekunder frem';
|
String get videoActionSkip10 => 'Spol 10 sekunder frem';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionShowPreviousFrame => 'Show previous frame';
|
String get videoActionShowPreviousFrame => 'Vis forrige frame';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionShowNextFrame => 'Show next frame';
|
String get videoActionShowNextFrame => 'Vis næste frame';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionSelectStreams => 'Select tracks';
|
String get videoActionSelectStreams => 'Vælg spor';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionSetSpeed => 'Afspilningshastighed';
|
String get videoActionSetSpeed => 'Afspilningshastighed';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoActionABRepeat => 'A-B repeat';
|
String get videoActionABRepeat => 'A-B gentagelse';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoRepeatActionSetStart => 'Sæt start';
|
String get videoRepeatActionSetStart => 'Sæt start';
|
||||||
|
@ -360,10 +360,10 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get viewerActionSettings => 'Indstillinger';
|
String get viewerActionSettings => 'Indstillinger';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get viewerActionLock => 'Lock viewer';
|
String get viewerActionLock => 'Lås fremviser';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get viewerActionUnlock => 'Unlock viewer';
|
String get viewerActionUnlock => 'Oplås fremviser';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get slideshowActionResume => 'Genoptag';
|
String get slideshowActionResume => 'Genoptag';
|
||||||
|
@ -548,7 +548,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get keepScreenOnVideoPlayback => 'Under videoafspilning';
|
String get keepScreenOnVideoPlayback => 'Under videoafspilning';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get keepScreenOnViewerOnly => 'Viewer page only';
|
String get keepScreenOnViewerOnly => 'Kun fremvisningsside';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get keepScreenOnAlways => 'Altid';
|
String get keepScreenOnAlways => 'Altid';
|
||||||
|
@ -575,7 +575,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get mapStyleOpenTopoMap => 'OpenTopoMap';
|
String get mapStyleOpenTopoMap => 'OpenTopoMap';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mapStyleOsmHot => 'Humanitarian OSM';
|
String get mapStyleOsmHot => 'Humanitært OSM';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mapStyleStamenWatercolor => 'Stamen Watercolor';
|
String get mapStyleStamenWatercolor => 'Stamen Watercolor';
|
||||||
|
@ -701,7 +701,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get widgetOpenPageCollection => 'Åbn samling';
|
String get widgetOpenPageCollection => 'Åbn samling';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get widgetOpenPageViewer => 'Open viewer';
|
String get widgetOpenPageViewer => 'Åbn fremviser';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get widgetTapUpdateWidget => 'Opdater widget';
|
String get widgetTapUpdateWidget => 'Opdater widget';
|
||||||
|
@ -756,7 +756,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get nameConflictDialogMultipleSourceMessage => 'Nogle filer har samme navn.';
|
String get nameConflictDialogMultipleSourceMessage => 'Nogle filer har samme navn.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addShortcutDialogLabel => 'Shortcut label';
|
String get addShortcutDialogLabel => 'Genvejsetiket';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get addShortcutButtonLabel => 'TILFØJ';
|
String get addShortcutButtonLabel => 'TILFØJ';
|
||||||
|
@ -793,7 +793,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get moveUndatedConfirmationDialogMessage => 'Save item dates before proceeding?';
|
String get moveUndatedConfirmationDialogMessage => 'Gem elementdatoer, før du fortsætter?';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get moveUndatedConfirmationDialogSetDate => 'Gem datoer';
|
String get moveUndatedConfirmationDialogSetDate => 'Gem datoer';
|
||||||
|
@ -976,10 +976,10 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get editEntryDateDialogExtractFromTitle => 'Udtræk fra titel';
|
String get editEntryDateDialogExtractFromTitle => 'Udtræk fra titel';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get editEntryDateDialogShift => 'Shift';
|
String get editEntryDateDialogShift => 'Skift';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get editEntryDateDialogSourceFileModifiedDate => 'File modified date';
|
String get editEntryDateDialogSourceFileModifiedDate => 'Filens ændringsdato';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get durationDialogHours => 'Timer';
|
String get durationDialogHours => 'Timer';
|
||||||
|
@ -1000,7 +1000,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get editEntryLocationDialogChooseOnMap => 'Vælg på kort';
|
String get editEntryLocationDialogChooseOnMap => 'Vælg på kort';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get editEntryLocationDialogImportGpx => 'Import GPX';
|
String get editEntryLocationDialogImportGpx => 'Importér GPX';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get editEntryLocationDialogLatitude => 'Breddegrad';
|
String get editEntryLocationDialogLatitude => 'Breddegrad';
|
||||||
|
@ -1009,7 +1009,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get editEntryLocationDialogLongitude => 'Længdegrad';
|
String get editEntryLocationDialogLongitude => 'Længdegrad';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get editEntryLocationDialogTimeShift => 'Time shift';
|
String get editEntryLocationDialogTimeShift => 'Tidsskift';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get locationPickerUseThisLocationButton => 'Brug denne placering';
|
String get locationPickerUseThisLocationButton => 'Brug denne placering';
|
||||||
|
@ -1021,7 +1021,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get removeEntryMetadataDialogTitle => 'Fjernelse af metadata';
|
String get removeEntryMetadataDialogTitle => 'Fjernelse af metadata';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get removeEntryMetadataDialogAll => 'All';
|
String get removeEntryMetadataDialogAll => 'Alle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get removeEntryMetadataDialogMore => 'Mere';
|
String get removeEntryMetadataDialogMore => 'Mere';
|
||||||
|
@ -1045,10 +1045,10 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get videoStreamSelectionDialogOff => 'Fra';
|
String get videoStreamSelectionDialogOff => 'Fra';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoStreamSelectionDialogTrack => 'Track';
|
String get videoStreamSelectionDialogTrack => 'Spor';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get videoStreamSelectionDialogNoSelection => 'There are no other tracks.';
|
String get videoStreamSelectionDialogNoSelection => 'Der er ingen andre spor.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get genericSuccessFeedback => 'Færdig!';
|
String get genericSuccessFeedback => 'Færdig!';
|
||||||
|
@ -1174,7 +1174,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get aboutDataUsageClearCache => 'Ryd cache';
|
String get aboutDataUsageClearCache => 'Ryd cache';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutCreditsSectionTitle => 'Credits';
|
String get aboutCreditsSectionTitle => 'Kreditering';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get aboutCreditsWorldAtlas1 => 'Denne app bruger en TopoJSON-fil fra';
|
String get aboutCreditsWorldAtlas1 => 'Denne app bruger en TopoJSON-fil fra';
|
||||||
|
@ -1428,10 +1428,10 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get collectionEmptyGrantAccessButtonLabel => 'Giv adgang';
|
String get collectionEmptyGrantAccessButtonLabel => 'Giv adgang';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get collectionSelectSectionTooltip => 'Select section';
|
String get collectionSelectSectionTooltip => 'Vælg sektion';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get collectionDeselectSectionTooltip => 'Deselect section';
|
String get collectionDeselectSectionTooltip => 'Fravælg sektion';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get drawerAboutButton => 'Om';
|
String get drawerAboutButton => 'Om';
|
||||||
|
@ -1440,7 +1440,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get drawerSettingsButton => 'Indstillinger';
|
String get drawerSettingsButton => 'Indstillinger';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get drawerCollectionAll => 'All collection';
|
String get drawerCollectionAll => 'Alle samlinger';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get drawerCollectionFavourites => 'Favoritter';
|
String get drawerCollectionFavourites => 'Favoritter';
|
||||||
|
@ -1530,7 +1530,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get sortOrderLongestFirst => 'Længste først';
|
String get sortOrderLongestFirst => 'Længste først';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get albumGroupTier => 'By tier';
|
String get albumGroupTier => 'Efter kategori';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get albumGroupType => 'Efter type';
|
String get albumGroupType => 'Efter type';
|
||||||
|
@ -1542,7 +1542,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get albumGroupNone => 'Gruppér ikke';
|
String get albumGroupNone => 'Gruppér ikke';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get albumMimeTypeMixed => 'Mixed';
|
String get albumMimeTypeMixed => 'Blandet';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get albumPickPageTitleCopy => 'Kopiér til album';
|
String get albumPickPageTitleCopy => 'Kopiér til album';
|
||||||
|
@ -1794,7 +1794,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsThumbnailShowRawIcon => 'Vis RAW-ikon';
|
String get settingsThumbnailShowRawIcon => 'Vis RAW-ikon';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsThumbnailShowVideoDuration => 'Show video duration';
|
String get settingsThumbnailShowVideoDuration => 'Vis videovarighed';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsCollectionQuickActionsTile => 'Hurtighandlinger';
|
String get settingsCollectionQuickActionsTile => 'Hurtighandlinger';
|
||||||
|
@ -1806,7 +1806,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsCollectionQuickActionTabBrowsing => 'Browsing';
|
String get settingsCollectionQuickActionTabBrowsing => 'Browsing';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsCollectionQuickActionTabSelecting => 'Selecting';
|
String get settingsCollectionQuickActionTabSelecting => 'Valg';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsCollectionBrowsingQuickActionEditorBanner => 'Tryk og hold for at flytte knapper og vælge, hvilke handlinger der vises, når du gennemser elementer.';
|
String get settingsCollectionBrowsingQuickActionEditorBanner => 'Tryk og hold for at flytte knapper og vælge, hvilke handlinger der vises, når du gennemser elementer.';
|
||||||
|
@ -1815,13 +1815,13 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsCollectionSelectionQuickActionEditorBanner => 'Tryk og hold for at flytte knapper og vælge, hvilke handlinger der vises, når du vælger elementer.';
|
String get settingsCollectionSelectionQuickActionEditorBanner => 'Tryk og hold for at flytte knapper og vælge, hvilke handlinger der vises, når du vælger elementer.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsCollectionBurstPatternsTile => 'Burst patterns';
|
String get settingsCollectionBurstPatternsTile => 'Filnavnmønstre';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsCollectionBurstPatternsNone => 'Ingen';
|
String get settingsCollectionBurstPatternsNone => 'Ingen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsViewerSectionTitle => 'Viewer';
|
String get settingsViewerSectionTitle => 'Fremviser';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsViewerGestureSideTapNext => 'Tryk på skærmkanterne for at vise forrige/næste element';
|
String get settingsViewerGestureSideTapNext => 'Tryk på skærmkanterne for at vise forrige/næste element';
|
||||||
|
@ -1845,7 +1845,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsViewerQuickActionEditorPageTitle => 'Hurtighandlinger';
|
String get settingsViewerQuickActionEditorPageTitle => 'Hurtighandlinger';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsViewerQuickActionEditorBanner => 'Touch and hold to move buttons and select which actions are displayed in the viewer.';
|
String get settingsViewerQuickActionEditorBanner => 'Tryk og hold for at flytte knapper og vælge, hvilke handlinger der vises i fremviseren.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsViewerQuickActionEditorDisplayedButtonsSectionTitle => 'Viste knapper';
|
String get settingsViewerQuickActionEditorDisplayedButtonsSectionTitle => 'Viste knapper';
|
||||||
|
@ -1938,7 +1938,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsVideoPlaybackPageTitle => 'Afspilning';
|
String get settingsVideoPlaybackPageTitle => 'Afspilning';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsVideoEnableHardwareAcceleration => 'Hardware acceleration';
|
String get settingsVideoEnableHardwareAcceleration => 'Hardwareacceleration';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsVideoAutoPlay => 'Afspil automatisk';
|
String get settingsVideoAutoPlay => 'Afspil automatisk';
|
||||||
|
@ -2031,7 +2031,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsPrivacySectionTitle => 'Privatliv';
|
String get settingsPrivacySectionTitle => 'Privatliv';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsAllowInstalledAppAccess => 'Allow access to app inventory';
|
String get settingsAllowInstalledAppAccess => 'Tillad adgang til app-lager';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsAllowInstalledAppAccessSubtitle => 'Bruges til at forbedre albumvisning';
|
String get settingsAllowInstalledAppAccessSubtitle => 'Bruges til at forbedre albumvisning';
|
||||||
|
@ -2106,7 +2106,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get settingsThemeBrightnessDialogTitle => 'Tema';
|
String get settingsThemeBrightnessDialogTitle => 'Tema';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsThemeColorHighlights => 'Color highlights';
|
String get settingsThemeColorHighlights => 'Farvemarkeringer';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get settingsThemeEnableDynamicColor => 'Dynamisk farve';
|
String get settingsThemeEnableDynamicColor => 'Dynamisk farve';
|
||||||
|
@ -2210,7 +2210,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get viewerInfoPageTitle => 'Info';
|
String get viewerInfoPageTitle => 'Info';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get viewerInfoBackToViewerTooltip => 'Back to viewer';
|
String get viewerInfoBackToViewerTooltip => 'Tilbage til fremviser';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get viewerInfoUnknown => 'ukendt';
|
String get viewerInfoUnknown => 'ukendt';
|
||||||
|
@ -2279,7 +2279,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get mapAttributionStamen => 'Fliser af [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)';
|
String get mapAttributionStamen => 'Fliser af [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get openMapPageTooltip => 'View on Map page';
|
String get openMapPageTooltip => 'Se på kortside';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mapEmptyRegion => 'Ingen billeder i denne region';
|
String get mapEmptyRegion => 'Ingen billeder i denne region';
|
||||||
|
@ -2297,7 +2297,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get viewerInfoSearchFieldLabel => 'Søg i metadata';
|
String get viewerInfoSearchFieldLabel => 'Søg i metadata';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get viewerInfoSearchEmpty => 'No matching keys';
|
String get viewerInfoSearchEmpty => 'Ingen matchende nøgler';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get viewerInfoSearchSuggestionDate => 'Dato og tid';
|
String get viewerInfoSearchSuggestionDate => 'Dato og tid';
|
||||||
|
@ -2315,7 +2315,7 @@ class AppLocalizationsDa extends AppLocalizations {
|
||||||
String get viewerInfoSearchSuggestionRights => 'Rettigheder';
|
String get viewerInfoSearchSuggestionRights => 'Rettigheder';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get wallpaperUseScrollEffect => 'Use scroll effect on home screen';
|
String get wallpaperUseScrollEffect => 'Brug rulleeffekt på startside';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get tagEditorPageTitle => 'Rediger Tags';
|
String get tagEditorPageTitle => 'Rediger Tags';
|
||||||
|
|
|
@ -1006,7 +1006,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get editEntryLocationDialogLongitude => 'Längengrad';
|
String get editEntryLocationDialogLongitude => 'Längengrad';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get editEntryLocationDialogTimeShift => 'Time shift';
|
String get editEntryLocationDialogTimeShift => 'Zeitverschiebung';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get locationPickerUseThisLocationButton => 'Diesen Standort verwenden';
|
String get locationPickerUseThisLocationButton => 'Diesen Standort verwenden';
|
||||||
|
@ -1018,7 +1018,7 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||||
String get removeEntryMetadataDialogTitle => 'Entfernung von Metadaten';
|
String get removeEntryMetadataDialogTitle => 'Entfernung von Metadaten';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get removeEntryMetadataDialogAll => 'All';
|
String get removeEntryMetadataDialogAll => 'Alle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get removeEntryMetadataDialogMore => 'Mehr';
|
String get removeEntryMetadataDialogMore => 'Mehr';
|
||||||
|
|
|
@ -4548,7 +4548,7 @@ class AppLocalizationsZhHant extends AppLocalizationsZh {
|
||||||
String get mapAttributionOsmLiberty => '地圖由 [OpenMapTiles](https://www.openmaptiles.org/) 所提供,以 [CC BY](http://creativecommons.org/licenses/by/4.0) 授權 • 托管於 [OSM Americana](https://tile.ourmap.us)';
|
String get mapAttributionOsmLiberty => '地圖由 [OpenMapTiles](https://www.openmaptiles.org/) 所提供,以 [CC BY](http://creativecommons.org/licenses/by/4.0) 授權 • 托管於 [OSM Americana](https://tile.ourmap.us)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mapAttributionOpenTopoMap => '[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | 地圖由 [OpenTopoMap](https://opentopomap.org/),以 [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/) 授權';
|
String get mapAttributionOpenTopoMap => '[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | 地圖由 [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get mapAttributionOsmHot => '繪製於 [HOT](https://www.hotosm.org/) • 主辦方 [OSM France](https://openstreetmap.fr/)';
|
String get mapAttributionOsmHot => '繪製於 [HOT](https://www.hotosm.org/) • 主辦方 [OSM France](https://openstreetmap.fr/)';
|
||||||
|
|
|
@ -130,6 +130,8 @@ class Contributors {
|
||||||
Contributor('pitroig', 'ona@riseup.net'),
|
Contributor('pitroig', 'ona@riseup.net'),
|
||||||
Contributor('Rubén Castiñeiras Lorenzo', 'rcasl@outlook.com'),
|
Contributor('Rubén Castiñeiras Lorenzo', 'rcasl@outlook.com'),
|
||||||
Contributor('hanyang cheng', 'cinxiafortis@tutanota.de'),
|
Contributor('hanyang cheng', 'cinxiafortis@tutanota.de'),
|
||||||
|
Contributor('Chethan', 'chethan@users.noreply.hosted.weblate.org'),
|
||||||
|
Contributor('Prasannakumar T Bhat', 'pbhat99@gmail.com'),
|
||||||
// Contributor('Femini', 'nizamismidov4@gmail.com'), // Azerbaijani
|
// Contributor('Femini', 'nizamismidov4@gmail.com'), // Azerbaijani
|
||||||
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
|
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
|
||||||
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
|
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
|
||||||
|
@ -142,7 +144,6 @@ class Contributors {
|
||||||
// Contributor('AJ07', 'ajaykumarmeena676@gmail.com'), // Hindi
|
// Contributor('AJ07', 'ajaykumarmeena676@gmail.com'), // Hindi
|
||||||
// Contributor('Sartaj', 'ssaarrttaajj111@gmail.com'), // Hindi
|
// Contributor('Sartaj', 'ssaarrttaajj111@gmail.com'), // Hindi
|
||||||
// Contributor('Anurag Samota', 'anuragsamotasamota@gmail.com'), // Hindi
|
// Contributor('Anurag Samota', 'anuragsamotasamota@gmail.com'), // Hindi
|
||||||
// Contributor('Chethan', 'chethan@users.noreply.hosted.weblate.org'), // Kannada
|
|
||||||
// Contributor('GoRaN', 'gorangharib.909@gmail.com'), // Kurdish (Central)
|
// Contributor('GoRaN', 'gorangharib.909@gmail.com'), // Kurdish (Central)
|
||||||
// Contributor('Rasti K5', 'rasti.khdhr@gmail.com'), // Kurdish (Central)
|
// Contributor('Rasti K5', 'rasti.khdhr@gmail.com'), // Kurdish (Central)
|
||||||
// Contributor('Raman', 'xysed@tutanota.com'), // Malayalam
|
// Contributor('Raman', 'xysed@tutanota.com'), // Malayalam
|
||||||
|
|
|
@ -496,13 +496,8 @@ class LocalMediaDbUpgrader {
|
||||||
static Future<void> _upgradeFrom14(Database db) async {
|
static Future<void> _upgradeFrom14(Database db) async {
|
||||||
debugPrint('upgrading DB from v14');
|
debugPrint('upgrading DB from v14');
|
||||||
|
|
||||||
// no schema changes, but v1.12.4 may have corrupted the DB,
|
// transitional upgrade previously used to sanitize rebuildable tables
|
||||||
// so we clear rebuildable tables
|
// (dateTakenTable, metadataTable, addressTable, trashTable, videoPlaybackTable)
|
||||||
final tables = [dateTakenTable, metadataTable, addressTable, trashTable, videoPlaybackTable];
|
// for users with a potentially corrupted DB following upgrade to v1.12.4
|
||||||
await Future.forEach(tables, (table) async {
|
|
||||||
if (await db.tableExists(table)) {
|
|
||||||
await db.delete(table, where: '1');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/image_providers/full_image_provider.dart';
|
||||||
import 'package:aves/image_providers/thumbnail_provider.dart';
|
import 'package:aves/image_providers/thumbnail_provider.dart';
|
||||||
import 'package:aves/image_providers/uri_image_provider.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
class EntryCache {
|
class EntryCache {
|
||||||
|
@ -30,7 +30,7 @@ class EntryCache {
|
||||||
int? pageId;
|
int? pageId;
|
||||||
|
|
||||||
// evict fullscreen image
|
// evict fullscreen image
|
||||||
await UriImage(
|
await FullImage(
|
||||||
uri: uri,
|
uri: uri,
|
||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
pageId: pageId,
|
pageId: pageId,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:aves/image_providers/full_image_provider.dart';
|
||||||
import 'package:aves/image_providers/region_provider.dart';
|
import 'package:aves/image_providers/region_provider.dart';
|
||||||
import 'package:aves/image_providers/thumbnail_provider.dart';
|
import 'package:aves/image_providers/thumbnail_provider.dart';
|
||||||
import 'package:aves/image_providers/uri_image_provider.dart';
|
|
||||||
import 'package:aves/model/entry/cache.dart';
|
import 'package:aves/model/entry/cache.dart';
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/utils/math_utils.dart';
|
import 'package:aves/utils/math_utils.dart';
|
||||||
|
@ -49,7 +49,7 @@ extension ExtraAvesEntryImages on AvesEntry {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
UriImage get uriImage => UriImage(
|
FullImage get fullImage => FullImage(
|
||||||
uri: uri,
|
uri: uri,
|
||||||
mimeType: mimeType,
|
mimeType: mimeType,
|
||||||
pageId: pageId,
|
pageId: pageId,
|
||||||
|
|
|
@ -157,7 +157,7 @@ class MappedGeoTiff with MapOverlay {
|
||||||
String get id => entry.uri;
|
String get id => entry.uri;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImageProvider get imageProvider => entry.uriImage;
|
ImageProvider get imageProvider => entry.fullImage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get canOverlay => center != null;
|
bool get canOverlay => center != null;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import 'package:aves/services/common/output_buffer.dart';
|
||||||
import 'package:aves/services/common/service_policy.dart';
|
import 'package:aves/services/common/service_policy.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/services/media/byte_receiving_codec.dart';
|
import 'package:aves/services/media/byte_receiving_codec.dart';
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:streams_channel/streams_channel.dart';
|
import 'package:streams_channel/streams_channel.dart';
|
||||||
|
@ -55,7 +56,9 @@ abstract class MediaFetchService {
|
||||||
int? priority,
|
int? priority,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> clearSizedThumbnailDiskCache();
|
Future<void> clearImageDiskCache();
|
||||||
|
|
||||||
|
Future<void> clearImageMemoryCache();
|
||||||
|
|
||||||
bool cancelRegion(Object taskKey);
|
bool cancelRegion(Object taskKey);
|
||||||
|
|
||||||
|
@ -255,9 +258,18 @@ class PlatformMediaFetchService implements MediaFetchService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> clearSizedThumbnailDiskCache() async {
|
Future<void> clearImageDiskCache() async {
|
||||||
try {
|
try {
|
||||||
return _platformObject.invokeMethod('clearSizedThumbnailDiskCache');
|
return _platformObject.invokeMethod('clearImageDiskCache');
|
||||||
|
} on PlatformException catch (e, stack) {
|
||||||
|
await reportService.recordError(e, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> clearImageMemoryCache() async {
|
||||||
|
try {
|
||||||
|
return _platformObject.invokeMethod('clearImageMemoryCache');
|
||||||
} on PlatformException catch (e, stack) {
|
} on PlatformException catch (e, stack) {
|
||||||
await reportService.recordError(e, stack);
|
await reportService.recordError(e, stack);
|
||||||
}
|
}
|
||||||
|
@ -317,7 +329,7 @@ class PlatformMediaFetchService implements MediaFetchService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@immutable
|
@immutable
|
||||||
class ImageRequest {
|
class ImageRequest extends Equatable {
|
||||||
final String uri;
|
final String uri;
|
||||||
final String mimeType;
|
final String mimeType;
|
||||||
final int? rotationDegrees;
|
final int? rotationDegrees;
|
||||||
|
@ -327,6 +339,9 @@ class ImageRequest {
|
||||||
final int? sizeBytes;
|
final int? sizeBytes;
|
||||||
final BytesReceivedCallback? onBytesReceived;
|
final BytesReceivedCallback? onBytesReceived;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [uri, mimeType, rotationDegrees, isFlipped, isAnimated, pageId, sizeBytes, onBytesReceived];
|
||||||
|
|
||||||
const ImageRequest(
|
const ImageRequest(
|
||||||
this.uri,
|
this.uri,
|
||||||
this.mimeType, {
|
this.mimeType, {
|
||||||
|
|
|
@ -88,7 +88,7 @@ class _AboutDataUsageState extends State<AboutDataUsage> with FeedbackMixin {
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await storageService.deleteTempDirectory();
|
await storageService.deleteTempDirectory();
|
||||||
await storageService.deleteExternalCache();
|
await storageService.deleteExternalCache();
|
||||||
await mediaFetchService.clearSizedThumbnailDiskCache();
|
await mediaFetchService.clearImageDiskCache();
|
||||||
imageCache.clear();
|
imageCache.clear();
|
||||||
_reload();
|
_reload();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
|
@ -70,7 +70,6 @@ class AvesApp extends StatefulWidget {
|
||||||
'fi', // Finnish
|
'fi', // Finnish
|
||||||
'he', // Hebrew
|
'he', // Hebrew
|
||||||
'hi', // Hindi
|
'hi', // Hindi
|
||||||
'kn', // Kannada
|
|
||||||
'ml', // Malayalam
|
'ml', // Malayalam
|
||||||
'my', // Burmese
|
'my', // Burmese
|
||||||
'or', // Odia
|
'or', // Odia
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
@ -220,6 +221,7 @@ class AvesFloatingBar extends StatefulWidget {
|
||||||
class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
||||||
// prevent expensive blurring when the current page is hidden
|
// prevent expensive blurring when the current page is hidden
|
||||||
final ValueNotifier<bool> _isBlurAllowedNotifier = ValueNotifier(true);
|
final ValueNotifier<bool> _isBlurAllowedNotifier = ValueNotifier(true);
|
||||||
|
Timer? _blurBlockTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
|
@ -240,6 +242,8 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
||||||
@override
|
@override
|
||||||
void didPopNext() {
|
void didPopNext() {
|
||||||
// post to prevent single frame flash during hero
|
// post to prevent single frame flash during hero
|
||||||
|
_blurBlockTimer?.cancel();
|
||||||
|
_blurBlockTimer = null;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_isBlurAllowedNotifier.value = true;
|
_isBlurAllowedNotifier.value = true;
|
||||||
|
@ -249,8 +253,9 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPushNext() {
|
void didPushNext() {
|
||||||
// post to prevent single frame flash during hero
|
// delay blur disabling, otherwise visual artifacts appear during page transition with Impeller
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
_blurBlockTimer?.cancel();
|
||||||
|
_blurBlockTimer = Timer(ADurations.pageTransitionLoose, () {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_isBlurAllowedNotifier.value = false;
|
_isBlurAllowedNotifier.value = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,20 @@ class DebugCacheSection extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DebugCacheSectionState extends State<DebugCacheSection> with AutomaticKeepAliveClientMixin {
|
class _DebugCacheSectionState extends State<DebugCacheSection> with AutomaticKeepAliveClientMixin {
|
||||||
|
final TextEditingController _imageCacheSizeTextController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_imageCacheSizeTextController.text = '${imageCache.maximumSizeBytes}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_imageCacheSizeTextController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
@ -41,6 +55,31 @@ class _DebugCacheSectionState extends State<DebugCacheSection> with AutomaticKee
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: _imageCacheSizeTextController,
|
||||||
|
decoration: const InputDecoration(labelText: 'imageCache size bytes'),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
final size = int.tryParse(_imageCacheSizeTextController.text);
|
||||||
|
if (size != null) {
|
||||||
|
imageCache.maximumSizeBytes = size;
|
||||||
|
} else {
|
||||||
|
_imageCacheSizeTextController.text = '${imageCache.maximumSizeBytes}';
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: const Text('Apply'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Expanded(
|
const Expanded(
|
||||||
|
@ -48,7 +87,19 @@ class _DebugCacheSectionState extends State<DebugCacheSection> with AutomaticKee
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: mediaFetchService.clearSizedThumbnailDiskCache,
|
onPressed: mediaFetchService.clearImageDiskCache,
|
||||||
|
child: const Text('Clear'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Expanded(
|
||||||
|
child: Text('Glide memory cache: ?'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: mediaFetchService.clearImageMemoryCache,
|
||||||
child: const Text('Clear'),
|
child: const Text('Clear'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -24,6 +24,7 @@ class SupportedLocales {
|
||||||
'is': 'Íslenska',
|
'is': 'Íslenska',
|
||||||
'it': 'Italiano',
|
'it': 'Italiano',
|
||||||
'ja': '日本語',
|
'ja': '日本語',
|
||||||
|
'kn': 'ಕನ್ನಡ',
|
||||||
'ko': '한국어',
|
'ko': '한국어',
|
||||||
'lt': 'Lietuvių',
|
'lt': 'Lietuvių',
|
||||||
'nb': 'Norsk (Bokmål)',
|
'nb': 'Norsk (Bokmål)',
|
||||||
|
|
|
@ -118,7 +118,7 @@ class EntryPrinter with FeedbackMixin {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return pdf.Image(
|
return pdf.Image(
|
||||||
await flutterImageProvider(entry.uriImage),
|
await flutterImageProvider(entry.fullImage),
|
||||||
fit: _fit,
|
fit: _fit,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ class WallpaperButtons extends StatelessWidget with FeedbackMixin {
|
||||||
} else {
|
} else {
|
||||||
// provider image is already rotated, but not cropped
|
// provider image is already rotated, but not cropped
|
||||||
needCrop = true;
|
needCrop = true;
|
||||||
provider = entry.uriImage;
|
provider = entry.fullImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (provider == null) return null;
|
if (provider == null) return null;
|
||||||
|
|
|
@ -90,7 +90,7 @@ class _PanoramaPageState extends State<PanoramaPage> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Image(
|
child: Image(
|
||||||
image: entry.uriImage,
|
image: entry.fullImage,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/entry/extensions/props.dart';
|
import 'package:aves/model/entry/extensions/props.dart';
|
||||||
|
@ -15,7 +16,7 @@ import 'package:leak_tracker/leak_tracker.dart';
|
||||||
class VideoConductor {
|
class VideoConductor {
|
||||||
final CollectionLens? _collection;
|
final CollectionLens? _collection;
|
||||||
final List<AvesVideoController> _controllers = [];
|
final List<AvesVideoController> _controllers = [];
|
||||||
final List<StreamSubscription> _subscriptions = [];
|
final Map<AvesVideoController, StreamSubscription> _subscriptions = {};
|
||||||
final PlaybackStateHandler _playbackStateHandler = DatabasePlaybackStateHandler();
|
final PlaybackStateHandler _playbackStateHandler = DatabasePlaybackStateHandler();
|
||||||
|
|
||||||
final ValueNotifier<AvesVideoController?> playingVideoControllerNotifier = ValueNotifier(null);
|
final ValueNotifier<AvesVideoController?> playingVideoControllerNotifier = ValueNotifier(null);
|
||||||
|
@ -36,9 +37,6 @@ class VideoConductor {
|
||||||
if (kFlutterMemoryAllocationsEnabled) {
|
if (kFlutterMemoryAllocationsEnabled) {
|
||||||
LeakTracking.dispatchObjectDisposed(object: this);
|
LeakTracking.dispatchObjectDisposed(object: this);
|
||||||
}
|
}
|
||||||
_subscriptions
|
|
||||||
..forEach((sub) => sub.cancel())
|
|
||||||
..clear();
|
|
||||||
await _disposeAll();
|
await _disposeAll();
|
||||||
playingVideoControllerNotifier.dispose();
|
playingVideoControllerNotifier.dispose();
|
||||||
_controllers.clear();
|
_controllers.clear();
|
||||||
|
@ -47,22 +45,24 @@ class VideoConductor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AvesVideoController getOrCreateController(AvesEntry entry, {int? maxControllerCount}) {
|
Future<AvesVideoController> getOrCreateController(AvesEntry entry, {int? maxControllerCount}) async {
|
||||||
var controller = getController(entry);
|
var controller = getController(entry);
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
_controllers.remove(controller);
|
_controllers.remove(controller);
|
||||||
} else {
|
} else {
|
||||||
|
maxControllerCount = max(_defaultMaxControllerCount, maxControllerCount ?? 0);
|
||||||
|
while (_controllers.length >= maxControllerCount) {
|
||||||
|
await _disposeController(_controllers.removeLast());
|
||||||
|
}
|
||||||
|
await deviceService.requestGarbageCollection();
|
||||||
controller = videoControllerFactory.buildController(
|
controller = videoControllerFactory.buildController(
|
||||||
entry,
|
entry,
|
||||||
playbackStateHandler: _playbackStateHandler,
|
playbackStateHandler: _playbackStateHandler,
|
||||||
settings: settings,
|
settings: settings,
|
||||||
);
|
);
|
||||||
_subscriptions.add(controller.statusStream.listen((event) => _onControllerStatusChanged(entry, controller!, event)));
|
_subscriptions[controller] = controller.statusStream.listen((event) => _onControllerStatusChanged(entry, controller!, event));
|
||||||
}
|
}
|
||||||
_controllers.insert(0, controller);
|
_controllers.insert(0, controller);
|
||||||
while (_controllers.length > (maxControllerCount ?? _defaultMaxControllerCount)) {
|
|
||||||
_controllers.removeLast().dispose();
|
|
||||||
}
|
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,9 +99,14 @@ class VideoConductor {
|
||||||
|
|
||||||
Future<void> _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach<AvesVideoController>(_controllers, action);
|
Future<void> _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach<AvesVideoController>(_controllers, action);
|
||||||
|
|
||||||
Future<void> _disposeAll() => _applyToAll((controller) => controller.dispose());
|
Future<void> _disposeAll() => _applyToAll(_disposeController);
|
||||||
|
|
||||||
Future<void> pauseAll() => _applyToAll((controller) => controller.pause());
|
Future<void> pauseAll() => _applyToAll((controller) => controller.pause());
|
||||||
|
|
||||||
Future<void> muteAll(bool muted) => _applyToAll((controller) => controller.mute(muted));
|
Future<void> muteAll(bool muted) => _applyToAll((controller) => controller.mute(muted));
|
||||||
|
|
||||||
|
Future<void> _disposeController(AvesVideoController controller) async {
|
||||||
|
await _subscriptions.remove(controller)?.cancel();
|
||||||
|
await controller.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ mixin EntryViewControllerMixin<T extends StatefulWidget> on State<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initVideoController(AvesEntry entry) async {
|
Future<void> _initVideoController(AvesEntry entry) async {
|
||||||
final controller = context.read<VideoConductor>().getOrCreateController(entry);
|
final controller = await context.read<VideoConductor>().getOrCreateController(entry);
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
||||||
if (videoAutoPlayEnabled || entry.isAnimated) {
|
if (videoAutoPlayEnabled || entry.isAnimated) {
|
||||||
|
@ -157,7 +157,9 @@ mixin EntryViewControllerMixin<T extends StatefulWidget> on State<T> {
|
||||||
if (videoPageEntries.isNotEmpty) {
|
if (videoPageEntries.isNotEmpty) {
|
||||||
// init video controllers for all pages that could need it
|
// init video controllers for all pages that could need it
|
||||||
final videoConductor = context.read<VideoConductor>();
|
final videoConductor = context.read<VideoConductor>();
|
||||||
videoPageEntries.forEach((entry) => videoConductor.getOrCreateController(entry, maxControllerCount: videoPageEntries.length));
|
await Future.forEach(videoPageEntries, (entry) async {
|
||||||
|
await videoConductor.getOrCreateController(entry, maxControllerCount: videoPageEntries.length);
|
||||||
|
});
|
||||||
|
|
||||||
// auto play/pause when changing page
|
// auto play/pause when changing page
|
||||||
Future<void> _onPageChanged() async {
|
Future<void> _onPageChanged() async {
|
||||||
|
|
|
@ -43,6 +43,8 @@ class _RasterImageViewState extends State<RasterImageView> {
|
||||||
final ValueNotifier<bool> _fullImageLoaded = ValueNotifier(false);
|
final ValueNotifier<bool> _fullImageLoaded = ValueNotifier(false);
|
||||||
ImageInfo? _fullImageInfo;
|
ImageInfo? _fullImageInfo;
|
||||||
|
|
||||||
|
static const double _tilesByShortestSide = 2;
|
||||||
|
|
||||||
AvesEntry get entry => widget.entry;
|
AvesEntry get entry => widget.entry;
|
||||||
|
|
||||||
ValueNotifier<ViewState> get viewStateNotifier => widget.viewStateNotifier;
|
ValueNotifier<ViewState> get viewStateNotifier => widget.viewStateNotifier;
|
||||||
|
@ -61,7 +63,7 @@ class _RasterImageViewState extends State<RasterImageView> {
|
||||||
region: fullImageRegion,
|
region: fullImageRegion,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return entry.uriImage;
|
return entry.fullImage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +160,7 @@ class _RasterImageViewState extends State<RasterImageView> {
|
||||||
|
|
||||||
void _initTiling(Size viewportSize) {
|
void _initTiling(Size viewportSize) {
|
||||||
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
|
||||||
_tileSide = viewportSize.shortestSide * devicePixelRatio;
|
_tileSide = viewportSize.shortestSide * devicePixelRatio / _tilesByShortestSide;
|
||||||
// scale for initial state `contained`
|
// scale for initial state `contained`
|
||||||
final containedScale = min(viewportSize.width / _displaySize.width, viewportSize.height / _displaySize.height);
|
final containedScale = min(viewportSize.width / _displaySize.width, viewportSize.height / _displaySize.height);
|
||||||
_maxSampleSize = ExtraAvesEntryImages.sampleSizeForScale(magnifierScale: containedScale, devicePixelRatio: devicePixelRatio);
|
_maxSampleSize = ExtraAvesEntryImages.sampleSizeForScale(magnifierScale: containedScale, devicePixelRatio: devicePixelRatio);
|
||||||
|
|
|
@ -58,7 +58,7 @@ class _VideoCoverState extends State<VideoCover> {
|
||||||
Size get videoDisplaySize => widget.videoDisplaySize;
|
Size get videoDisplaySize => widget.videoDisplaySize;
|
||||||
|
|
||||||
// use the high res photo as cover for the video part of a motion photo
|
// use the high res photo as cover for the video part of a motion photo
|
||||||
ImageProvider get videoCoverUriImage => (mainEntry.isMotionPhoto ? mainEntry : entry).uriImage;
|
ImageProvider get videoCoverUriImage => (mainEntry.isMotionPhoto ? mainEntry : entry).fullImage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
102
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _flutterfire_internals
|
name: _flutterfire_internals
|
||||||
sha256: "7fd72d77a7487c26faab1d274af23fb008763ddc10800261abbfb2c067f183d5"
|
sha256: de9ecbb3ddafd446095f7e833c853aff2fa1682b017921fe63a833f9d6f0e422
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.53"
|
version: "1.3.54"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -29,10 +29,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "0c64e928dcbefddecd234205422bcfc2b5e6d31be0b86fef0d0dd48d7b4c9742"
|
sha256: "7dcbd0f87fe5f61cb28da39a1a8b70dbc106e2fe0516f7836eb7bb2948481a12"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.4"
|
version: "4.0.5"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -296,10 +296,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
sha256: f4d8f49574a4e396f34567f3eec4d38ab9c3910818dec22ca42b2a467c685d8b
|
sha256: "017d17d9915670e6117497e640b2859e0b868026ea36bf3a57feb28c3b97debe"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.12.1"
|
version: "3.13.0"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -312,26 +312,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
sha256: faa5a76f6380a9b90b53bc3bdcb85bc7926a382e0709b9b5edac9f7746651493
|
sha256: "129a34d1e0fb62e2b488d988a1fc26cc15636357e50944ffee2862efe8929b23"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.21.1"
|
version: "2.22.0"
|
||||||
firebase_crashlytics:
|
firebase_crashlytics:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_crashlytics
|
name: firebase_crashlytics
|
||||||
sha256: d672dad83e6e99b826599fef63dbe71bac70633d5c3df90c124e986e1461e79b
|
sha256: f3fa4a17c2f061b16b2e3ac7aaed889ae954b8952d0fd3e0009a9870cde7bbd2
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.4"
|
version: "4.3.5"
|
||||||
firebase_crashlytics_platform_interface:
|
firebase_crashlytics_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_crashlytics_platform_interface
|
name: firebase_crashlytics_platform_interface
|
||||||
sha256: b2468a5cd54051dd31ca332a5c35f1bcbfb21b0135f84d4606c3275a226c0321
|
sha256: cedfbe39927711c0e56fc38bfecbd89e17816b21698a3d88d63298c530ed375c
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.8.4"
|
version: "3.8.5"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -352,10 +352,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: flex_seed_scheme
|
name: flex_seed_scheme
|
||||||
sha256: d3ba3c5c92d2d79d45e94b4c6c71d01fac3c15017da1545880c53864da5dfeb0
|
sha256: b06d8b367b84cbf7ca5c5603c858fa5edae88486c4e4da79ac1044d73b6c62ec
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.0"
|
version: "3.5.1"
|
||||||
floating:
|
floating:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -440,10 +440,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: flutter_markdown
|
name: flutter_markdown
|
||||||
sha256: e7bbc718adc9476aa14cfddc1ef048d2e21e4e8f18311aaac723266db9f9e7b5
|
sha256: "634622a3a826d67cb05c0e3e576d1812c430fa98404e95b60b131775c73d76ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.6+2"
|
version: "0.7.7"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -535,26 +535,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter
|
name: google_maps_flutter
|
||||||
sha256: "621125e35e81ca39ef600e45243d2be93167e61def72bc7207b0c4a635c58506"
|
sha256: "830d8f7b51b4a950bf0d7daa675324fed6c9beb57a7ecca2a59018270c96b4e0"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.10.1"
|
version: "2.12.1"
|
||||||
google_maps_flutter_android:
|
google_maps_flutter_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_android
|
name: google_maps_flutter_android
|
||||||
sha256: "3b3f55d6b4f2bde6bbe80dca0bf8d228313005c9ce8a97a1d24257600d8c92e5"
|
sha256: "0ede4ae8326335c0c007c8c7a8c9737449263123385e2bdf49f3e71103b2dc2e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.14.14"
|
version: "2.16.0"
|
||||||
google_maps_flutter_ios:
|
google_maps_flutter_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_ios
|
name: google_maps_flutter_ios
|
||||||
sha256: "6f798adb0aa1db5adf551f2e39e24bd06c8c0fbe4de912fb2d9b5b3f48147b02"
|
sha256: ef72c822930ce69515cb91c10cd88cfb8b26296f765808a43cbc9a10eaffacfe
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.13.2"
|
version: "2.15.0"
|
||||||
google_maps_flutter_platform_interface:
|
google_maps_flutter_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -567,10 +567,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: google_maps_flutter_web
|
name: google_maps_flutter_web
|
||||||
sha256: bbeb93807a34bfeebdb7ace506bd2bc400a3915dc96736254fea721eb264caa0
|
sha256: a45786ea6691cc7cdbe2cf3ce2c2daf4f82a885745666b4a36baada3a4e12897
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.5.11"
|
version: "0.5.12"
|
||||||
gpx:
|
gpx:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -623,10 +623,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: image
|
name: image
|
||||||
sha256: "13d3349ace88f12f4a0d175eb5c12dcdd39d35c4c109a8a13dfeb6d0bd9e31c3"
|
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.3"
|
version: "4.5.4"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -788,31 +788,29 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.7296"
|
version: "7.0.7296"
|
||||||
media_kit:
|
media_kit:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: media_kit
|
name: media_kit
|
||||||
ref: d2145a50f68394096845915a28874341fbf5b3fe
|
sha256: "48c10c3785df5d88f0eef970743f8c99b2e5da2b34b9d8f9876e598f62d9e776"
|
||||||
resolved-ref: d2145a50f68394096845915a28874341fbf5b3fe
|
url: "https://pub.dev"
|
||||||
url: "https://github.com/media-kit/media-kit.git"
|
source: hosted
|
||||||
source: git
|
version: "1.2.0"
|
||||||
version: "1.1.11"
|
|
||||||
media_kit_libs_android_video:
|
media_kit_libs_android_video:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: media_kit_libs_android_video
|
name: media_kit_libs_android_video
|
||||||
sha256: "9dd8012572e4aff47516e55f2597998f0a378e3d588d0fad0ca1f11a53ae090c"
|
sha256: adff9b571b8ead0867f9f91070f8df39562078c0eb3371d88b9029a2d547d7b7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.6"
|
version: "1.3.7"
|
||||||
media_kit_video:
|
media_kit_video:
|
||||||
dependency: "direct overridden"
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
path: media_kit_video
|
name: media_kit_video
|
||||||
ref: d2145a50f68394096845915a28874341fbf5b3fe
|
sha256: a656a9463298c1adc64c57f2d012874f7f2900f0c614d9545a3e7b8bb9e2137b
|
||||||
resolved-ref: d2145a50f68394096845915a28874341fbf5b3fe
|
url: "https://pub.dev"
|
||||||
url: "https://github.com/media-kit/media-kit.git"
|
source: hosted
|
||||||
source: git
|
version: "1.3.0"
|
||||||
version: "1.2.5"
|
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1171,10 +1169,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: provider
|
name: provider
|
||||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
sha256: "489024f942069c2920c844ee18bb3d467c69e48955a4f32d1677f71be103e310"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.2"
|
version: "6.1.4"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1267,10 +1265,10 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: "846849e3e9b68f3ef4b60c60cf4b3e02e9321bc7f4d8c4692cf87ffa82fc8a3a"
|
sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.2"
|
version: "2.5.3"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1577,10 +1575,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
|
sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.3.2"
|
version: "6.3.3"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1673,18 +1671,18 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: volume_controller
|
name: volume_controller
|
||||||
sha256: "30863a51338db47fe16f92902b1a6c4ee5e15c9287b46573d7c2eb6be1f197d2"
|
sha256: e82fd689bb8e1fe8e64be3fa5946ff8699058f8cf9f4c1679acdba20cda7f5bd
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.3.3"
|
||||||
wakelock_plus:
|
wakelock_plus:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: wakelock_plus
|
name: wakelock_plus
|
||||||
sha256: "36c88af0b930121941345306d259ec4cc4ecca3b151c02e3a9e71aede83c615e"
|
sha256: b90fbcc8d7bdf3b883ea9706d9d76b9978cb1dfa4351fcc8014d6ec31a493354
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.10"
|
version: "1.2.11"
|
||||||
wakelock_plus_platform_interface:
|
wakelock_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
17
pubspec.yaml
|
@ -7,7 +7,7 @@ repository: https://github.com/deckerst/aves
|
||||||
# - play changelog: /whatsnew/whatsnew-en-US
|
# - play changelog: /whatsnew/whatsnew-en-US
|
||||||
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
|
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
|
||||||
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
|
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
|
||||||
version: 1.12.8+148
|
version: 1.12.9+149
|
||||||
publish_to: none
|
publish_to: none
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -130,18 +130,6 @@ dependencies:
|
||||||
volume_controller:
|
volume_controller:
|
||||||
xml:
|
xml:
|
||||||
|
|
||||||
dependency_overrides:
|
|
||||||
media_kit:
|
|
||||||
git:
|
|
||||||
url: https://github.com/media-kit/media-kit.git
|
|
||||||
ref: d2145a50f68394096845915a28874341fbf5b3fe
|
|
||||||
path: media_kit
|
|
||||||
media_kit_video:
|
|
||||||
git:
|
|
||||||
url: https://github.com/media-kit/media-kit.git
|
|
||||||
ref: d2145a50f68394096845915a28874341fbf5b3fe
|
|
||||||
path: media_kit_video
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
@ -171,9 +159,6 @@ flutter:
|
||||||
################################################################################
|
################################################################################
|
||||||
# Test driver
|
# Test driver
|
||||||
|
|
||||||
# capture shaders (profile mode, real device only):
|
|
||||||
# % ./flutterw drive --flavor play -t test_driver/driver_shaders.dart --profile --cache-sksl --write-sksl-on-exit shaders.sksl.json
|
|
||||||
|
|
||||||
# generate screenshots (profile mode, specific collection):
|
# generate screenshots (profile mode, specific collection):
|
||||||
# % ./flutterw drive --flavor play -t test_driver/driver_screenshots.dart --profile
|
# % ./flutterw drive --flavor play -t test_driver/driver_screenshots.dart --profile
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,8 @@ Future<void> configureAndLaunch() async {
|
||||||
..coordinateFormat = CoordinateFormat.dms
|
..coordinateFormat = CoordinateFormat.dms
|
||||||
..unitSystem = UnitSystem.metric
|
..unitSystem = UnitSystem.metric
|
||||||
// map
|
// map
|
||||||
..mapStyle = EntryMapStyle.googleNormal;
|
..mapStyle = EntryMapStyle.googleNormal
|
||||||
|
// debug
|
||||||
|
..debugShowViewerTiles = false;
|
||||||
app.main();
|
app.main();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
In v1.12.8:
|
In v1.12.9:
|
||||||
- play more kinds of motion photos
|
- play more kinds of motion photos
|
||||||
- enjoy the app in Galician
|
- enjoy the app in Galician and Kannada
|
||||||
Full changelog available on GitHub
|
Full changelog available on GitHub
|