5using System.Collections.ObjectModel;
6using System.Security.Cryptography;
24 private readonly ObservableCollection<Photo> photos = Photos;
25 private readonly List<string> attachmentIds = [];
26 private DateTime loadPhotosTimestamp;
32 public PhotosLoader() : this([])
45 return this.LoadPhotos(Attachments,
SignWith, DateTime.UtcNow, WhenDoneAction);
52 public void CancelLoadPhotos()
56 this.loadPhotosTimestamp = DateTime.UtcNow;
57 this.attachmentIds.Clear();
83 return (
null,
string.Empty, 0);
88 if (Attachments is
null || Attachments.Length <= 0)
90 WhenDoneAction?.Invoke();
94 List<Attachment> attachmentsList = Attachments.GetImageAttachments().ToList();
95 List<string> newAttachmentIds = attachmentsList.Select(x => x.Id).ToList();
97 if (this.attachmentIds.HasSameContentAs(newAttachmentIds))
99 WhenDoneAction?.Invoke();
101 foreach (Photo Photo
in this.photos)
107 this.attachmentIds.Clear();
108 this.attachmentIds.AddRange(newAttachmentIds);
112 foreach (
Attachment attachment
in attachmentsList)
114 if (this.loadPhotosTimestamp > Now)
116 WhenDoneAction?.Invoke();
118 foreach (Photo Photo
in this.photos)
126 (
byte[]? Bin,
string ContentType,
int Rotation) = await this.GetPhoto(attachment,
SignWith, Now);
136 TaskCompletionSource<bool> PhotoAddedTaskSource =
new();
138 MainThread.BeginInvokeOnMainThread(() =>
140 this.photos.Add(Photo);
141 PhotoAddedTaskSource.TrySetResult(
true);
144 await PhotoAddedTaskSource.Task;
153 WhenDoneAction?.Invoke();
161 return (
null,
string.Empty, 0);
166 return (Bin, ContentType, GetImageRotation(Bin));
169 return (
null,
string.Empty, 0);
175 if (this.loadPhotosTimestamp > Now)
176 return (
null,
string.Empty, 0);
178 if (pair.Value.Length >
int.MaxValue)
179 return (
null,
string.Empty, 0);
184 Bin =
new byte[file.Length];
186 if (file.Length != file.Read(Bin, 0, (
int)file.Length))
187 return (
null,
string.Empty, 0);
193 return (Bin, ContentType, GetImageRotation(Bin));
201 public static int GetImageRotation(
byte[] JpegImage)
204 if (DeviceInfo.Platform == DevicePlatform.iOS)
207 if (JpegImage is
null)
213 return GetImageRotation(Tags);
221 public static int GetImageRotation(
ExifTag[] Tags)
227 if (Tag.
Value is ushort Orientation)
229 return Orientation
switch
255 PhotosLoader Loader =
new();
257 (
byte[]?, string, int) Image = await Loader.LoadOnePhoto(
Attachment,
SignWith.LatestApprovedIdOrCurrentKeys);
269 public static Task<(
string?, int, int)> LoadPhotoAsTemporaryFile(
Attachment[] Attachments,
int MaxWith,
int MaxHeight)
285 return Task.FromResult<(
string?, int, int)>((
null, 0, 0));
287 return LoadPhotoAsTemporaryFile(Photo, MaxWith, MaxHeight);
297 public static async Task<(
string?, int, int)> LoadPhotoAsTemporaryFile(
Attachment Attachment,
int MaxWith,
int MaxHeight)
299 (
byte[]? Data,
string _,
int _) = await LoadPhoto(
Attachment);
301 if (Data is not
null)
303 string FileName = await GetTemporaryFile(Data);
307 using (SKBitmap Bitmap = SKBitmap.Decode(Data))
309 Width = Bitmap.Width;
310 Height = Bitmap.Height;
313 double ScaleWidth = ((double)MaxWith) / Width;
314 double ScaleHeight = ((double)MaxHeight) / Height;
315 double Scale = Math.Min(ScaleWidth, ScaleHeight);
319 Width = (int)(Width * Scale + 0.5);
320 Height = (int)(Height * Scale + 0.5);
323 return (FileName, Width, Height);
329 #region From Waher.Content.Markdown.Model.Multimedia.ImageContent, with permission
336 public static Task<string> GetTemporaryFile(
byte[] BinaryImage)
338 return GetTemporaryFile(BinaryImage,
"tmp");
347 public static async Task<string> GetTemporaryFile(
byte[] BinaryImage,
string FileExtension)
349 byte[] Digest = SHA256.HashData(BinaryImage);
350 string FileName = Path.Combine(Path.GetTempPath(),
"tmp" +
Base64Url.
Encode(Digest) +
"." + FileExtension);
352 if (!File.Exists(FileName))
358 if (temporaryFiles is
null)
361 Log.Terminating += CurrentDomain_ProcessExit;
364 temporaryFiles[FileName] =
true;
371 private static Dictionary<string, bool>? temporaryFiles =
null;
372 private static readonly
object synchObject =
new();
374 private static void CurrentDomain_ProcessExit(
object? sender, EventArgs e)
378 if (temporaryFiles is not
null)
380 foreach (
string FileName
in temporaryFiles.Keys)
384 File.Delete(FileName);
392 temporaryFiles.Clear();
const string Png
The PNG MIME type.
static readonly TimeSpan DownloadFile
Download file timeout
A set of never changing property constants and helpful values.
Base class that references services in the app.
static ILogService LogService
Log service.
static INetworkService NetworkService
Network service.
static IAttachmentCacheService AttachmentCacheService
AttachmentCache service.
static IXmppService XmppService
The XMPP service for XMPP communication.
A base class for all view models, inheriting from the BindableObject. NOTE: using this class requir...
Static class that does BASE64URL encoding (using URL and filename safe alphabet), as defined in RFC46...
static string Encode(byte[] Data)
Converts a binary block of data to a Base64URL-encoded string.
Extracts EXIF meta-data from images.
static bool TryExtractFromJPeg(string FileName, out ExifTag[] Tags)
Tries to extract EXIF meta-data from a JPEG image.
Abstract base class for EXIF meta-data tags.
ExifTagName Name
EXIF Tag Name
abstract object Value
EXIF Tag Value
Static class managing loading of resources stored as embedded resources or in content files.
static Task WriteAllBytesAsync(string FileName, byte[] Data)
Creates a binary file asynchronously.
Contains a reference to an attachment assigned to a legal object.
string LegalId
Legal ID of uploader of attachment
string ContentType
Internet Content Type of binary attachment.
string Url
URL to retrieve attachment, if provided.
Class managing the contents of a temporary file. When the class is disposed, the temporary file is de...
Task Add(string Url, string ParentId, bool Permanent, byte[] Data, string ContentType)
Adds an image to the cache.
Task<(byte[]?, string)> TryGet(string Url)
Tries to get a cached image given the specified url.
class Photo(byte[] Binary, int Rotation)
Class containing information about a photo.
ExifTagName
Defined EXIF Tag names
SignWith
Options on what keys to use when signing data.
ContentType
DTLS Record content type.