Android Release / Build signed release APK (push) Successful in 34m17s
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.
182 lines
6.5 KiB
Rust
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())
|
|
}
|
|
}
|