Skip to content

Commit

Permalink
Code Quality: POC loop OneDrive thumbnails until cached
Browse files Browse the repository at this point in the history
  • Loading branch information
yaira2 committed Jan 26, 2024
1 parent 4e6f024 commit 7f10763
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 36 deletions.
16 changes: 11 additions & 5 deletions src/Files.App/Data/Models/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -941,11 +941,8 @@ private async Task<BitmapImage> GetShieldIcon()
}

// ThumbnailSize is set to 96 so that unless we override it, mode is in turn set to SingleItem
private async Task LoadItemThumbnailAsync(ListedItem item, uint thumbnailSize = 96, IStorageItem? matchingStorageItem = null)
private async Task<bool> LoadItemThumbnailAsync(ListedItem item, uint thumbnailSize = 96, IStorageItem? matchingStorageItem = null)
{
var wasIconLoaded = false;


if (item.IsLibrary || item.PrimaryItemAttribute == StorageItemTypes.File || item.IsArchive)
{
var getIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false;
Expand All @@ -972,6 +969,8 @@ private async Task LoadItemThumbnailAsync(ListedItem item, uint thumbnailSize =
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

return iconInfo.isIconCached;
}
else
{
Expand All @@ -993,6 +992,8 @@ private async Task LoadItemThumbnailAsync(ListedItem item, uint thumbnailSize =
item.ShieldIcon = await GetShieldIcon();
}, Microsoft.UI.Dispatching.DispatcherQueuePriority.Low);
}

return iconInfo.isIconCached;
}
}

Expand Down Expand Up @@ -1044,12 +1045,12 @@ public async Task LoadExtendedItemPropertiesAsync(ListedItem item, uint thumbnai
if (matchingStorageFile is not null)
{
cts.Token.ThrowIfCancellationRequested();
await LoadItemThumbnailAsync(item, thumbnailSize, matchingStorageFile);
var syncStatus = await CheckCloudDriveSyncStatusAsync(matchingStorageFile);
var fileFRN = await FileTagsHelper.GetFileFRN(matchingStorageFile);
var fileTag = FileTagsHelper.ReadFileTag(item.ItemPath);
var itemType = (item.ItemType == "Folder".GetLocalizedResource()) ? item.ItemType : matchingStorageFile.DisplayType;
cts.Token.ThrowIfCancellationRequested();
await dispatcherQueue.EnqueueOrInvokeAsync(() =>
Expand All @@ -1064,6 +1065,11 @@ public async Task LoadExtendedItemPropertiesAsync(ListedItem item, uint thumbnai
SetFileTag(item);
wasSyncStatusLoaded = true;
var cancellationTokenSource = new CancellationTokenSource(3000);
// Loop until cached thumbnail is loaded or timeout is reached
while (!await LoadItemThumbnailAsync(item, thumbnailSize, matchingStorageFile))
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
}
Expand Down
70 changes: 47 additions & 23 deletions src/Files.App/Utils/Shell/Win32API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,31 @@ private class IconAndOverlayCacheEntry

private static readonly object _lock = new object();

public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path, int thumbnailSize, bool isFolder, bool getIconOnly, bool getOverlay = true, bool onlyGetOverlay = false)
/// <summary>
/// Returns an icon when a thumbnail isn't available or if getIconOnly is true
/// Returns an icon overlay when getOverlay is true
/// Returns a boolean indicating if the icon/thumbnail is cached
/// </summary>
/// <param name="path"></param>
/// <param name="thumbnailSize"></param>
/// <param name="isFolder"></param>
/// <param name="getIconOnly"></param>
/// <param name="getOverlay"></param>
/// <param name="onlyGetOverlay"></param>
/// <returns></returns>
public static (byte[]? icon, byte[]? overlay, bool isIconCached) GetFileIconAndOverlay
(
string path,
int thumbnailSize,
bool isFolder,
bool getIconOnly,
bool getOverlay = true,
bool onlyGetOverlay = false
)
{
byte[]? iconData = null, overlayData = null;
bool isIconCached = false;

var entry = _iconAndOverlayCache.GetOrAdd(path, _ => new());

if (entry.TryGetValue(thumbnailSize, out var cacheEntry))
Expand All @@ -242,34 +264,36 @@ public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path,
(!getOverlay && iconData is not null) ||
(overlayData is not null && iconData is not null))
{
return (iconData, overlayData);
return (iconData, overlayData, true);
}
}

try
{
if (!onlyGetOverlay)
{
using var shellItem = SafetyExtensions.IgnoreExceptions(()
=> ShellFolderExtensions.GetShellItemFromPathOrPIDL(path));
// Attempt to get file icon/thumbnail using IShellItemImageFactory GetImage
using var shellItem = SafetyExtensions.IgnoreExceptions(()
=> ShellFolderExtensions.GetShellItemFromPathOrPIDL(path));

if (shellItem is not null && shellItem.IShellItem is Shell32.IShellItemImageFactory fctry)
{
var flags = Shell32.SIIGBF.SIIGBF_BIGGERSIZEOK;
if (shellItem is not null && shellItem.IShellItem is Shell32.IShellItemImageFactory shellFactory)
{
var flags = Shell32.SIIGBF.SIIGBF_BIGGERSIZEOK;

if (getIconOnly)
flags |= Shell32.SIIGBF.SIIGBF_ICONONLY;
if (getIconOnly)
flags |= Shell32.SIIGBF.SIIGBF_ICONONLY;
else
flags |= Shell32.SIIGBF.SIIGBF_THUMBNAILONLY;

var hres = fctry.GetImage(new SIZE(thumbnailSize, thumbnailSize), flags, out var hbitmap);
if (hres == HRESULT.S_OK)
{
using var image = GetBitmapFromHBitmap(hbitmap);
if (image is not null)
iconData = (byte[]?)new ImageConverter().ConvertTo(image, typeof(byte[]));
}
var hres = shellFactory.GetImage(new SIZE(thumbnailSize, thumbnailSize), flags, out var hbitmap);
if (hres == HRESULT.S_OK)
{
using var image = GetBitmapFromHBitmap(hbitmap);
if (image is not null)
iconData = (byte[]?)new ImageConverter().ConvertTo(image, typeof(byte[]));

//Marshal.ReleaseComObject(fctry);
isIconCached = true;
}

Marshal.ReleaseComObject(shellFactory);
}

if (getOverlay || (!onlyGetOverlay && iconData is null))
Expand All @@ -284,7 +308,7 @@ public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path,
Shell32.SHGetFileInfo(pidl, 0, ref shfi, Shell32.SHFILEINFO.Size, Shell32.SHGFI.SHGFI_PIDL | flags) :
Shell32.SHGetFileInfo(path, isFolder ? FileAttributes.Directory : 0, ref shfi, Shell32.SHFILEINFO.Size, flags | (useFileAttibutes ? Shell32.SHGFI.SHGFI_USEFILEATTRIBUTES : 0));
if (ret == IntPtr.Zero)
return (iconData, null);
return (iconData, null, isIconCached);

User32.DestroyIcon(shfi.hIcon);

Expand All @@ -299,7 +323,7 @@ public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path,
lock (_lock)
{
if (!Shell32.SHGetImageList(imageListSize, typeof(ComCtl32.IImageList).GUID, out var imageListOut).Succeeded)
return (iconData, null);
return (iconData, null, isIconCached);

var imageList = (ComCtl32.IImageList)imageListOut;

Expand Down Expand Up @@ -352,11 +376,11 @@ public static (byte[]? icon, byte[]? overlay) GetFileIconAndOverlay(string path,
Marshal.ReleaseComObject(imageList);
}

return (iconData, overlayData);
return (iconData, overlayData, isIconCached);
}
else
{
return (iconData, null);
return (iconData, null, isIconCached);
}
}
finally
Expand Down
11 changes: 3 additions & 8 deletions src/Files.App/Utils/Storage/Helpers/FileThumbnailHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,12 @@ namespace Files.App.Utils.Storage
{
public static class FileThumbnailHelper
{
public static Task<(byte[] IconData, byte[] OverlayData)> LoadIconAndOverlayAsync(string filePath, uint thumbnailSize, bool isFolder = false, bool getIconOnly = false)
=> Win32API.StartSTATask(() => Win32API.GetFileIconAndOverlay(filePath, (int)thumbnailSize, isFolder, getIconOnly, true, false));

public static async Task<byte[]> LoadOverlayAsync(string filePath, uint thumbnailSize)
{
return (await Win32API.StartSTATask(() => Win32API.GetFileIconAndOverlay(filePath, (int)thumbnailSize, false, false, true, true))).overlay;
}
public static Task<(byte[] IconData, byte[] OverlayData, bool isIconCached)> LoadIconAndOverlayAsync(string filePath, uint thumbnailSize, bool isFolder = false, bool getIconOnly = false)
=> Win32API.StartSTATask(() => Win32API.GetFileIconAndOverlay(filePath, (int)thumbnailSize, isFolder, getIconOnly));

public static async Task<byte[]> LoadIconWithoutOverlayAsync(string filePath, uint thumbnailSize, bool isFolder, bool getIconOnly)
{
return (await Win32API.StartSTATask(() => Win32API.GetFileIconAndOverlay(filePath, (int)thumbnailSize, isFolder, getIconOnly, false))).icon;
return (await Win32API.StartSTATask(() => Win32API.GetFileIconAndOverlay(filePath, (int)thumbnailSize, isFolder, getIconOnly))).icon;
}

public static async Task<byte[]> LoadIconFromStorageItemAsync(IStorageItem item, uint thumbnailSize, ThumbnailMode thumbnailMode, ThumbnailOptions thumbnailOptions)
Expand Down

0 comments on commit 7f10763

Please sign in to comment.