Files
LockScreenWallpaper/plugins/src/desktop.rs
T
Christoph 5ffa921967
Android Release / Build signed release APK (push) Successful in 34m17s
feat(wallpaper-store): enhance previews, EXIF handling, and shuffle
These changes add image previews and robust image handling across platforms.
Android, desktop, and TS UI now share imagePreviews data and a
persistent shuffle queue, improving ordering and previews.
EXIF orientation is applied when decoding images and rendering crops.
Manifest and rendering paths were adjusted for portrait mode.

- Introduce imagePreviews in models and UI to show per-image crop data.
- Implement EXIF orientation handling and enhanced cropping.
- Improve shuffle with persistent queue and seen-tracking.
2026-08-26 17:12:18 +02:00

182 lines
6.5 KiB
Rust

use serde::de::DeserializeOwned;
use tauri::{plugin::PluginApi, AppHandle, Runtime};
use crate::models::*;
pub fn init<R: Runtime, C: DeserializeOwned>(
app: &AppHandle<R>,
_api: PluginApi<R, C>,
) -> crate::Result<Wallpaper<R>> {
Ok(Wallpaper(app.clone()))
}
/// Access to the wallpaper APIs.
pub struct Wallpaper<R: Runtime>(AppHandle<R>);
impl<R: Runtime> Wallpaper<R> {
fn demo() -> WallpaperState {
let image_previews = ["alpine", "waterfall", "coast"]
.into_iter()
.enumerate()
.map(|(index, name)| GalleryImage {
id: format!("demo-{index}"),
url: format!("/wallpapers/{name}.png"),
selected: index == 0,
crop_mode: "cover".into(),
crop_zoom: 1.0,
crop_position_x: 0.5,
crop_position_y: 0.5,
crop_rotation: 0,
})
.collect();
WallpaperState {
image_count: 3,
enabled: true,
interval_minutes: 30,
shuffle: true,
lock_screen_only: true,
allow_mobile_data: false,
prefetch_immich: true,
current_index: 0,
current_id: Some("demo-0".into()),
image_ids: vec!["demo-0".into(), "demo-1".into(), "demo-2".into()],
image_urls: vec![
"/wallpapers/alpine.png".into(),
"/wallpapers/waterfall.png".into(),
"/wallpapers/coast.png".into(),
],
image_previews,
}
}
pub fn get_state(&self) -> crate::Result<WallpaperState> {
Ok(Self::demo())
}
pub fn get_gallery(&self, payload: GalleryRequest) -> crate::Result<GalleryPage> {
let urls = Self::demo().image_urls;
let items = urls
.into_iter()
.enumerate()
.skip(payload.offset)
.take(payload.limit)
.map(|(index, url)| GalleryImage {
id: format!("demo-{index}"),
url,
selected: index == 0,
crop_mode: "cover".into(),
crop_zoom: 1.0,
crop_position_x: 0.5,
crop_position_y: 0.5,
crop_rotation: 0,
})
.collect();
Ok(GalleryPage { total: 3, items })
}
pub fn select_images(&self) -> crate::Result<WallpaperState> {
Ok(Self::demo())
}
pub fn delete_image(&self, _payload: DeleteImageRequest) -> crate::Result<WallpaperState> {
Ok(Self::demo())
}
pub fn get_image_ids(&self) -> crate::Result<Vec<String>> {
Ok((0..Self::demo().image_count).map(|index| format!("demo-{index}")).collect())
}
pub fn delete_images(&self, _payload: DeleteImagesRequest) -> crate::Result<WallpaperState> {
Ok(Self::demo())
}
pub fn set_image_crop(&self, payload: ImageCropRequest) -> crate::Result<GalleryImage> {
let url = Self::demo()
.image_urls
.into_iter()
.nth(payload.id.trim_start_matches("demo-").parse().unwrap_or(0))
.unwrap_or_default();
Ok(GalleryImage {
id: payload.id,
url,
selected: false,
crop_mode: payload.mode,
crop_zoom: payload.zoom,
crop_position_x: payload.position_x,
crop_position_y: payload.position_y,
crop_rotation: payload.rotation,
})
}
pub fn set_setting(&self, payload: SettingRequest) -> crate::Result<WallpaperState> {
let mut state = Self::demo();
match payload.name.as_str() {
"enabled" => state.enabled = payload.value,
"shuffle" => state.shuffle = payload.value,
"lockScreenOnly" => state.lock_screen_only = payload.value,
"allowMobileData" => state.allow_mobile_data = payload.value,
"prefetchImmich" => state.prefetch_immich = payload.value,
_ => {}
};
Ok(state)
}
pub fn set_interval(&self, payload: IntervalRequest) -> crate::Result<WallpaperState> {
let mut state = Self::demo();
state.interval_minutes = payload.minutes;
state.enabled = payload.minutes != 0;
Ok(state)
}
pub fn next_wallpaper(&self) -> crate::Result<WallpaperState> {
let mut state = Self::demo();
state.current_index = 1;
state.current_id = Some("demo-1".into());
Ok(state)
}
pub fn apply_wallpaper(&self, payload: ApplyWallpaperRequest) -> crate::Result<WallpaperState> {
let mut state = Self::demo();
if let Some(index) = state.image_ids.iter().position(|id| id == &payload.id) {
state.current_index = index;
state.current_id = Some(payload.id);
}
Ok(state)
}
pub fn get_immich_connection(&self) -> crate::Result<ImmichConnection> {
Ok(ImmichConnection::default())
}
pub fn connect_immich(&self, payload: ImmichConnectRequest) -> crate::Result<ImmichConnection> {
Ok(ImmichConnection {
configured: true,
server_url: payload.server_url,
user_name: "Demo User".into(),
})
}
pub fn disconnect_immich(&self) -> crate::Result<ImmichConnection> {
Ok(ImmichConnection::default())
}
pub fn get_immich_albums(&self) -> crate::Result<ImmichAlbumsResponse> {
Ok(ImmichAlbumsResponse {
albums: vec![ImmichAlbum {
id: "demo-album".into(),
name: "Nature".into(),
asset_count: 3,
thumbnail_url: "/wallpapers/alpine.png".into(),
}],
})
}
pub fn get_immich_assets(&self, payload: ImmichAssetsRequest) -> crate::Result<ImmichAssetsPage> {
let urls = Self::demo().image_urls;
Ok(ImmichAssetsPage {
items: urls
.into_iter()
.enumerate()
.map(|(index, thumbnail_url)| ImmichAsset {
id: format!("immich-demo-{index}"),
file_name: format!("wallpaper-{}.jpg", index + 1),
thumbnail_url,
taken_at: "2026-08-21T12:00:00Z".into(),
})
.collect(),
page: payload.page,
has_more: false,
})
}
pub fn import_immich_assets(&self, _payload: ImmichImportRequest) -> crate::Result<WallpaperState> {
Ok(Self::demo())
}
pub fn get_immich_import_progress(&self) -> crate::Result<ImmichImportProgress> {
Ok(ImmichImportProgress::default())
}
}