From ac11e23fd2b8338b037173bae3a929e4fec460c3 Mon Sep 17 00:00:00 2001 From: Savvas Dalkitsis Date: Tue, 29 Aug 2023 14:31:39 +0100 Subject: [PATCH] Fixing local media time offset and bug where media would show duplicated in the feed when uploaded in some scenarios. Fixes #447. Fixes #446 --- .../uhuruphotos/feature/db/domain/api/18.sqm | 1 + .../implementation/usecase/FeedUseCase.kt | 76 +++++++++++-------- .../repository/LocalMediaRepository.kt | 2 +- .../foundation/exif/api/model/ExifMetadata.kt | 1 + .../implementation/usecase/ExifUseCase.kt | 3 + gradle.properties | 2 +- 6 files changed, 53 insertions(+), 32 deletions(-) create mode 100644 feature/db/domain/api/src/main/sqldelight/com/savvasdalkitsis/uhuruphotos/feature/db/domain/api/18.sqm diff --git a/feature/db/domain/api/src/main/sqldelight/com/savvasdalkitsis/uhuruphotos/feature/db/domain/api/18.sqm b/feature/db/domain/api/src/main/sqldelight/com/savvasdalkitsis/uhuruphotos/feature/db/domain/api/18.sqm new file mode 100644 index 000000000..f13e78888 --- /dev/null +++ b/feature/db/domain/api/src/main/sqldelight/com/savvasdalkitsis/uhuruphotos/feature/db/domain/api/18.sqm @@ -0,0 +1 @@ +DELETE FROM localMediaItemDetails; \ No newline at end of file diff --git a/feature/feed/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/feed/domain/implementation/usecase/FeedUseCase.kt b/feature/feed/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/feed/domain/implementation/usecase/FeedUseCase.kt index f995908a8..2caf5abd4 100644 --- a/feature/feed/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/feed/domain/implementation/usecase/FeedUseCase.kt +++ b/feature/feed/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/feed/domain/implementation/usecase/FeedUseCase.kt @@ -96,8 +96,14 @@ internal class FeedUseCase @Inject constructor( mediaBeingUploaded: Set, ): List { val allLocalDays = allLocalMedia.groupBy { it.displayDayDate } - val combined = mergeLocalMediaIntoExistingRemoteDays(allRemoteDays, allLocalDays) + - remainingLocalDays(allRemoteDays, allLocalDays) + val mergedToRemote = + allRemoteDays.mergeLocalMediaIntoExistingRemoteDays(allLocalMedia) + val remainingLocalDays = allLocalDays.mediaNotIn(mergedToRemote) + val remoteAndLocalExistingDays = mergedToRemote + .addRemainingLocalMediaToExistingDays(remainingLocalDays) + val combined = remoteAndLocalExistingDays + allLocalDays + .mediaNotIn(remoteAndLocalExistingDays) + .toMediaCollections() return combined .sortedByDescending { it.unformattedDate } .markDownloading(mediaBeingDownloaded) @@ -105,6 +111,18 @@ internal class FeedUseCase @Inject constructor( .toList() } + private fun Map>.mediaNotIn( + mergedToRemote: Sequence + ): Map> { + val mergedToRemoteHashes = mergedToRemote + .flatMap { it.mediaItems } + .map { it.mediaHash } + return mapValues { (_, media) -> + media.filter { it.mediaHash !in mergedToRemoteHashes } + } + .filter { (_, media) -> media.isNotEmpty() } + } + private fun Sequence.markDownloading( inProgress: Set, ): Sequence = map { collection -> @@ -186,29 +204,32 @@ internal class FeedUseCase @Inject constructor( toMediaCollection() } - private fun mergeLocalMediaIntoExistingRemoteDays( - remoteDays: Sequence, - localDays: Map> - ) = remoteDays.map { remoteDay -> - val localMediaOnDay = localDays[remoteDay.displayTitle] ?: emptyList() - val existingRemoteMediaHashes = remoteDay.mediaItems.map { it.mediaHash } - val localMediaNotPresentRemotely = localMediaOnDay.filter { - it.mediaHash !in existingRemoteMediaHashes - } - val combinedMediaItems = remoteDay.mediaItems.map { remoteMediaItem -> - mergeLocalDuplicates(localDays.flatMap { it.value }, remoteMediaItem) - } + localMediaNotPresentRemotely + private fun Sequence.mergeLocalMediaIntoExistingRemoteDays( + localMedia: Sequence + ) = map { remoteDay -> + remoteDay.copy( + mediaItems = remoteDay.mediaItems.map { remoteMediaItem -> + mergeLocalDuplicates(localMedia, remoteMediaItem) + } + ) + } + + private fun Sequence.addRemainingLocalMediaToExistingDays( + remainingLocalDays: Map> + ) = map { remoteDay -> + val localMediaOnDay = remainingLocalDays[remoteDay.displayTitle] ?: emptyList() + val combinedMediaItems = remoteDay.mediaItems + localMediaOnDay remoteDay.copy( mediaItems = combinedMediaItems.sortedByDescending { it.sortableDate } ) } private fun mergeLocalDuplicates( - allLocalMedia: List, + localMedia: Sequence, remoteMediaItem: MediaItem ): MediaItem { val localCopies = - allLocalMedia.filter { it.mediaHash == remoteMediaItem.mediaHash }.toSet() + localMedia.filter { it.mediaHash == remoteMediaItem.mediaHash }.toSet() return when { localCopies.isNotEmpty() -> MediaItemGroup( @@ -220,20 +241,15 @@ internal class FeedUseCase @Inject constructor( } } - private fun remainingLocalDays( - allRemoteDays: Sequence, - allLocalDays: Map> - ) = allLocalDays - .filter { (day, _) -> day !in allRemoteDays.map { it.displayTitle } } - .map { (day, items) -> - MediaCollection( - id = "local_media_collection_$day", - mediaItems = items, - displayTitle = day ?: "-", - location = null, - unformattedDate = items.firstOrNull()?.sortableDate - ) - } + private fun Map>.toMediaCollections() = map { (day, items) -> + MediaCollection( + id = "local_media_collection_$day", + mediaItems = items, + displayTitle = day ?: "-", + location = null, + unformattedDate = items.firstOrNull()?.sortableDate + ) + } private fun observeLocalMediaFeed(feedFetchType: FeedFetchType) = mediaUseCase.observeLocalMedia() diff --git a/feature/media/local/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/media/local/domain/implementation/repository/LocalMediaRepository.kt b/feature/media/local/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/media/local/domain/implementation/repository/LocalMediaRepository.kt index 7724ad6fa..000bd38aa 100644 --- a/feature/media/local/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/media/local/domain/implementation/repository/LocalMediaRepository.kt +++ b/feature/media/local/domain/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/feature/media/local/domain/implementation/repository/LocalMediaRepository.kt @@ -169,7 +169,7 @@ class LocalMediaRepository @Inject constructor( LocalMediaItemDetails( id = item.id, displayName = item.displayName, - dateTaken = item.dateTaken.toDateString(), + dateTaken = (exif.dateTime ?: item.dateTaken).toDateString(), bucketId = item.bucketId, bucketName = item.bucketName, width = item.width ?: 0, diff --git a/foundation/exif/api/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/api/model/ExifMetadata.kt b/foundation/exif/api/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/api/model/ExifMetadata.kt index 86ff7f834..52056cde1 100644 --- a/foundation/exif/api/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/api/model/ExifMetadata.kt +++ b/foundation/exif/api/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/api/model/ExifMetadata.kt @@ -20,6 +20,7 @@ data class ExifMetadata( val shutterSpeed: Double?, val isoSpeed: Int?, val camera: String?, + val dateTime: Long?, val focalLength: Double?, val focalLength35Equivalent: Int?, val width: Int?, diff --git a/foundation/exif/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/implementation/usecase/ExifUseCase.kt b/foundation/exif/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/implementation/usecase/ExifUseCase.kt index 55de2e77a..e6f296f64 100644 --- a/foundation/exif/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/implementation/usecase/ExifUseCase.kt +++ b/foundation/exif/implementation/src/main/kotlin/com/savvasdalkitsis/uhuruphotos/foundation/exif/implementation/usecase/ExifUseCase.kt @@ -15,6 +15,7 @@ limitations under the License. */ package com.savvasdalkitsis.uhuruphotos.foundation.exif.implementation.usecase +import android.annotation.SuppressLint import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface.TAG_APERTURE_VALUE import androidx.exifinterface.media.ExifInterface.TAG_DIGITAL_ZOOM_RATIO @@ -66,6 +67,7 @@ class ExifUseCase @Inject constructor( } + @SuppressLint("RestrictedApi") private fun ExifInterface.metadata() = ExifMetadata( fStop = double(TAG_F_NUMBER) ?: ratio(TAG_APERTURE_VALUE)?.let { exp(it * ln(2.0) * 0.5) }, @@ -74,6 +76,7 @@ class ExifUseCase @Inject constructor( }, isoSpeed = int(TAG_PHOTOGRAPHIC_SENSITIVITY), camera = string(TAG_MODEL), + dateTime = dateTime, focalLength = ratio(TAG_FOCAL_LENGTH), focalLength35Equivalent = int(TAG_FOCAL_LENGTH_IN_35MM_FILM), width = int(TAG_PIXEL_X_DIMENSION) ?: int(TAG_IMAGE_WIDTH), diff --git a/gradle.properties b/gradle.properties index 0d77c920d..132427380 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,5 +21,5 @@ kotlin.code.style=official # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -org.gradle.daemon=true +org.gradle.daemon=false org.gradle.configuration-cache=true