Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Quality: Remove thumbnail cache to simplify logic and improve performance #14871

Merged
merged 12 commits into from
Mar 3, 2024
203 changes: 118 additions & 85 deletions src/Files.App/Data/Models/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -937,117 +937,142 @@ private async Task<BitmapImage> GetShieldIcon()
return shieldIcon;
}

private async Task LoadItemThumbnailAsync(ListedItem item)
/// <summary>
/// Loads basic icon using ReturnIconOnly flag
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task LoadBasicIconAsync(ListedItem item)
{
var thumbnailSize = folderSettings.GetRoundedIconSize();

if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive)
{
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48;
var getThumbnailOnly = !item.IsExecutable && !getIconOnly;
var iconInfo = await FileThumbnailHelper.GetIconAsync(
// Get icon
var icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
folderSettings.GetRoundedIconSize(),
item.IsFolder,
false,
getThumbnailOnly,
getIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None);
IconOptions.ReturnIconOnly);

if (!iconInfo.isIconCached)
{
// Assign a placeholder icon while trying to get a cached thumbnail
if (iconInfo.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

// Loop until cached thumbnail is loaded or timeout is reached
var cancellationTokenSource = new CancellationTokenSource(3000);
while (!iconInfo.isIconCached)
{
iconInfo = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
false,
getThumbnailOnly,
getIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None);

if (cancellationTokenSource.Token.IsCancellationRequested)
break;

await Task.Delay(500);
}
}

if (iconInfo.IconData is not null)
if (icon.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
var image = await icon.IconData.ToBitmapAsync();
if (image is not null)
{
// Assign the thumbnail/icon to the listed item
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
// Assign FileImage property
item.FileImage = image;

// Add the file icon to the DefaultIcons list
// Add file icon to the DefaultIcons list
if
(
!item.IsFolder &&
!DefaultIcons.ContainsKey(item.FileExtension.ToLowerInvariant()) &&
!string.IsNullOrEmpty(item.FileExtension) &&
!item.IsShortcut &&
!item.IsExecutable
)
{
var fileIcon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
false,
false,
IconOptions.ReturnIconOnly);

var bitmapImage = await fileIcon.IconData.ToBitmapAsync();
DefaultIcons.TryAdd(item.FileExtension.ToLowerInvariant(), bitmapImage);
DefaultIcons.TryAdd(item.FileExtension.ToLowerInvariant(), image);
}
}
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
// Get icon overlay
var iconOverlay = await FileThumbnailHelper.GetIconOverlayAsync(item.ItemPath, true);
if (iconOverlay is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconOverlay.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}

/// <summary>
/// Loads thumbnail without any flags
/// Returns early if thumbnails aren't needed for this item (eg. if thumbnails are disabled or size is too small)
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task LoadThumbnailAsync(ListedItem item)
{
// Cancel if thumbnails aren't enabled
var thumbnailSize = folderSettings.GetRoundedIconSize();
if (UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48)
return;

// Get thumbnail
var icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
item.IsFolder,
false,
IconOptions.None);

var iconOverlay = await FileThumbnailHelper.GetIconOverlayAsync(item.ItemPath, false);
if (iconOverlay is not null)
if (icon.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
// Assign the icon overlay to the listed item
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconOverlay.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
// Assign FileImage property
var image = await icon.IconData.ToBitmapAsync();
if (image is not null)
item.FileImage = image;
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
else
}

/// <summary>
/// Tries getting a cached thumbnail from the system. This is usually only needed for cloud locations, otherwise LoadThumbnailAsync does the job.
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private async Task LoadCachedThumbnailAsync(ListedItem item)
{
// Cancel for exe files which don't need thumbnails
if (item.IsExecutable)
return;

// Cancel if thumbnails aren't enabled
var thumbnailSize = folderSettings.GetRoundedIconSize();
if (UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48)
return;

// Get icon
var icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
item.IsFolder,
true,
IconOptions.None);

// Loop until cached thumbnail is loaded or timeout is reached
var cancellationTokenSource = new CancellationTokenSource(3000);
while (true)
{
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48;
var iconInfo = await FileThumbnailHelper.GetIconAsync(
icon = await FileThumbnailHelper.GetIconAsync(
item.ItemPath,
thumbnailSize,
item.IsFolder,
true,
false, getIconOnly ? IconOptions.ReturnIconOnly : IconOptions.None);
IconOptions.None);

if (iconInfo.IconData is not null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.FileImage = await iconInfo.IconData.ToBitmapAsync();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
if (icon.isIconCached || cancellationTokenSource.Token.IsCancellationRequested)
break;

await Task.Delay(500);
}

var iconOverlay = await FileThumbnailHelper.GetIconOverlayAsync(item.ItemPath, true);
if (iconOverlay is not null)
// Update FileImage property if icon is cached
if (icon.isIconCached)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
item.IconOverlay = await iconOverlay.ToBitmapAsync();
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
var image = await icon.IconData.ToBitmapAsync();
if (image is not null)
item.FileImage = image;
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}
}

Expand Down Expand Up @@ -1092,7 +1117,7 @@ await Task.Run(async () =>
}

cts.Token.ThrowIfCancellationRequested();
_ = LoadItemThumbnailAsync(item);
await LoadBasicIconAsync(item);

if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive)
{
Expand Down Expand Up @@ -1198,6 +1223,14 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() =>
SetFileTag(item);
});
}
else
{
// Load cached thumbnail for cloud files
if (item.SyncStatusUI.SyncStatus != CloudDriveSyncStatus.NotSynced && item.SyncStatusUI.SyncStatus != CloudDriveSyncStatus.Unknown)
_ = LoadCachedThumbnailAsync(item);
else
_ = LoadThumbnailAsync(item);
}
yaira2 marked this conversation as resolved.
Show resolved Hide resolved

if (loadGroupHeaderInfo)
{
Expand Down
Loading