зеркало из https://github.com/DeGsoft/maui-linux.git
Remove usage of IsolatedStorage (#2710)
This commit is contained in:
Родитель
c905fd6260
Коммит
0a3229912d
|
@ -15,7 +15,7 @@
|
|||
]
|
||||
},
|
||||
"microsoft.dotnet.xharness.cli": {
|
||||
"version": "1.0.0-prerelease.21425.2",
|
||||
"version": "1.0.0-prerelease.21558.2",
|
||||
"commands": [
|
||||
"xharness"
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
using System.IO.IsolatedStorage;
|
||||
using System;
|
||||
using Microsoft.Maui.Controls.Compatibility.ControlGallery;
|
||||
using IOPath = System.IO.Path;
|
||||
|
||||
|
@ -8,22 +8,7 @@ namespace Microsoft.Maui.Controls.Compatibility.ControlGallery.Android
|
|||
{
|
||||
public void ClearImageCache()
|
||||
{
|
||||
DeleteFilesInDirectory("ImageLoaderCache");
|
||||
}
|
||||
|
||||
static void DeleteFilesInDirectory(string directory)
|
||||
{
|
||||
using (IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
|
||||
{
|
||||
if (isolatedStorage.DirectoryExists(directory))
|
||||
{
|
||||
var files = isolatedStorage.GetFileNames(IOPath.Combine(directory, "*"));
|
||||
foreach (string file in files)
|
||||
{
|
||||
isolatedStorage.DeleteFile(IOPath.Combine(directory, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new NotImplementedException("TODO: CACHING https://github.com/dotnet/runtime/issues/52332");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http;
|
||||
|
@ -665,24 +664,6 @@ namespace Microsoft.Maui.Controls.Compatibility
|
|||
return null;
|
||||
}
|
||||
|
||||
public async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
using (var client = new HttpClient())
|
||||
{
|
||||
// Do not remove this await otherwise the client will dispose before
|
||||
// the stream even starts
|
||||
var result = await StreamWrapper.GetStreamAsync(uri, cancellationToken, client).ConfigureAwait(false);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
throw new NotImplementedException("GetUserStoreForApplication currently not available https://github.com/dotnet/runtime/issues/52332");
|
||||
//return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
|
||||
}
|
||||
|
||||
public bool IsInvokeRequired
|
||||
{
|
||||
get
|
||||
|
@ -783,49 +764,6 @@ namespace Microsoft.Maui.Controls.Compatibility
|
|||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class _IsolatedStorageFile : IIsolatedStorageFile
|
||||
{
|
||||
readonly IsolatedStorageFile _isolatedStorageFile;
|
||||
|
||||
public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
|
||||
{
|
||||
_isolatedStorageFile = isolatedStorageFile;
|
||||
}
|
||||
|
||||
public Task CreateDirectoryAsync(string path)
|
||||
{
|
||||
_isolatedStorageFile.CreateDirectory(path);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<bool> GetDirectoryExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(_isolatedStorageFile.DirectoryExists(path));
|
||||
}
|
||||
|
||||
public Task<bool> GetFileExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(_isolatedStorageFile.FileExists(path));
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
|
||||
{
|
||||
return Task.FromResult(_isolatedStorageFile.GetLastWriteTime(path));
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access, share);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,11 +147,6 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
|
|||
}
|
||||
}
|
||||
|
||||
public IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
return new WindowsIsolatedStorage(ApplicationData.Current.LocalFolder);
|
||||
}
|
||||
|
||||
public bool IsInvokeRequired => !_dispatcher?.HasThreadAccess ?? true;
|
||||
|
||||
public string RuntimePlatform => Device.UWP;
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.FileProperties;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
//#if !UWP_16299
|
||||
//using FileMode = Microsoft.Maui.Controls.Compatibility.FileMode;
|
||||
//using FileAccess = Microsoft.Maui.Controls.Compatibility.Internals.FileAccess;
|
||||
//using FileShare = Microsoft.Maui.Controls.Compatibility.Internals.FileShare;
|
||||
//#endif
|
||||
|
||||
namespace Microsoft.Maui.Controls.Compatibility.Platform.UWP
|
||||
{
|
||||
internal class WindowsIsolatedStorage : Internals.IIsolatedStorageFile
|
||||
{
|
||||
StorageFolder _folder;
|
||||
|
||||
public WindowsIsolatedStorage(StorageFolder folder)
|
||||
{
|
||||
if (folder == null)
|
||||
throw new ArgumentNullException("folder");
|
||||
|
||||
_folder = folder;
|
||||
}
|
||||
|
||||
public Task CreateDirectoryAsync(string path)
|
||||
{
|
||||
return _folder.CreateFolderAsync(path).AsTask();
|
||||
}
|
||||
|
||||
public async Task<bool> GetDirectoryExistsAsync(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _folder.GetFolderAsync(path).AsTask().ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> GetFileExistsAsync(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
|
||||
return true;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
|
||||
{
|
||||
StorageFile file = await _folder.GetFileAsync(path).AsTask().ConfigureAwait(false);
|
||||
BasicProperties properties = await file.GetBasicPropertiesAsync().AsTask().ConfigureAwait(false);
|
||||
return properties.DateModified;
|
||||
}
|
||||
|
||||
public async Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
StorageFile file;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case FileMode.CreateNew:
|
||||
file = await _folder.CreateFileAsync(path, CreationCollisionOption.FailIfExists).AsTask().ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case FileMode.Create:
|
||||
case FileMode.Truncate: // TODO See if ReplaceExisting already truncates
|
||||
file = await _folder.CreateFileAsync(path, CreationCollisionOption.ReplaceExisting).AsTask().ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case FileMode.OpenOrCreate:
|
||||
case FileMode.Append:
|
||||
file = await _folder.CreateFileAsync(path, CreationCollisionOption.OpenIfExists).AsTask().ConfigureAwait(false);
|
||||
break;
|
||||
|
||||
case FileMode.Open:
|
||||
file = await _folder.GetFileAsync(path);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("mode was an invalid FileMode", "mode");
|
||||
}
|
||||
|
||||
switch (access)
|
||||
{
|
||||
case FileAccess.Read:
|
||||
return await file.OpenStreamForReadAsync().ConfigureAwait(false);
|
||||
case FileAccess.Write:
|
||||
Stream stream = await file.OpenStreamForWriteAsync().ConfigureAwait(false);
|
||||
if (mode == FileMode.Append)
|
||||
stream.Position = stream.Length;
|
||||
|
||||
return stream;
|
||||
|
||||
case FileAccess.ReadWrite:
|
||||
IRandomAccessStream randStream = await file.OpenAsync(FileAccessMode.ReadWrite).AsTask().ConfigureAwait(false);
|
||||
return randStream.AsStream();
|
||||
|
||||
default:
|
||||
throw new ArgumentException("access was an invalid FileAccess", "access");
|
||||
}
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
return OpenFileAsync(path, mode, access);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http;
|
||||
|
@ -637,11 +636,6 @@ namespace Microsoft.Maui.Controls.Compatibility
|
|||
}
|
||||
}
|
||||
|
||||
public IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
return new _IsolatedStorageFile(IsolatedStorageFile.GetUserStoreForApplication());
|
||||
}
|
||||
|
||||
public bool IsInvokeRequired => !NSThread.IsMain;
|
||||
|
||||
#if __MOBILE__
|
||||
|
@ -679,49 +673,6 @@ namespace Microsoft.Maui.Controls.Compatibility
|
|||
return 'a' + v - 10;
|
||||
}
|
||||
|
||||
public class _IsolatedStorageFile : IIsolatedStorageFile
|
||||
{
|
||||
readonly IsolatedStorageFile _isolatedStorageFile;
|
||||
|
||||
public _IsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
|
||||
{
|
||||
_isolatedStorageFile = isolatedStorageFile;
|
||||
}
|
||||
|
||||
public Task CreateDirectoryAsync(string path)
|
||||
{
|
||||
_isolatedStorageFile.CreateDirectory(path);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<bool> GetDirectoryExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(_isolatedStorageFile.DirectoryExists(path));
|
||||
}
|
||||
|
||||
public Task<bool> GetFileExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(_isolatedStorageFile.FileExists(path));
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
|
||||
{
|
||||
return Task.FromResult(_isolatedStorageFile.GetLastWriteTime(path));
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
Stream stream = _isolatedStorageFile.OpenFile(path, mode, access, share);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
}
|
||||
|
||||
#if !__MOBILE__
|
||||
public void QuitApplication()
|
||||
{
|
||||
|
|
|
@ -2,7 +2,6 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Reflection;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Maui.Controls;
|
||||
using System.Security.Cryptography;
|
||||
|
@ -116,53 +115,6 @@ namespace Microsoft.Maui.Controls.DualScreen.UnitTests
|
|||
return AppDomain.CurrentDomain.GetAssemblies();
|
||||
}
|
||||
|
||||
public Internals.IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
return new MockIsolatedStorageFile(IsolatedStorageFile.GetUserStoreForAssembly());
|
||||
}
|
||||
|
||||
public class MockIsolatedStorageFile : Internals.IIsolatedStorageFile
|
||||
{
|
||||
readonly IsolatedStorageFile isolatedStorageFile;
|
||||
public MockIsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
|
||||
{
|
||||
this.isolatedStorageFile = isolatedStorageFile;
|
||||
}
|
||||
|
||||
public Task<bool> GetDirectoryExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.DirectoryExists(path));
|
||||
}
|
||||
|
||||
public Task CreateDirectoryAsync(string path)
|
||||
{
|
||||
isolatedStorageFile.CreateDirectory(path);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
Stream stream = isolatedStorageFile.OpenFile(path, mode, access);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
Stream stream = isolatedStorageFile.OpenFile(path, mode, access, share);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<bool> GetFileExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.FileExists(path));
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.GetLastWriteTime(path));
|
||||
}
|
||||
}
|
||||
|
||||
public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
|
||||
{
|
||||
if (getNativeSizeFunc != null)
|
||||
|
|
|
@ -169,11 +169,6 @@ namespace Microsoft.Maui.Controls
|
|||
return PlatformServices.GetNamedColor(name);
|
||||
}
|
||||
|
||||
internal static Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
return PlatformServices.GetStreamAsync(uri, cancellationToken);
|
||||
}
|
||||
|
||||
public static class Styles
|
||||
{
|
||||
public static readonly string TitleStyleKey = "TitleStyle";
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Maui.Controls.Internals
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public interface IIsolatedStorageFile
|
||||
{
|
||||
Task CreateDirectoryAsync(string path);
|
||||
Task<bool> GetDirectoryExistsAsync(string path);
|
||||
Task<bool> GetFileExistsAsync(string path);
|
||||
|
||||
Task<DateTimeOffset> GetLastWriteTimeAsync(string path);
|
||||
|
||||
Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access);
|
||||
Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share);
|
||||
}
|
||||
}
|
|
@ -23,10 +23,6 @@ namespace Microsoft.Maui.Controls.Internals
|
|||
|
||||
OSAppTheme RequestedTheme { get; }
|
||||
|
||||
Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken);
|
||||
|
||||
IIsolatedStorageFile GetUserStoreForApplication();
|
||||
|
||||
void StartTimer(TimeSpan interval, Func<bool> callback);
|
||||
|
||||
string RuntimePlatform { get; }
|
||||
|
|
|
@ -1,76 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Maui.Controls.Internals;
|
||||
using IOPath = System.IO.Path;
|
||||
|
||||
namespace Microsoft.Maui.Controls
|
||||
{
|
||||
// TODO: CACHING https://github.com/dotnet/runtime/issues/52332
|
||||
public sealed partial class UriImageSource : ImageSource, IStreamImageSource
|
||||
{
|
||||
internal const string CacheName = "ImageLoaderCache";
|
||||
public static readonly BindableProperty UriProperty = BindableProperty.Create(
|
||||
nameof(Uri), typeof(Uri), typeof(UriImageSource), default(Uri),
|
||||
propertyChanged: (bindable, oldvalue, newvalue) => ((UriImageSource)bindable).OnUriChanged(),
|
||||
validateValue: (bindable, value) => value == null || ((Uri)value).IsAbsoluteUri);
|
||||
|
||||
public static readonly BindableProperty UriProperty = BindableProperty.Create("Uri", typeof(Uri), typeof(UriImageSource), default(Uri),
|
||||
propertyChanged: (bindable, oldvalue, newvalue) => ((UriImageSource)bindable).OnUriChanged(), validateValue: (bindable, value) => value == null || ((Uri)value).IsAbsoluteUri);
|
||||
public static readonly BindableProperty CacheValidityProperty = BindableProperty.Create(
|
||||
nameof(CacheValidity), typeof(TimeSpan), typeof(UriImageSource), TimeSpan.FromDays(1));
|
||||
|
||||
// https://github.com/dotnet/runtime/issues/52332
|
||||
// static readonly Microsoft.Maui.Controls.Internals.IIsolatedStorageFile Store = Device.PlatformServices.GetUserStoreForApplication();
|
||||
|
||||
static readonly object s_syncHandle = new object();
|
||||
static readonly Dictionary<string, LockingSemaphore> s_semaphores = new Dictionary<string, LockingSemaphore>();
|
||||
|
||||
TimeSpan _cacheValidity = TimeSpan.FromDays(1);
|
||||
|
||||
bool _cachingEnabled = true;
|
||||
|
||||
static UriImageSource()
|
||||
{
|
||||
/*if (!Store.GetDirectoryExistsAsync(CacheName).Result)
|
||||
Store.CreateDirectoryAsync(CacheName).Wait();*/
|
||||
}
|
||||
public static readonly BindableProperty CachingEnabledProperty = BindableProperty.Create(
|
||||
nameof(CachingEnabled), typeof(bool), typeof(UriImageSource), true);
|
||||
|
||||
public override bool IsEmpty => Uri == null;
|
||||
|
||||
public TimeSpan CacheValidity
|
||||
{
|
||||
get { return _cacheValidity; }
|
||||
set
|
||||
{
|
||||
if (_cacheValidity == value)
|
||||
return;
|
||||
|
||||
OnPropertyChanging();
|
||||
_cacheValidity = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
get => (TimeSpan)GetValue(CacheValidityProperty);
|
||||
set => SetValue(CacheValidityProperty, value);
|
||||
}
|
||||
|
||||
public bool CachingEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
//return _cachingEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_cachingEnabled == value)
|
||||
return;
|
||||
|
||||
OnPropertyChanging();
|
||||
_cachingEnabled = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
get => (bool)GetValue(CachingEnabledProperty);
|
||||
set => SetValue(CachingEnabledProperty, value);
|
||||
}
|
||||
|
||||
[System.ComponentModel.TypeConverter(typeof(UriTypeConverter))]
|
||||
public Uri Uri
|
||||
{
|
||||
get { return (Uri)GetValue(UriProperty); }
|
||||
set { SetValue(UriProperty, value); }
|
||||
get => (Uri)GetValue(UriProperty);
|
||||
set => SetValue(UriProperty, value);
|
||||
}
|
||||
|
||||
async Task<Stream> IStreamImageSource.GetStreamAsync(CancellationToken userToken)
|
||||
|
@ -94,7 +62,7 @@ namespace Microsoft.Maui.Controls
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Microsoft.Maui.Controls.Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
|
||||
Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -106,23 +74,6 @@ namespace Microsoft.Maui.Controls
|
|||
return $"Uri: {Uri}";
|
||||
}
|
||||
|
||||
Task<bool> GetHasLocallyCachedCopyAsync(string key, bool checkValidity = true)
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
//DateTime now = DateTime.UtcNow;
|
||||
//DateTime? lastWriteTime = await GetLastWriteTimeUtcAsync(key).ConfigureAwait(false);
|
||||
//return lastWriteTime.HasValue && now - lastWriteTime.Value < CacheValidity;
|
||||
}
|
||||
|
||||
//static async Task<DateTime?> GetLastWriteTimeUtcAsync(string key)
|
||||
//{
|
||||
// string path = IOPath.Combine(CacheName, key);
|
||||
// if (!await Store.GetFileExistsAsync(path).ConfigureAwait(false))
|
||||
// return null;
|
||||
|
||||
// return (await Store.GetLastWriteTimeAsync(path).ConfigureAwait(false)).UtcDateTime;
|
||||
//}
|
||||
|
||||
async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
@ -130,127 +81,45 @@ namespace Microsoft.Maui.Controls
|
|||
Stream stream = null;
|
||||
|
||||
if (CachingEnabled)
|
||||
stream = await GetStreamFromCacheAsync(uri, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
stream = await Device.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Microsoft.Maui.Controls.Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
|
||||
stream = null;
|
||||
}
|
||||
// TODO: CACHING https://github.com/dotnet/runtime/issues/52332
|
||||
|
||||
// var key = GetKey();
|
||||
// var cached = TryGetFromCache(key, out stream)
|
||||
if (stream is null)
|
||||
stream = await DownloadStreamAsync(uri, cancellationToken).ConfigureAwait(false);
|
||||
// if (!cached)
|
||||
// Cache(key, stream)
|
||||
}
|
||||
else
|
||||
{
|
||||
stream = await DownloadStreamAsync(uri, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
async Task<Stream> GetStreamAsyncUnchecked(string key, Uri uri, CancellationToken cancellationToken)
|
||||
async Task<Stream> DownloadStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
//if (await GetHasLocallyCachedCopyAsync(key).ConfigureAwait(false))
|
||||
//{
|
||||
// var retry = 5;
|
||||
// while (retry >= 0)
|
||||
// {
|
||||
// int backoff;
|
||||
// try
|
||||
// {
|
||||
// Stream result = await Store.OpenFileAsync(IOPath.Combine(CacheName, key), FileMode.Open, FileAccess.Read).ConfigureAwait(false);
|
||||
// return result;
|
||||
// }
|
||||
// catch (IOException)
|
||||
// {
|
||||
// // iOS seems to not like 2 readers opening the file at the exact same time, back off for random amount of time
|
||||
// backoff = new Random().Next(1, 5);
|
||||
// retry--;
|
||||
// }
|
||||
|
||||
// if (backoff > 0)
|
||||
// {
|
||||
// await Task.Delay(backoff);
|
||||
// }
|
||||
// }
|
||||
// return null;
|
||||
//}
|
||||
|
||||
Stream stream;
|
||||
try
|
||||
{
|
||||
stream = await Device.GetStreamAsync(uri, cancellationToken).ConfigureAwait(false);
|
||||
if (stream == null)
|
||||
return null;
|
||||
using var client = new HttpClient();
|
||||
|
||||
// Do not remove this await otherwise the client will dispose before
|
||||
// the stream even starts
|
||||
return await StreamWrapper.GetStreamAsync(uri, cancellationToken, client).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
|
||||
Internals.Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stream == null || !stream.CanRead)
|
||||
{
|
||||
stream?.Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
return stream;
|
||||
|
||||
//try
|
||||
//{
|
||||
// Stream writeStream = await Store.OpenFileAsync(IOPath.Combine(CacheName, key), FileMode.Create, FileAccess.Write).ConfigureAwait(false);
|
||||
// await stream.CopyToAsync(writeStream, 16384, cancellationToken).ConfigureAwait(false);
|
||||
// if (writeStream != null)
|
||||
// writeStream.Dispose();
|
||||
|
||||
// stream.Dispose();
|
||||
|
||||
// return await Store.OpenFileAsync(IOPath.Combine(CacheName, key), FileMode.Open, FileAccess.Read).ConfigureAwait(false);
|
||||
//}
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// Log.Warning("Image Loading", $"Error getting stream for {Uri}: {ex}");
|
||||
// return null;
|
||||
//}
|
||||
}
|
||||
|
||||
async Task<Stream> GetStreamFromCacheAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
string key = Crc64.ComputeHashString(uri.AbsoluteUri);
|
||||
LockingSemaphore sem;
|
||||
lock (s_syncHandle)
|
||||
{
|
||||
if (s_semaphores.ContainsKey(key))
|
||||
sem = s_semaphores[key];
|
||||
else
|
||||
s_semaphores.Add(key, sem = new LockingSemaphore(1));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await sem.WaitAsync(cancellationToken);
|
||||
Stream stream = await GetStreamAsyncUnchecked(key, uri, cancellationToken);
|
||||
if (stream == null || stream.Length == 0 || !stream.CanRead)
|
||||
{
|
||||
sem.Release();
|
||||
return null;
|
||||
}
|
||||
var wrapped = new StreamWrapper(stream);
|
||||
wrapped.Disposed += (o, e) => sem.Release();
|
||||
return wrapped;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
sem.Release();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void OnUriChanged()
|
||||
{
|
||||
if (CancellationTokenSource != null)
|
||||
CancellationTokenSource.Cancel();
|
||||
CancellationTokenSource?.Cancel();
|
||||
|
||||
OnSourceChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,23 +7,8 @@ using NUnit.Framework;
|
|||
namespace Microsoft.Maui.Controls.Core.UnitTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ImageButtonTests
|
||||
: CommandSourceTests<ImageButton>
|
||||
public class ImageButtonTests : CommandSourceTests<ImageButton>
|
||||
{
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
Device.PlatformServices = new MockPlatformServices(getStreamAsync: GetStreamAsync);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSizing()
|
||||
{
|
||||
|
@ -205,38 +190,40 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
[Test]
|
||||
public void TestImageSourceToNullCancelsLoading()
|
||||
{
|
||||
var cancelled = false;
|
||||
|
||||
var image = new ImageButton();
|
||||
var mockImageRenderer = new MockImageRenderer(image);
|
||||
var loader = new UriImageSource { Uri = new Uri("http://www.public-domain-image.com/free-images/miscellaneous/big-high-border-fence.jpg") };
|
||||
var loader = new StreamImageSource { Stream = GetStreamAsync };
|
||||
|
||||
image.Source = loader;
|
||||
Assert.IsTrue(image.IsLoading);
|
||||
|
||||
image.Source = null;
|
||||
Assert.IsFalse(image.IsLoading);
|
||||
Assert.IsTrue(cancelled);
|
||||
}
|
||||
|
||||
static bool cancelled;
|
||||
|
||||
static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
async Task<Stream> GetStreamAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(5000, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
cancelled = true;
|
||||
throw ex;
|
||||
}
|
||||
try
|
||||
{
|
||||
await Task.Delay(5000, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
cancelled = true;
|
||||
throw ex;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
cancelled = true;
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
cancelled = true;
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
var stream = typeof(ImageTests).Assembly.GetManifestResourceStream(uri.LocalPath.Substring(1));
|
||||
return stream;
|
||||
var stream = typeof(ImageTests).Assembly.GetManifestResourceStream("dummy");
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
class MockImageRenderer
|
||||
|
@ -258,9 +245,19 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
if (initialLoad && Element.Source != null)
|
||||
{
|
||||
initialLoad = false;
|
||||
Element.SetIsLoading(true);
|
||||
await ((IStreamImageSource)Element.Source).GetStreamAsync();
|
||||
Element.SetIsLoading(false);
|
||||
try
|
||||
{
|
||||
Element.SetIsLoading(true);
|
||||
await ((IStreamImageSource)Element.Source).GetStreamAsync();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
finally
|
||||
{
|
||||
Element.SetIsLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,20 +11,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
[TestFixture]
|
||||
public class ImageTests : BaseTestFixture
|
||||
{
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
Device.PlatformServices = new MockPlatformServices(getStreamAsync: GetStreamAsync);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
Device.PlatformServices = null;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSizing()
|
||||
{
|
||||
|
@ -204,38 +190,39 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
[Test]
|
||||
public void TestImageSourceToNullCancelsLoading()
|
||||
{
|
||||
var cancelled = false;
|
||||
|
||||
var image = new Image();
|
||||
var mockImageRenderer = new MockImageRenderer(image);
|
||||
var loader = new UriImageSource { Uri = new Uri("http://www.public-domain-image.com/free-images/miscellaneous/big-high-border-fence.jpg") };
|
||||
var loader = new StreamImageSource { Stream = GetStreamAsync };
|
||||
|
||||
image.Source = loader;
|
||||
Assert.IsTrue(image.IsLoading);
|
||||
|
||||
image.Source = null;
|
||||
Assert.IsFalse(image.IsLoading);
|
||||
Assert.IsTrue(cancelled);
|
||||
}
|
||||
|
||||
static bool cancelled;
|
||||
|
||||
static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
async Task<Stream> GetStreamAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(5000, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException ex)
|
||||
{
|
||||
cancelled = true;
|
||||
throw ex;
|
||||
}
|
||||
try
|
||||
{
|
||||
await Task.Delay(5000, cancellationToken);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
cancelled = true;
|
||||
throw;
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
cancelled = true;
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
cancelled = true;
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
var stream = typeof(ImageTests).Assembly.GetManifestResourceStream(uri.LocalPath.Substring(1));
|
||||
return stream;
|
||||
return typeof(ImageTests).Assembly.GetManifestResourceStream("dummy");
|
||||
}
|
||||
}
|
||||
|
||||
class MockImageRenderer
|
||||
|
@ -258,9 +245,19 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
{
|
||||
initialLoad = false;
|
||||
var controller = (IImageController)Element;
|
||||
controller.SetIsLoading(true);
|
||||
await ((IStreamImageSource)Element.Source).GetStreamAsync();
|
||||
controller.SetIsLoading(false);
|
||||
try
|
||||
{
|
||||
controller.SetIsLoading(true);
|
||||
await ((IStreamImageSource)Element.Source).GetStreamAsync();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// this is expected
|
||||
}
|
||||
finally
|
||||
{
|
||||
controller.SetIsLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -305,11 +305,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void QuitApplication()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
@ -24,18 +23,15 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
internal class MockPlatformServices : Internals.IPlatformServices
|
||||
{
|
||||
Action<Action> invokeOnMainThread;
|
||||
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync;
|
||||
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc;
|
||||
readonly bool useRealisticLabelMeasure;
|
||||
readonly bool _isInvokeRequired;
|
||||
|
||||
public MockPlatformServices(Action<Action> invokeOnMainThread = null,
|
||||
Func<Uri, CancellationToken, Task<Stream>> getStreamAsync = null,
|
||||
Func<VisualElement, double, double, SizeRequest> getNativeSizeFunc = null,
|
||||
bool useRealisticLabelMeasure = false, bool isInvokeRequired = false)
|
||||
{
|
||||
this.invokeOnMainThread = invokeOnMainThread;
|
||||
this.getStreamAsync = getStreamAsync;
|
||||
this.getNativeSizeFunc = getNativeSizeFunc;
|
||||
this.useRealisticLabelMeasure = useRealisticLabelMeasure;
|
||||
_isInvokeRequired = isInvokeRequired;
|
||||
|
@ -105,65 +101,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
timer = new Timer(onTimeout, null, interval, interval);
|
||||
}
|
||||
|
||||
public Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken)
|
||||
{
|
||||
if (getStreamAsync == null)
|
||||
throw new NotImplementedException();
|
||||
return getStreamAsync(uri, cancellationToken);
|
||||
}
|
||||
|
||||
public Assembly[] GetAssemblies()
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies();
|
||||
}
|
||||
|
||||
public Internals.IIsolatedStorageFile GetUserStoreForApplication()
|
||||
{
|
||||
return new MockIsolatedStorageFile(IsolatedStorageFile.GetUserStoreForAssembly());
|
||||
}
|
||||
|
||||
public class MockIsolatedStorageFile : Internals.IIsolatedStorageFile
|
||||
{
|
||||
readonly IsolatedStorageFile isolatedStorageFile;
|
||||
public MockIsolatedStorageFile(IsolatedStorageFile isolatedStorageFile)
|
||||
{
|
||||
this.isolatedStorageFile = isolatedStorageFile;
|
||||
}
|
||||
|
||||
public Task<bool> GetDirectoryExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.DirectoryExists(path));
|
||||
}
|
||||
|
||||
public Task CreateDirectoryAsync(string path)
|
||||
{
|
||||
isolatedStorageFile.CreateDirectory(path);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access)
|
||||
{
|
||||
Stream stream = isolatedStorageFile.OpenFile(path, mode, access);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<Stream> OpenFileAsync(string path, FileMode mode, FileAccess access, FileShare share)
|
||||
{
|
||||
Stream stream = isolatedStorageFile.OpenFile(path, mode, access, share);
|
||||
return Task.FromResult(stream);
|
||||
}
|
||||
|
||||
public Task<bool> GetFileExistsAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.FileExists(path));
|
||||
}
|
||||
|
||||
public Task<DateTimeOffset> GetLastWriteTimeAsync(string path)
|
||||
{
|
||||
return Task.FromResult(isolatedStorageFile.GetLastWriteTime(path));
|
||||
}
|
||||
}
|
||||
|
||||
public SizeRequest GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
|
||||
{
|
||||
if (getNativeSizeFunc != null)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.IO.IsolatedStorage;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
|
@ -11,14 +10,11 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
[TestFixture]
|
||||
public class UriImageSourceTests : BaseTestFixture
|
||||
{
|
||||
IsolatedStorageFile NativeStore { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
Device.PlatformServices = new MockPlatformServices(getStreamAsync: GetStreamAsync);
|
||||
NativeStore = IsolatedStorageFile.GetUserStoreForAssembly();
|
||||
Device.PlatformServices = new MockPlatformServices();
|
||||
networkcalls = 0;
|
||||
}
|
||||
|
||||
|
@ -27,14 +23,6 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
|
|||
{
|
||||
base.TearDown();
|
||||
Device.PlatformServices = null;
|
||||
string cacheName = "ImageLoaderCache";
|
||||
if (NativeStore.DirectoryExists(cacheName))
|
||||
{
|
||||
foreach (var f in NativeStore.GetFileNames(cacheName + "/*"))
|
||||
NativeStore.DeleteFile(IOPath.Combine(cacheName, f));
|
||||
}
|
||||
NativeStore.Dispose();
|
||||
NativeStore = null;
|
||||
}
|
||||
|
||||
static Random rnd = new Random();
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21425.2" />
|
||||
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21558.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.utility" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21425.2" />
|
||||
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.21558.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
Загрузка…
Ссылка в новой задаче