Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Added "On video tap" option to choose between in-app player or default player app ([#917])
- Update system favorites collection on Android 11+ when favoriting media ([#848])

### Fixed
- Fixed unnecessary "Video has no audio" toast when looping videos ([#876])
Expand Down Expand Up @@ -294,6 +295,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[#800]: https://github.com/FossifyOrg/Gallery/issues/800
[#830]: https://github.com/FossifyOrg/Gallery/issues/830
[#831]: https://github.com/FossifyOrg/Gallery/issues/831
[#848]: https://github.com/FossifyOrg/Gallery/issues/848
[#876]: https://github.com/FossifyOrg/Gallery/issues/876
[#917]: https://github.com/FossifyOrg/Gallery/issues/917

Expand Down
62 changes: 62 additions & 0 deletions app/src/main/kotlin/org/fossify/gallery/extensions/Activity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package org.fossify.gallery.extensions

import android.app.Activity
import android.content.ContentProviderOperation
import android.content.ContentResolver
import android.content.ContentValues
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Process
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
Expand All @@ -12,12 +15,14 @@ import android.graphics.Point
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.provider.MediaStore.Files
import android.provider.MediaStore.Images
import android.provider.Settings
import android.util.DisplayMetrics
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.exifinterface.media.ExifInterface
import com.bumptech.glide.Glide
Expand Down Expand Up @@ -1072,3 +1077,60 @@ fun Activity.proposeNewFilePath(uri: Uri): Pair<String, Boolean> {

return Pair(newPath, shouldAppendFilename)
}

fun Activity.updateFavorite(path: String, isFavorite: Boolean) {
try {
if (isFavorite) {
favoritesDB.insert(getFavoriteFromPath(path))
} else {
favoritesDB.deleteFavoritePath(path)
}
// Update media in favorites collection for Android 11+ (API level 30)
if (isRPlus()) {
val uri = getFilePublicUri(File(path), BuildConfig.APPLICATION_ID)
if (isMedia(contentResolver, uri)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not reuse helpers from the commons lib here?

Suggested change
if (isMedia(contentResolver, uri)) {
if (path.isImageFast() && !path.isSvg()) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. Using path.isImageFast() would throw exceptions for AVIF, JXL, etc. (fails on my Android 15 test device).

Given Android's scoped storage restrictions, when users grant "media only" access they only see specific MIME types - which is exactly what we should check for in the isMedia function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested many formats but only image/jpeg consistently works with the media picker. Propose reducing supportedImageTypes to just image/jpeg. What do you think?

updateFavoriteInMediaStore(uri, isFavorite)
}
}
} catch (_: Exception) {
toast(org.fossify.commons.R.string.unknown_error_occurred)
}
}

private const val FAVORITE_REQUEST_CODE = 100

@RequiresApi(Build.VERSION_CODES.R)
private fun Activity.updateFavoriteInMediaStore(uri: Uri, isFavorite: Boolean) {
if (checkUriPermission(
uri, Process.myPid(), Process.myUid(),
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
) != PackageManager.PERMISSION_GRANTED
) {
val pendingIntent = MediaStore.createFavoriteRequest(contentResolver, listOf(uri), isFavorite)
startIntentSenderForResult(pendingIntent.intentSender, FAVORITE_REQUEST_CODE, null, 0, 0, 0)
} else {
val values = ContentValues().apply {
put(MediaStore.MediaColumns.IS_FAVORITE, if (isFavorite) 1 else 0)
}
contentResolver.update(uri, values, null)
}
}

private fun isMedia(contentResolver: ContentResolver, uri: Uri): Boolean {
return try {
val type = contentResolver.getType(uri) ?: return false
val supportedImageTypes = setOf(
"image/jpeg",
"image/png",
"image/webp",
"image/gif",
"image/bmp",
"image/x-ms-bmp"
)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about other formats such as avif, JXL, etc? Are those failing too?

val isImage = type in supportedImageTypes
val isVideo = type.startsWith("video/")
isImage || isVideo
} catch (_: Exception) {
false
}
}
12 changes: 0 additions & 12 deletions app/src/main/kotlin/org/fossify/gallery/extensions/Context.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1074,18 +1074,6 @@ fun Context.getFavoriteFromPath(path: String): Favorite {
return Favorite(null, path, path.getFilenameFromPath(), path.getParentPath())
}

fun Context.updateFavorite(path: String, isFavorite: Boolean) {
try {
if (isFavorite) {
favoritesDB.insert(getFavoriteFromPath(path))
} else {
favoritesDB.deleteFavoritePath(path)
}
} catch (e: Exception) {
toast(org.fossify.commons.R.string.unknown_error_occurred)
}
}

// remove the "recycle_bin" from the file path prefix, replace it with real bin path /data/user...
fun Context.getUpdatedDeletedMedia(): ArrayList<Medium> {
val media = try {
Expand Down
Loading