#1084 crashfix for png with large exif via ExifInterface,
fixed ExifInterface disambiguation, improved safe mode
This commit is contained in:
parent
a38c5b72ee
commit
2d143f139f
22 changed files with 75 additions and 55 deletions
|
@ -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'
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -21,11 +21,13 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E
|
|||
|
||||
private var knownEntries: Map<Long?, Int?>? = 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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -51,8 +51,10 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
context: Context,
|
||||
knownEntries: Map<Long?, Int?>,
|
||||
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<String>? = 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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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.
|
||||
*
|
||||
* <p>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,
|
|
@ -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
|
||||
}
|
||||
/**
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,6 +23,10 @@ class MediaStoreSource extends CollectionSource {
|
|||
final Set<String> _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.
|
||||
|
|
|
@ -15,7 +15,7 @@ abstract class MediaStoreService {
|
|||
Future<int?> getGeneration();
|
||||
|
||||
// knownEntries: map of contentId -> dateModifiedSecs
|
||||
Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory});
|
||||
Stream<AvesEntry> getEntries(bool safe, Map<int?, int?> knownEntries, {String? directory});
|
||||
|
||||
// returns media URI
|
||||
Future<Uri?> scanFile(String path, String mimeType);
|
||||
|
@ -75,12 +75,13 @@ class PlatformMediaStoreService implements MediaStoreService {
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory}) {
|
||||
Stream<AvesEntry> getEntries(bool safe, Map<int?, int?> knownEntries, {String? directory}) {
|
||||
try {
|
||||
return _stream
|
||||
.receiveBroadcastStream(<String, dynamic>{
|
||||
'knownEntries': knownEntries,
|
||||
'directory': directory,
|
||||
'safe': safe,
|
||||
})
|
||||
.where((event) => event is Map)
|
||||
.map((event) => AvesEntry.fromMap(event as Map));
|
||||
|
|
|
@ -203,10 +203,10 @@ class _HomePageState extends State<HomePage> {
|
|||
unawaited(GlobalSearch.registerCallback());
|
||||
unawaited(AnalysisService.registerCallback());
|
||||
final source = context.read<CollectionSource>();
|
||||
source.safeMode = safeMode;
|
||||
if (source.initState != SourceInitializationState.full) {
|
||||
await source.init(
|
||||
loadTopEntriesFirst: settings.homePage == HomePageSetting.collection && settings.homeCustomCollection.isEmpty,
|
||||
canAnalyze: !safeMode,
|
||||
);
|
||||
}
|
||||
case AppMode.screenSaver:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -33,7 +33,7 @@ class FakeMediaStoreService extends Fake implements MediaStoreService {
|
|||
}
|
||||
|
||||
@override
|
||||
Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory}) => Stream.fromIterable(entries);
|
||||
Stream<AvesEntry> getEntries(bool safe, Map<int?, int?> knownEntries, {String? directory}) => Stream.fromIterable(entries);
|
||||
|
||||
static var _lastId = 1;
|
||||
|
||||
|
|
Loading…
Reference in a new issue