Add support for viewing photos in photo albums (#703)

## Description
Add support for viewing photos in photo albums

### Features
- Browse and view albums & photos
- View an album as a slideshow
- Show overlay with photo details and controls
- Rotate, zoom, & pan photos
- Apply basic filters to photos or albums such as contrast, saturation,
red/green/blue, etc
- Setting to include video clips during slideshows

### Controls
- D-Pad left or right: scroll between photos in album
- Hold D-Pad up or down: zoom in or out of the current photo
- D-Pad left/right/up/down, while zoomed: pan around photo
- Back, while zoomed: revert the zoom
- Otherwise: show photo overlay

### Related issues
Closes #458
Closes #634

### Screenshots
#### Overlay
![photo_overlay
Large](https://github.com/user-attachments/assets/b44ed876-d8bd-4f75-aa1e-4106c2edb2f7)

#### Applying filters
![photo_filters
Large](https://github.com/user-attachments/assets/1379978a-b27c-47ac-80af-f7bb6a2177df)
This commit is contained in:
Ray 2026-02-04 20:04:46 -05:00 committed by GitHub
parent ee440698b0
commit d9d5ab02e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 3128 additions and 32 deletions

View file

@ -903,6 +903,46 @@ sealed interface AppPreference<Pref, T> {
getter = { },
setter = { prefs, _ -> prefs },
)
val SlideshowDuration =
AppSliderPreference<AppPreferences>(
title = R.string.slideshow_duration,
defaultValue = 5_000,
min = 1_000,
max = 30.seconds.inWholeMilliseconds,
interval = 250,
getter = {
it.photoPreferences.slideshowDuration
},
setter = { prefs, value ->
prefs.updatePhotoPreferences {
slideshowDuration = value
}
},
summarizer = { value ->
if (value != null) {
val seconds = value / 1000.0
WholphinApplication.instance.resources.getString(
R.string.decimal_seconds,
seconds,
)
} else {
null
}
},
)
val SlideshowPlayVideos =
AppSwitchPreference<AppPreferences>(
title = R.string.play_videos_during_slideshow,
defaultValue = false,
getter = { it.photoPreferences.slideshowPlayVideos },
setter = { prefs, value ->
prefs.updatePhotoPreferences { slideshowPlayVideos = value }
},
summaryOn = R.string.enabled,
summaryOff = R.string.disabled,
)
}
}
@ -1015,6 +1055,8 @@ val advancedPreferences =
// AppPreference.NavDrawerSwitchOnFocus,
AppPreference.ControllerTimeout,
AppPreference.BackdropStylePref,
AppPreference.SlideshowDuration,
AppPreference.SlideshowPlayVideos,
),
),
)

View file

@ -128,6 +128,14 @@ class AppPreferencesSerializer
imageDiskCacheSizeBytes =
AppPreference.ImageDiskCacheSize.defaultValue * AppPreference.MEGA_BIT
}.build()
photoPreferences =
PhotoPreferences
.newBuilder()
.apply {
slideshowDuration = AppPreference.SlideshowDuration.defaultValue
slideshowPlayVideos = AppPreference.SlideshowPlayVideos.defaultValue
}.build()
}.build()
override suspend fun readFrom(input: InputStream): AppPreferences {
@ -186,6 +194,11 @@ inline fun AppPreferences.updateAdvancedPreferences(block: AdvancedPreferences.B
advancedPreferences = advancedPreferences.toBuilder().apply(block).build()
}
inline fun AppPreferences.updatePhotoPreferences(block: PhotoPreferences.Builder.() -> Unit): AppPreferences =
update {
photoPreferences = photoPreferences.toBuilder().apply(block).build()
}
fun SubtitlePreferences.Builder.resetSubtitles() {
fontSize = SubtitleSettings.FontSize.defaultValue.toInt()
fontColor = SubtitleSettings.FontColor.defaultValue.toArgb()