diff --git a/app/src/main/kotlin/org/fossify/gallery/activities/VideoPlayerActivity.kt b/app/src/main/kotlin/org/fossify/gallery/activities/VideoPlayerActivity.kt index f0750cbcd..40332906a 100644 --- a/app/src/main/kotlin/org/fossify/gallery/activities/VideoPlayerActivity.kt +++ b/app/src/main/kotlin/org/fossify/gallery/activities/VideoPlayerActivity.kt @@ -309,6 +309,18 @@ open class VideoPlayerActivity : SimpleActivity(), SeekBar.OnSeekBarChangeListen mVideoSize.y = videoSize.height setVideoSize() } + + override fun onPlayerErrorChanged(error: PlaybackException?) { + binding.errorMessageHolder.errorMessage.apply { + if (error != null) { + text = error.localizedMessage ?: getString(R.string.failed_to_load_media) + setTextColor(if (context.config.blackBackground) Color.WHITE else context.getProperTextColor()) + fadeIn() + } else { + beGone() + } + } + } }) } diff --git a/app/src/main/kotlin/org/fossify/gallery/adapters/DirectoryAdapter.kt b/app/src/main/kotlin/org/fossify/gallery/adapters/DirectoryAdapter.kt index 3810c0e28..b21841f9a 100644 --- a/app/src/main/kotlin/org/fossify/gallery/adapters/DirectoryAdapter.kt +++ b/app/src/main/kotlin/org/fossify/gallery/adapters/DirectoryAdapter.kt @@ -11,8 +11,10 @@ import android.view.Menu import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.RelativeLayout import androidx.annotation.RequiresApi +import androidx.appcompat.content.res.AppCompatResources import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout @@ -820,14 +822,18 @@ class DirectoryAdapter( } activity.loadImage( - thumbnailType, - directory.tmb, - dirThumbnail, - scrollHorizontally, - animateGifs, - cropThumbnails, - roundedCorners, - directory.getKey() + type = thumbnailType, + path = directory.tmb, + target = dirThumbnail, + horizontalScroll = scrollHorizontally, + animateGifs = animateGifs, + cropThumbnails = cropThumbnails, + roundCorners = roundedCorners, + signature = directory.getKey(), + onError = { + dirThumbnail.scaleType = ImageView.ScaleType.CENTER + dirThumbnail.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_vector_warning_colored)) + } ) } diff --git a/app/src/main/kotlin/org/fossify/gallery/adapters/MediaAdapter.kt b/app/src/main/kotlin/org/fossify/gallery/adapters/MediaAdapter.kt index 5f681344f..06e132793 100644 --- a/app/src/main/kotlin/org/fossify/gallery/adapters/MediaAdapter.kt +++ b/app/src/main/kotlin/org/fossify/gallery/adapters/MediaAdapter.kt @@ -9,7 +9,9 @@ import android.os.Looper import android.view.Menu import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.Toast +import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.allViews import com.bumptech.glide.Glide import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller @@ -681,7 +683,19 @@ class MediaAdapter( if (loadImageInstantly) { activity.loadImage( - medium.type, path, mediumThumbnail, scrollHorizontally, animateGifs, cropThumbnails, roundedCorners, medium.getKey(), rotatedImagePaths + type = medium.type, + path = path, + target = mediumThumbnail, + horizontalScroll = scrollHorizontally, + animateGifs = animateGifs, + cropThumbnails = cropThumbnails, + roundCorners = roundedCorners, + signature = medium.getKey(), + skipMemoryCacheAtPaths = rotatedImagePaths, + onError = { + mediumThumbnail.scaleType = ImageView.ScaleType.CENTER + mediumThumbnail.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_vector_warning_colored)) + } ) } else { mediumThumbnail.setImageDrawable(null) @@ -690,8 +704,19 @@ class MediaAdapter( val isVisible = visibleItemPaths.contains(medium.path) if (isVisible) { activity.loadImage( - medium.type, path, mediumThumbnail, scrollHorizontally, animateGifs, cropThumbnails, roundedCorners, - medium.getKey(), rotatedImagePaths + type = medium.type, + path = path, + target = mediumThumbnail, + horizontalScroll = scrollHorizontally, + animateGifs = animateGifs, + cropThumbnails = cropThumbnails, + roundCorners = roundedCorners, + signature = medium.getKey(), + skipMemoryCacheAtPaths = rotatedImagePaths, + onError = { + mediumThumbnail.scaleType = ImageView.ScaleType.CENTER + mediumThumbnail.setImageDrawable(AppCompatResources.getDrawable(activity, R.drawable.ic_vector_warning_colored)) + } ) } }, IMAGE_LOAD_DELAY) diff --git a/app/src/main/kotlin/org/fossify/gallery/extensions/Context.kt b/app/src/main/kotlin/org/fossify/gallery/extensions/Context.kt index edac79d3e..335f28912 100644 --- a/app/src/main/kotlin/org/fossify/gallery/extensions/Context.kt +++ b/app/src/main/kotlin/org/fossify/gallery/extensions/Context.kt @@ -482,13 +482,29 @@ fun Context.loadImage( roundCorners: Int, signature: ObjectKey, skipMemoryCacheAtPaths: ArrayList? = null, + onError: (() -> Unit)? = null ) { target.isHorizontalScrolling = horizontalScroll if (type == TYPE_SVGS) { - loadSVG(path, target, cropThumbnails, roundCorners, signature) + loadSVG( + path = path, + target = target, + cropThumbnails = cropThumbnails, + roundCorners = roundCorners, + signature = signature + ) } else { - val tryLoadingWithPicasso = type == TYPE_IMAGES && path.isPng() - loadImageBase(path, target, cropThumbnails, roundCorners, signature, skipMemoryCacheAtPaths, animateGifs, tryLoadingWithPicasso) + loadImageBase( + path = path, + target = target, + cropThumbnails = cropThumbnails, + roundCorners = roundCorners, + signature = signature, + skipMemoryCacheAtPaths = skipMemoryCacheAtPaths, + animate = animateGifs, + tryLoadingWithPicasso = type == TYPE_IMAGES && path.isPng(), + onError = onError + ) } } @@ -524,6 +540,7 @@ fun Context.loadImageBase( animate: Boolean = false, tryLoadingWithPicasso: Boolean = false, crossFadeDuration: Int = THUMBNAIL_FADE_DURATION_MS, + onError: (() -> Unit)? = null ) { val options = RequestOptions() .signature(signature) @@ -571,24 +588,25 @@ fun Context.loadImageBase( .set(WebpDownsampler.USE_SYSTEM_DECODER, false) // CVE-2023-4863 .transition(DrawableTransitionOptions.withCrossFade(crossFadeDuration)) - if (tryLoadingWithPicasso) { - builder = builder.listener(object : RequestListener { - override fun onLoadFailed(e: GlideException?, model: Any?, targetBitmap: Target, isFirstResource: Boolean): Boolean { + builder = builder.listener(object : RequestListener { + override fun onLoadFailed(e: GlideException?, model: Any?, targetBitmap: Target, isFirstResource: Boolean): Boolean { + if (tryLoadingWithPicasso) { tryLoadingWithPicasso(path, target, cropThumbnails, roundCorners, signature) - return true + } else { + onError?.invoke() } - override fun onResourceReady( - resource: Drawable, - model: Any, - targetBitmap: Target, - dataSource: DataSource, - isFirstResource: Boolean, - ): Boolean { - return false - } - }) - } + return true + } + + override fun onResourceReady( + resource: Drawable, + model: Any, + targetBitmap: Target, + dataSource: DataSource, + isFirstResource: Boolean, + ) = false + }) builder.into(target) } diff --git a/app/src/main/kotlin/org/fossify/gallery/fragments/PhotoFragment.kt b/app/src/main/kotlin/org/fossify/gallery/fragments/PhotoFragment.kt index 8b483099f..c97d56290 100644 --- a/app/src/main/kotlin/org/fossify/gallery/fragments/PhotoFragment.kt +++ b/app/src/main/kotlin/org/fossify/gallery/fragments/PhotoFragment.kt @@ -518,6 +518,11 @@ class PhotoFragment : ViewPagerFragment() { loadImage() // TODO: Implement panorama using a FOSS library // checkIfPanorama() + } else { + binding.errorMessageHolder.errorMessage.apply { + setTextColor(if (context.config.blackBackground) Color.WHITE else context.getProperTextColor()) + fadeIn() + } } } }) diff --git a/app/src/main/kotlin/org/fossify/gallery/fragments/VideoFragment.kt b/app/src/main/kotlin/org/fossify/gallery/fragments/VideoFragment.kt index ab1c2452a..d224f9f80 100644 --- a/app/src/main/kotlin/org/fossify/gallery/fragments/VideoFragment.kt +++ b/app/src/main/kotlin/org/fossify/gallery/fragments/VideoFragment.kt @@ -2,6 +2,7 @@ package org.fossify.gallery.fragments import android.annotation.SuppressLint import android.content.res.Configuration +import android.graphics.Color import android.graphics.Point import android.graphics.SurfaceTexture import android.net.Uri @@ -449,6 +450,20 @@ class VideoFragment : ViewPagerFragment(), TextureView.SurfaceTextureListener, S mVideoSize.y = (videoSize.height / videoSize.pixelWidthHeightRatio).toInt() setVideoSize() } + + override fun onPlayerErrorChanged(error: PlaybackException?) { + binding.errorMessageHolder.errorMessage.apply { + if (error != null) { + binding.videoPlayOutline.beGone() + text = error.localizedMessage ?: getString(R.string.failed_to_load_media) + setTextColor(if (context.config.blackBackground) Color.WHITE else context.getProperTextColor()) + fadeIn() + } else { + beGone() + binding.videoPlayOutline.beVisible() + } + } + } }) } diff --git a/app/src/main/kotlin/org/fossify/gallery/helpers/MyGlideImageDecoder.kt b/app/src/main/kotlin/org/fossify/gallery/helpers/MyGlideImageDecoder.kt index a5da7b2c1..84e6f445c 100644 --- a/app/src/main/kotlin/org/fossify/gallery/helpers/MyGlideImageDecoder.kt +++ b/app/src/main/kotlin/org/fossify/gallery/helpers/MyGlideImageDecoder.kt @@ -23,7 +23,7 @@ class MyGlideImageDecoder(val degrees: Int, val signature: ObjectKey) : ImageDec .load(uri.toString().substringAfter("file://")) .apply(options) .transform(RotateTransformation(-degrees)) - .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) + .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL) return builder.get() } diff --git a/app/src/main/res/drawable/ic_vector_warning_colored.xml b/app/src/main/res/drawable/ic_vector_warning_colored.xml new file mode 100644 index 000000000..a83af2669 --- /dev/null +++ b/app/src/main/res/drawable/ic_vector_warning_colored.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_video_player.xml b/app/src/main/res/layout/activity_video_player.xml index b57b0841f..4734329d4 100644 --- a/app/src/main/res/layout/activity_video_player.xml +++ b/app/src/main/res/layout/activity_video_player.xml @@ -50,6 +50,10 @@ android:id="@+id/bottom_video_time_holder" layout="@layout/bottom_video_time_holder" /> + + + diff --git a/app/src/main/res/layout/pager_photo_item.xml b/app/src/main/res/layout/pager_photo_item.xml index 22f9f5da4..b4d908804 100644 --- a/app/src/main/res/layout/pager_photo_item.xml +++ b/app/src/main/res/layout/pager_photo_item.xml @@ -32,6 +32,10 @@ android:layout_height="match_parent" android:visibility="gone" /> + + + + + android:layout_height="match_parent" + tools:src="@mipmap/ic_launcher" /> + android:layout_height="@dimen/list_view_folder_thumbnail_size" + tools:src="@mipmap/ic_launcher" /> Reorder folders by dragging (Pro) Restoring to \'%s\' Fossify Gallery needs full access to display all your photos and videos. Go to Settings > Permissions > Photos and videos > Allow all. + Failed to load media. Filter media