diff --git a/android/app/build.gradle b/android/app/build.gradle index d81242fbf..1a9404a93 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -211,6 +211,7 @@ dependencies { implementation 'com.github.deckerst.mp4parser:isoparser:4cc0c5d06c' implementation 'com.github.deckerst.mp4parser:muxer:4cc0c5d06c' implementation 'com.github.deckerst:pixymeta-android:9ec7097f17' + implementation project(':exifinterface') testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt index 9f223655f..0f230102b 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt @@ -12,7 +12,7 @@ import android.os.Handler import android.os.Looper import android.provider.MediaStore import android.util.Log -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import com.drew.metadata.file.FileTypeDirectory import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.metadata.ExifInterfaceHelper diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt index 06c64cd50..16b40c40f 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt @@ -6,7 +6,7 @@ import android.media.MediaMetadataRetriever import android.net.Uri import android.os.Build import android.util.Log -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import com.adobe.internal.xmp.XMPException import com.adobe.internal.xmp.XMPMeta import com.adobe.internal.xmp.XMPMetaFactory diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt index d87f6076f..99915191c 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt @@ -21,11 +21,13 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E private var knownEntries: Map? = null private var directory: String? = null + private var safe: Boolean = false init { if (arguments is Map<*, *>) { knownEntries = (arguments["knownEntries"] as? Map<*, *>?)?.map { (it.key as Number?)?.toLong() to it.value as Int? }?.toMap() directory = arguments["directory"] as String? + safe = arguments.getOrDefault("safe", false) as Boolean } } @@ -59,7 +61,7 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E } private fun fetchAll() { - MediaStoreImageProvider().fetchAll(context, knownEntries ?: emptyMap(), directory) { success(it) } + MediaStoreImageProvider().fetchAll(context, knownEntries ?: emptyMap(), directory, safe) { success(it) } endOfStream() } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt index f0d28fb3e..8b5aa7114 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt @@ -1,7 +1,7 @@ package deckers.thibault.aves.metadata import android.util.Log -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import com.drew.lang.Rational import com.drew.metadata.Directory import com.drew.metadata.exif.ExifDirectoryBase diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt index 04a21de0a..d97314915 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt @@ -2,7 +2,7 @@ package deckers.thibault.aves.metadata import android.content.Context import android.net.Uri -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import deckers.thibault.aves.utils.FileUtils.transferFrom import deckers.thibault.aves.utils.MimeTypes import deckers.thibault.aves.utils.StorageUtils diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt index 113562fee..7aea03e08 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt @@ -9,7 +9,7 @@ import android.net.Uri import android.os.Build import android.os.ParcelFileDescriptor import android.util.Log -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import com.adobe.internal.xmp.XMPMeta import com.drew.imaging.jpeg.JpegSegmentType import com.drew.metadata.exif.ExifDirectoryBase diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt index d339fda96..1be7e4236 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt @@ -5,7 +5,7 @@ import android.content.Context import android.graphics.BitmapFactory import android.media.MediaMetadataRetriever import android.net.Uri -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import com.drew.metadata.avi.AviDirectory import com.drew.metadata.exif.ExifIFD0Directory import com.drew.metadata.jpeg.JpegDirectory @@ -116,8 +116,8 @@ class SourceEntry { // metadata retrieval // expects entry with: uri, mimeType // finds: width, height, orientation/rotation, date, title, duration - fun fillPreCatalogMetadata(context: Context): SourceEntry { - if (isSvg) return this + fun fillPreCatalogMetadata(context: Context, safe: Boolean): SourceEntry { + if (isSvg || safe) return this if (isVideo) { fillVideoByMediaMetadataRetriever(context) if (isSized && hasDuration) return this diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt index bc45c1c2c..4405fe5e0 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt @@ -52,7 +52,7 @@ internal class FileImageProvider : ImageProvider() { callback.onFailure(e) } } - entry.fillPreCatalogMetadata(context) + entry.fillPreCatalogMetadata(context, safe = false) if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) { callback.onSuccess(entry.toMap()) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt index 9d4f6dea3..a5f56c454 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt @@ -11,7 +11,7 @@ import android.net.Uri import android.os.Binder import android.os.Build import android.util.Log -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import com.bumptech.glide.Glide import com.bumptech.glide.load.DecodeFormat import com.bumptech.glide.load.engine.DiskCacheStrategy diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt index 8fc3e5cef..39cd9858f 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt @@ -51,8 +51,10 @@ class MediaStoreImageProvider : ImageProvider() { context: Context, knownEntries: Map, directory: String?, + safe: Boolean, handleNewEntry: NewEntryHandler, ) { + Log.d(LOG_TAG, "fetching all media store items for ${knownEntries.size} known entries, directory=$directory safe=$safe") val isModified = fun(contentId: Long, dateModifiedSecs: Int): Boolean { val knownDate = knownEntries[contentId] return knownDate == null || knownDate < dateModifiedSecs @@ -82,8 +84,8 @@ class MediaStoreImageProvider : ImageProvider() { } else { handleNew = handleNewEntry } - fetchFrom(context, isModified, handleNew, IMAGE_CONTENT_URI, IMAGE_PROJECTION, selection, selectionArgs) - fetchFrom(context, isModified, handleNew, VIDEO_CONTENT_URI, VIDEO_PROJECTION, selection, selectionArgs) + fetchFrom(context, isModified, handleNew, IMAGE_CONTENT_URI, IMAGE_PROJECTION, selection, selectionArgs, safe = safe) + fetchFrom(context, isModified, handleNew, VIDEO_CONTENT_URI, VIDEO_PROJECTION, selection, selectionArgs, safe = safe) } // the provided URI can point to the wrong media collection, @@ -206,6 +208,7 @@ class MediaStoreImageProvider : ImageProvider() { selection: String? = null, selectionArgs: Array? = null, fileMimeType: String? = null, + safe: Boolean = false, ): Boolean { var found = false val orderBy = "${MediaStore.MediaColumns.DATE_MODIFIED} DESC" @@ -299,7 +302,7 @@ class MediaStoreImageProvider : ImageProvider() { // missing some attributes such as width, height, orientation. // Also, the reported size of raw images is inconsistent across devices // and Android versions (sometimes the raw size, sometimes the decoded size). - val entry = SourceEntry(entryMap).fillPreCatalogMetadata(context) + val entry = SourceEntry(entryMap).fillPreCatalogMetadata(context, safe) entryMap = entry.toMap() } diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt index ffcc2400f..8c8e29ddf 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/UnknownContentProvider.kt @@ -70,7 +70,7 @@ open class UnknownContentProvider : ImageProvider() { return } - val entry = SourceEntry(fields).fillPreCatalogMetadata(context) + val entry = SourceEntry(fields).fillPreCatalogMetadata(context, safe = false) if (allowUnsized || entry.isSized || entry.isSvg || entry.isVideo) { callback.onSuccess(entry.toMap()) } else { diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt index 8920a5d40..c240a5df2 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt @@ -1,7 +1,7 @@ package deckers.thibault.aves.utils import android.webkit.MimeTypeMap -import androidx.exifinterface.media.ExifInterface +import androidx.exifinterface.media.ExifInterfaceFork as ExifInterface import deckers.thibault.aves.decoder.MultiPageImage object MimeTypes { diff --git a/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java b/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceFork.java similarity index 99% rename from android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java rename to android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceFork.java index cb1817043..9beaaf01d 100644 --- a/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterface.java +++ b/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceFork.java @@ -16,12 +16,12 @@ package androidx.exifinterface.media; -import static androidx.exifinterface.media.ExifInterfaceUtils.closeFileDescriptor; -import static androidx.exifinterface.media.ExifInterfaceUtils.closeQuietly; -import static androidx.exifinterface.media.ExifInterfaceUtils.convertToLongArray; -import static androidx.exifinterface.media.ExifInterfaceUtils.copy; -import static androidx.exifinterface.media.ExifInterfaceUtils.parseSubSeconds; -import static androidx.exifinterface.media.ExifInterfaceUtils.startsWith; +import static androidx.exifinterface.media.ExifInterfaceUtilsFork.closeFileDescriptor; +import static androidx.exifinterface.media.ExifInterfaceUtilsFork.closeQuietly; +import static androidx.exifinterface.media.ExifInterfaceUtilsFork.convertToLongArray; +import static androidx.exifinterface.media.ExifInterfaceUtilsFork.copy; +import static androidx.exifinterface.media.ExifInterfaceUtilsFork.parseSubSeconds; +import static androidx.exifinterface.media.ExifInterfaceUtilsFork.startsWith; import static java.nio.ByteOrder.BIG_ENDIAN; import static java.nio.ByteOrder.LITTLE_ENDIAN; @@ -41,8 +41,8 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; -import androidx.exifinterface.media.ExifInterfaceUtils.Api21Impl; -import androidx.exifinterface.media.ExifInterfaceUtils.Api23Impl; +import androidx.exifinterface.media.ExifInterfaceUtilsFork.Api21Impl; +import androidx.exifinterface.media.ExifInterfaceUtilsFork.Api23Impl; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -84,6 +84,7 @@ import java.util.zip.CRC32; /* * Forked from 'androidx.exifinterface:exifinterface:1.3.7' on 2024/02/21 + * Named differently to let ExifInterface be loaded as subdependency. */ /** @@ -97,7 +98,7 @@ import java.util.zip.CRC32; * it. This class will search both locations for XMP data, but if XMP data exist both inside and * outside Exif, will favor the XMP data inside Exif over the one outside. */ -public class ExifInterface { +public class ExifInterfaceFork { // TLAD threshold for safer Exif attribute parsing private static final int ATTRIBUTE_SIZE_DANGER_THRESHOLD = 3 * (1 << 20); // MB @@ -3949,7 +3950,7 @@ public class ExifInterface { * @throws IOException if an I/O error occurs while retrieving file descriptor via * {@link FileInputStream#getFD()}. */ - public ExifInterface(@NonNull File file) throws IOException { + public ExifInterfaceFork(@NonNull File file) throws IOException { if (file == null) { throw new NullPointerException("file cannot be null"); } @@ -3964,7 +3965,7 @@ public class ExifInterface { * @throws IOException if an I/O error occurs while retrieving file descriptor via * {@link FileInputStream#getFD()}. */ - public ExifInterface(@NonNull String filename) throws IOException { + public ExifInterfaceFork(@NonNull String filename) throws IOException { if (filename == null) { throw new NullPointerException("filename cannot be null"); } @@ -3980,7 +3981,7 @@ public class ExifInterface { * @throws NullPointerException if file descriptor is null * @throws IOException if an error occurs while duplicating the file descriptor. */ - public ExifInterface(@NonNull FileDescriptor fileDescriptor) throws IOException { + public ExifInterfaceFork(@NonNull FileDescriptor fileDescriptor) throws IOException { if (fileDescriptor == null) { throw new NullPointerException("fileDescriptor cannot be null"); } @@ -4023,7 +4024,7 @@ public class ExifInterface { * @param inputStream the input stream that contains the image data * @throws NullPointerException if the input stream is null */ - public ExifInterface(@NonNull InputStream inputStream) throws IOException { + public ExifInterfaceFork(@NonNull InputStream inputStream) throws IOException { this(inputStream, STREAM_TYPE_FULL_IMAGE_DATA); } @@ -4039,7 +4040,7 @@ public class ExifInterface { * @throws IOException if an I/O error occurs while retrieving file descriptor via * {@link FileInputStream#getFD()}. */ - public ExifInterface(@NonNull InputStream inputStream, @ExifStreamType int streamType) + public ExifInterfaceFork(@NonNull InputStream inputStream, @ExifStreamType int streamType) throws IOException { if (inputStream == null) { throw new NullPointerException("inputStream cannot be null"); @@ -5071,7 +5072,7 @@ public class ExifInterface { if (location == null) { return; } - setAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD, location.getProvider()); + setAttribute(ExifInterfaceFork.TAG_GPS_PROCESSING_METHOD, location.getProvider()); setLatLong(location.getLatitude(), location.getLongitude()); setAltitude(location.getAltitude()); // Location objects store speeds in m/sec. Translates it to km/hr here. @@ -5080,8 +5081,8 @@ public class ExifInterface { * TimeUnit.HOURS.toSeconds(1) / 1000).toString()); String[] dateTime = sFormatterPrimary.format( new Date(location.getTime())).split("\\s+", -1); - setAttribute(ExifInterface.TAG_GPS_DATESTAMP, dateTime[0]); - setAttribute(ExifInterface.TAG_GPS_TIMESTAMP, dateTime[1]); + setAttribute(ExifInterfaceFork.TAG_GPS_DATESTAMP, dateTime[0]); + setAttribute(ExifInterfaceFork.TAG_GPS_TIMESTAMP, dateTime[1]); } /** @@ -5158,11 +5159,11 @@ public class ExifInterface { } /** - * Returns parsed {@link ExifInterface#TAG_DATETIME} value as number of milliseconds since + * Returns parsed {@link ExifInterfaceFork#TAG_DATETIME} value as number of milliseconds since * Jan. 1, 1970, midnight local time. * *

Note: The return value includes the first three digits (or less depending on the length - * of the string) of {@link ExifInterface#TAG_SUBSEC_TIME}. + * of the string) of {@link ExifInterfaceFork#TAG_SUBSEC_TIME}. * * @return null if date time information is unavailable or invalid. */ @@ -5175,11 +5176,11 @@ public class ExifInterface { } /** - * Returns parsed {@link ExifInterface#TAG_DATETIME_DIGITIZED} value as number of + * Returns parsed {@link ExifInterfaceFork#TAG_DATETIME_DIGITIZED} value as number of * milliseconds since Jan. 1, 1970, midnight local time. * *

Note: The return value includes the first three digits (or less depending on the length - * of the string) of {@link ExifInterface#TAG_SUBSEC_TIME_DIGITIZED}. + * of the string) of {@link ExifInterfaceFork#TAG_SUBSEC_TIME_DIGITIZED}. * * @return null if digitized date time information is unavailable or invalid. */ @@ -5192,11 +5193,11 @@ public class ExifInterface { } /** - * Returns parsed {@link ExifInterface#TAG_DATETIME_ORIGINAL} value as number of + * Returns parsed {@link ExifInterfaceFork#TAG_DATETIME_ORIGINAL} value as number of * milliseconds since Jan. 1, 1970, midnight local time. * *

Note: The return value includes the first three digits (or less depending on the length - * of the string) of {@link ExifInterface#TAG_SUBSEC_TIME_ORIGINAL}. + * of the string) of {@link ExifInterfaceFork#TAG_SUBSEC_TIME_ORIGINAL}. * * @return null if original date time information is unavailable or invalid. */ @@ -5910,18 +5911,18 @@ public class ExifInterface { } if (rotation != null) { - int orientation = ExifInterface.ORIENTATION_NORMAL; + int orientation = ExifInterfaceFork.ORIENTATION_NORMAL; // all rotation angles in CW switch (Integer.parseInt(rotation)) { case 90: - orientation = ExifInterface.ORIENTATION_ROTATE_90; + orientation = ExifInterfaceFork.ORIENTATION_ROTATE_90; break; case 180: - orientation = ExifInterface.ORIENTATION_ROTATE_180; + orientation = ExifInterfaceFork.ORIENTATION_ROTATE_180; break; case 270: - orientation = ExifInterface.ORIENTATION_ROTATE_270; + orientation = ExifInterfaceFork.ORIENTATION_ROTATE_270; break; } @@ -6175,7 +6176,11 @@ public class ExifInterface { // IEND marks the end of the image. break; } else if (Arrays.equals(type, PNG_CHUNK_TYPE_EXIF)) { - // TODO: Need to handle potential OutOfMemoryError + // TLAD start + if (length > ATTRIBUTE_SIZE_DANGER_THRESHOLD) { + throw new IOException("dangerous exif chunk size=" + length); + } + // TLAD end byte[] data = new byte[length]; in.readFully(data); @@ -6976,9 +6981,11 @@ public class ExifInterface { } final int bytesOffset = dataInputStream.position() + mOffsetToExifData; - if (byteCount > 0 && byteCount < ATTRIBUTE_SIZE_DANGER_THRESHOLD) { + // TLAD start + if (byteCount > ATTRIBUTE_SIZE_DANGER_THRESHOLD) { throw new IOException("dangerous attribute size=" + byteCount); } + // TLAD end final byte[] bytes = new byte[(int) byteCount]; dataInputStream.readFully(bytes); ExifAttribute attribute = new ExifAttribute(dataFormat, numberOfComponents, diff --git a/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java b/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtilsFork.java similarity index 98% rename from android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java rename to android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtilsFork.java index a7033b4ae..df7ed9320 100644 --- a/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtils.java +++ b/android/exifinterface/src/main/java/androidx/exifinterface/media/ExifInterfaceUtilsFork.java @@ -32,10 +32,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -class ExifInterfaceUtils { +class ExifInterfaceUtilsFork { private static final String TAG = "ExifInterfaceUtils"; - private ExifInterfaceUtils() { + private ExifInterfaceUtilsFork() { // Prevent instantiation } /** diff --git a/android/settings.gradle b/android/settings.gradle index 5ad529b31..3480028f5 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -10,7 +10,7 @@ pluginManagement { settings.ext.kotlin_version = '1.9.24' settings.ext.ksp_version = "$kotlin_version-1.0.20" - settings.ext.agp_version = '8.5.0' + settings.ext.agp_version = '8.5.1' includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index a198cc66e..6f21d047d 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -93,6 +93,8 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place _rawEntries.forEach((v) => v.dispose()); } + set safeMode(bool enabled); + final EventBus _eventBus = EventBus(); @override diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index d616dbe98..6cb66da7e 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -23,6 +23,10 @@ class MediaStoreSource extends CollectionSource { final Set _changedUris = {}; int? _lastGeneration; SourceInitializationState _initState = SourceInitializationState.none; + bool _safeMode = false; + + @override + set safeMode(bool enabled) => _safeMode = enabled; @override SourceInitializationState get initState => _initState; @@ -46,7 +50,7 @@ class MediaStoreSource extends CollectionSource { analysisController: analysisController, directory: directory, loadTopEntriesFirst: loadTopEntriesFirst, - canAnalyze: canAnalyze, + canAnalyze: canAnalyze && _safeMode, )); } @@ -175,7 +179,7 @@ class MediaStoreSource extends CollectionSource { pendingNewEntries.clear(); } - mediaStoreService.getEntries(knownDateByContentId, directory: directory).listen( + mediaStoreService.getEntries(_safeMode, knownDateByContentId, directory: directory).listen( (entry) { // when discovering modified entry with known content ID, // reuse known entry ID to overwrite it while preserving favourites, etc. diff --git a/lib/services/media/media_store_service.dart b/lib/services/media/media_store_service.dart index 72a7296f7..f60414ee7 100644 --- a/lib/services/media/media_store_service.dart +++ b/lib/services/media/media_store_service.dart @@ -15,7 +15,7 @@ abstract class MediaStoreService { Future getGeneration(); // knownEntries: map of contentId -> dateModifiedSecs - Stream getEntries(Map knownEntries, {String? directory}); + Stream getEntries(bool safe, Map knownEntries, {String? directory}); // returns media URI Future scanFile(String path, String mimeType); @@ -75,12 +75,13 @@ class PlatformMediaStoreService implements MediaStoreService { } @override - Stream getEntries(Map knownEntries, {String? directory}) { + Stream getEntries(bool safe, Map knownEntries, {String? directory}) { try { return _stream .receiveBroadcastStream({ 'knownEntries': knownEntries, 'directory': directory, + 'safe': safe, }) .where((event) => event is Map) .map((event) => AvesEntry.fromMap(event as Map)); diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index 225572e23..5ec822745 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -203,10 +203,10 @@ class _HomePageState extends State { unawaited(GlobalSearch.registerCallback()); unawaited(AnalysisService.registerCallback()); final source = context.read(); + source.safeMode = safeMode; if (source.initState != SourceInitializationState.full) { await source.init( loadTopEntriesFirst: settings.homePage == HomePageSetting.collection && settings.homeCustomCollection.isEmpty, - canAnalyze: !safeMode, ); } case AppMode.screenSaver: diff --git a/plugins/aves_screen_state/android/build.gradle b/plugins/aves_screen_state/android/build.gradle index 2a7c98004..79767327b 100644 --- a/plugins/aves_screen_state/android/build.gradle +++ b/plugins/aves_screen_state/android/build.gradle @@ -4,7 +4,7 @@ version '1.0-SNAPSHOT' buildscript { ext { kotlin_version = '1.9.24' - agp_version = '8.5.0' + agp_version = '8.5.1' } repositories { diff --git a/test/fake/media_store_service.dart b/test/fake/media_store_service.dart index d3037b39b..81e8e08a9 100644 --- a/test/fake/media_store_service.dart +++ b/test/fake/media_store_service.dart @@ -33,7 +33,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService { } @override - Stream getEntries(Map knownEntries, {String? directory}) => Stream.fromIterable(entries); + Stream getEntries(bool safe, Map knownEntries, {String? directory}) => Stream.fromIterable(entries); static var _lastId = 1;