574 строки
14 KiB
C#
574 строки
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using Xamarin.Forms.Internals;
|
|
using Xamarin.Forms.CustomAttributes;
|
|
using System.Threading.Tasks;
|
|
|
|
#if UITEST
|
|
using Xamarin.UITest;
|
|
using Xamarin.Forms.Core.UITests;
|
|
using NUnit.Framework;
|
|
#endif
|
|
|
|
namespace Xamarin.Forms.Controls.Issues
|
|
{
|
|
[Preserve(AllMembers = true)]
|
|
[Issue(IssueTracker.Github, 1704, "[Enhancement] Basic GIF animation features", PlatformAffected.UWP)]
|
|
public class Issue1704 : TestTabbedPage
|
|
{
|
|
ContentPage _page1;
|
|
ContentPage _page2;
|
|
ContentPage _page3;
|
|
ContentPage _page4;
|
|
|
|
protected override void Init()
|
|
{
|
|
_page1 = new OnLoadAnimationPage { Title = "On Load" };
|
|
_page2 = new OnStartAnimationPage { Title = "On Start" };
|
|
_page3 = new LoadImageSourceAnimationPage { Title = "Source" };
|
|
_page4 = new MiscPage { Title = "Misc" };
|
|
|
|
Children.Add(_page1);
|
|
Children.Add(_page2);
|
|
Children.Add(_page3);
|
|
Children.Add(_page4);
|
|
}
|
|
|
|
#if UITEST
|
|
[Test]
|
|
[Category(UITestCategories.ManualReview)]
|
|
public void Issue1704Test()
|
|
{
|
|
RunningApp.WaitForElement("On Load");
|
|
RunningApp.WaitForElement("On Start");
|
|
RunningApp.WaitForElement("Source");
|
|
RunningApp.WaitForElement("Misc");
|
|
|
|
RunningApp.Tap(q => q.Marked("On Load"));
|
|
RunningApp.Tap(q => q.Marked("On Start"));
|
|
RunningApp.WaitForElement(q => q.Marked("Start Animation"));
|
|
RunningApp.Tap(q => q.Marked("Start Animation"));
|
|
RunningApp.Tap(q => q.Marked("Stop Animation"));
|
|
|
|
RunningApp.Tap(q => q.Marked("Misc"));
|
|
RunningApp.WaitForElement(q => q.Marked("Start Animation"));
|
|
RunningApp.Tap(q => q.Marked("Start Animation"));
|
|
RunningApp.Tap(q => q.Marked("Stop Animation"));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
[Preserve(AllMembers = true)]
|
|
class OnLoadAnimationPage : ContentPage
|
|
{
|
|
Label _referenceImageLabel;
|
|
Image _referenceImage;
|
|
Label _animatedImageLabel;
|
|
Image _animatedImage;
|
|
|
|
public OnLoadAnimationPage()
|
|
{
|
|
_referenceImageLabel = new Label
|
|
{
|
|
Text = "Reference image (no animation).",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_referenceImage = new Image
|
|
{
|
|
Source = "GifTwo.gif",
|
|
HorizontalOptions = LayoutOptions.Start
|
|
};
|
|
|
|
_animatedImageLabel = new Label
|
|
{
|
|
Text = "Animated image (if this image isn't animating make sure you are using Fast Renderers).",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_animatedImage = new Image
|
|
{
|
|
Source = "GifTwo.gif",
|
|
HorizontalOptions = LayoutOptions.Start
|
|
};
|
|
|
|
Content = new StackLayout
|
|
{
|
|
Padding = new Thickness(0, 16),
|
|
Children = {
|
|
_referenceImageLabel,
|
|
_referenceImage,
|
|
_animatedImageLabel,
|
|
_animatedImage
|
|
}
|
|
};
|
|
}
|
|
|
|
protected override void OnAppearing()
|
|
{
|
|
base.OnAppearing();
|
|
_animatedImage.IsAnimationPlaying = true;
|
|
}
|
|
}
|
|
|
|
[Preserve(AllMembers = true)]
|
|
class OnStartAnimationPage : ContentPage
|
|
{
|
|
Label _referenceImageLabel;
|
|
Image _referenceImage;
|
|
Label _animatedImageLabel;
|
|
Image _animatedImage;
|
|
Button _startStopButton;
|
|
|
|
public OnStartAnimationPage()
|
|
{
|
|
_referenceImageLabel = new Label
|
|
{
|
|
Text = "Reference image (no animation).",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_referenceImage = new Image
|
|
{
|
|
Source = "GifOne.gif",
|
|
HorizontalOptions = LayoutOptions.Start
|
|
};
|
|
|
|
_animatedImageLabel = new Label
|
|
{
|
|
Text = "Animated image.",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_animatedImage = new Image
|
|
{
|
|
Source = "GifOne.gif",
|
|
HorizontalOptions = LayoutOptions.Start
|
|
};
|
|
|
|
_animatedImage.PropertyChanged += (sender, args) =>
|
|
{
|
|
if (args.PropertyName == nameof(Image.IsAnimationPlaying))
|
|
OnAnimationFinishedPlaying(sender, args);
|
|
};
|
|
|
|
_startStopButton = new Button { Text = "Start Animation", Margin = new Thickness(0, 20, 0, 0) };
|
|
_startStopButton.Clicked += (object sender, EventArgs e) =>
|
|
{
|
|
if (!_animatedImage.IsAnimationPlaying)
|
|
{
|
|
_animatedImage.IsAnimationPlaying = true;
|
|
_startStopButton.Text = "Stop Animation";
|
|
}
|
|
else
|
|
{
|
|
_animatedImage.IsAnimationPlaying = false;
|
|
_startStopButton.Text = "Start Animation";
|
|
}
|
|
};
|
|
|
|
Content = new ScrollView()
|
|
{
|
|
Content =
|
|
new StackLayout
|
|
{
|
|
Padding = new Thickness(0, 16),
|
|
Children = {
|
|
_startStopButton,
|
|
_referenceImageLabel,
|
|
_referenceImage,
|
|
_animatedImageLabel,
|
|
_animatedImage
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
void OnAnimationFinishedPlaying(object sender, EventArgs e)
|
|
{
|
|
_startStopButton.Text = "Start Animation";
|
|
}
|
|
}
|
|
|
|
// Example URI's:
|
|
//
|
|
// Small animated GIF (24 KB compressed, 14 frames)
|
|
// https://media.giphy.com/media/qyjQsUt0p0TT2/giphy.gif
|
|
//
|
|
// Medium animated GIF (184 KB compressed, 30 frames)
|
|
// https://media.giphy.com/media/xThta5b9vezPO75kL6/giphy.gif
|
|
//
|
|
// Semi large GIF (447 KB, 48 frames).
|
|
// https://media.giphy.com/media/AWNxDbtHGIJDW/giphy.gif
|
|
//
|
|
// Large animated GIF (3 MB compressed, 192 frames).
|
|
// https://media.giphy.com/media/YVYRtHiAv1t8Q/giphy.gif
|
|
//
|
|
// Large animated GIF that could trigger OOM scenarios and slow load times (12 MB compressed, 240 frames).
|
|
// http://media.giphy.com/media/mf8UbIDew7e8g/giphy.gif
|
|
//
|
|
[Preserve(AllMembers = true)]
|
|
class LoadImageSourceAnimationPage : ContentPage
|
|
{
|
|
Label _animatedImageLabel;
|
|
Image _animatedImage;
|
|
Entry _imageSource;
|
|
Button _loadImageButton;
|
|
ActivityIndicator _loadingIndicator;
|
|
|
|
class TimerContextData
|
|
{
|
|
public Image AnimationImage { get; set; }
|
|
public Entry ImageSource { get; set; }
|
|
public Button LoadButton { get; set; }
|
|
public ActivityIndicator LoadIndicator { get; set; }
|
|
public Timer Timer { get; set; }
|
|
}
|
|
|
|
public LoadImageSourceAnimationPage()
|
|
{
|
|
_animatedImageLabel = new Label
|
|
{
|
|
Text = "Animated image.",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_animatedImage = new Image
|
|
{
|
|
HorizontalOptions = LayoutOptions.Start,
|
|
};
|
|
|
|
_imageSource = new Entry { Placeholder = "Image Source" };
|
|
|
|
_imageSource.Focused += (object sender, FocusEventArgs e) =>
|
|
{
|
|
_imageSource.TextColor = Color.Default;
|
|
};
|
|
|
|
_loadImageButton = new Button { Text = "Load Image" };
|
|
_loadImageButton.Clicked += (object sender, EventArgs e) =>
|
|
{
|
|
if (!string.IsNullOrEmpty(_imageSource.Text) && !_animatedImage.IsLoading)
|
|
{
|
|
try
|
|
{
|
|
_loadImageButton.IsEnabled = false;
|
|
_imageSource.IsEnabled = false;
|
|
_loadingIndicator.IsVisible = true;
|
|
_loadingIndicator.IsRunning = true;
|
|
|
|
_animatedImage.Source = ImageSource.FromUri(new Uri(_imageSource.Text));
|
|
|
|
var timerContext = new TimerContextData
|
|
{
|
|
AnimationImage = _animatedImage,
|
|
ImageSource = _imageSource,
|
|
LoadButton = _loadImageButton,
|
|
LoadIndicator = _loadingIndicator
|
|
};
|
|
|
|
var onLoadCompleteTimer = new Timer(OnLoadImageComplete, timerContext, 100, 100);
|
|
timerContext.Timer = onLoadCompleteTimer;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
_imageSource.TextColor = Color.Red;
|
|
_loadImageButton.IsEnabled = true;
|
|
_imageSource.IsEnabled = true;
|
|
_loadingIndicator.IsVisible = false;
|
|
_loadingIndicator.IsRunning = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
_loadingIndicator = new ActivityIndicator
|
|
{
|
|
IsVisible = false,
|
|
IsRunning = false
|
|
};
|
|
|
|
Content = new ScrollView()
|
|
{
|
|
Content = new StackLayout
|
|
{
|
|
Padding = new Thickness(0, 16),
|
|
Children = {
|
|
_loadImageButton,
|
|
_animatedImageLabel,
|
|
_animatedImage,
|
|
_imageSource,
|
|
_loadingIndicator,
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
protected override void OnAppearing()
|
|
{
|
|
base.OnAppearing();
|
|
_animatedImage.IsAnimationPlaying = true;
|
|
}
|
|
|
|
static void OnLoadImageComplete(Object state)
|
|
{
|
|
if (state is TimerContextData context)
|
|
{
|
|
lock (context)
|
|
{
|
|
if (context.AnimationImage != null && !context.AnimationImage.IsLoading)
|
|
{
|
|
var animationImage = context.AnimationImage;
|
|
var imageSource = context.ImageSource;
|
|
var loadButton = context.LoadButton;
|
|
var loadingIndicator = context.LoadIndicator;
|
|
|
|
context.Timer?.Dispose();
|
|
context.Timer = null;
|
|
|
|
context.AnimationImage = null;
|
|
context.ImageSource = null;
|
|
context.LoadButton = null;
|
|
context.LoadIndicator = null;
|
|
|
|
Device.BeginInvokeOnMainThread(() =>
|
|
{
|
|
if (loadButton != null)
|
|
loadButton.IsEnabled = true;
|
|
|
|
if (loadingIndicator != null)
|
|
{
|
|
loadingIndicator.IsVisible = false;
|
|
loadingIndicator.IsRunning = false;
|
|
}
|
|
|
|
if (imageSource != null)
|
|
imageSource.IsEnabled = true;
|
|
|
|
if (animationImage != null)
|
|
{
|
|
animationImage.IsAnimationPlaying = true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Preserve(AllMembers = true)]
|
|
class MiscPage : ContentPage
|
|
{
|
|
Label _noAnimationFallbackLabel;
|
|
Image _noAnimationFallbackImage;
|
|
Label _initNoAnimationLabel;
|
|
Image _initNoAnimationImage;
|
|
Button _initNoAnimationButton;
|
|
Label _stressTestLabel;
|
|
Label _stressTestIterationLabel;
|
|
Entry _stressTestItertionEntry;
|
|
Image _stressTestImage;
|
|
Button _startStressTestButton;
|
|
ProgressBar _stressTestProgressBar;
|
|
Button _stopStressTestButton;
|
|
|
|
int _stressTestIterationCount = 1000;
|
|
AutoResetEvent _nextStressTest = new AutoResetEvent(false);
|
|
bool _abortStressTest = false;
|
|
|
|
protected override void OnAppearing()
|
|
{
|
|
base.OnAppearing();
|
|
_noAnimationFallbackImage.IsAnimationPlaying = true;
|
|
_stressTestImage.IsAnimationPlaying = true;
|
|
}
|
|
|
|
public MiscPage()
|
|
{
|
|
_noAnimationFallbackLabel = new Label
|
|
{
|
|
Text = "No animation error fallback.",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_noAnimationFallbackImage = new Image
|
|
{
|
|
Source = "coffee.png",
|
|
HorizontalOptions = LayoutOptions.Start
|
|
};
|
|
|
|
_initNoAnimationLabel = new Label
|
|
{
|
|
Text = "Initial loaded without animation.",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_initNoAnimationImage = new Image
|
|
{
|
|
Source = "GifTwo.gif",
|
|
HorizontalOptions = LayoutOptions.Start
|
|
};
|
|
|
|
_initNoAnimationButton = new Button
|
|
{
|
|
Text = "Start Animation",
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_initNoAnimationButton.Clicked += (object sender, EventArgs e) =>
|
|
{
|
|
|
|
if (!_initNoAnimationImage.IsAnimationPlaying)
|
|
{
|
|
_initNoAnimationImage.IsAnimationPlaying = true;
|
|
_noAnimationFallbackImage.IsAnimationPlaying = true;
|
|
|
|
_initNoAnimationButton.Text = "Stop Animation";
|
|
}
|
|
else
|
|
{
|
|
_initNoAnimationImage.IsAnimationPlaying = false;
|
|
_noAnimationFallbackImage.IsAnimationPlaying = false;
|
|
|
|
_initNoAnimationButton.Text = "Start Animation";
|
|
}
|
|
};
|
|
|
|
_stressTestLabel = new Label
|
|
{
|
|
Text = "Image loading stress test.",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold,
|
|
Margin = new Thickness(0, 24, 0, 0)
|
|
};
|
|
|
|
_stressTestIterationLabel = new Label
|
|
{
|
|
Text = "Test iterations:",
|
|
FontSize = 12,
|
|
FontAttributes = FontAttributes.Bold
|
|
};
|
|
|
|
_stressTestItertionEntry = new Entry { Text = _stressTestIterationCount.ToString() };
|
|
|
|
_stressTestImage = new Image
|
|
{
|
|
Source = "GifTwo.gif",
|
|
HorizontalOptions = LayoutOptions.Start,
|
|
IsVisible = false
|
|
};
|
|
|
|
_startStressTestButton = new Button
|
|
{
|
|
Text = "Run Stress Test",
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_startStressTestButton.Clicked += (object sender, EventArgs e) =>
|
|
{
|
|
|
|
_startStressTestButton.Text = "Running...";
|
|
_startStressTestButton.IsEnabled = false;
|
|
_stopStressTestButton.IsEnabled = true;
|
|
_abortStressTest = false;
|
|
|
|
int.TryParse(_stressTestItertionEntry.Text, out _stressTestIterationCount);
|
|
|
|
#if __UWP__
|
|
Task.Run(runStressTest);
|
|
#else
|
|
ThreadPool.QueueUserWorkItem(delegate
|
|
{ runStressTest(); });
|
|
#endif
|
|
};
|
|
|
|
_stressTestProgressBar = new ProgressBar();
|
|
|
|
_stopStressTestButton = new Button
|
|
{
|
|
Text = "Stop Stress Test",
|
|
IsEnabled = false,
|
|
Margin = new Thickness(0, 12, 0, 12)
|
|
};
|
|
|
|
_stopStressTestButton.Clicked += (object sender, EventArgs e) =>
|
|
{
|
|
_stopStressTestButton.IsEnabled = false;
|
|
_abortStressTest = true;
|
|
};
|
|
|
|
Content = new StackLayout
|
|
{
|
|
Padding = new Thickness(0, 16),
|
|
Children = {
|
|
_noAnimationFallbackLabel,
|
|
_noAnimationFallbackImage,
|
|
_initNoAnimationLabel,
|
|
_initNoAnimationImage,
|
|
_initNoAnimationButton,
|
|
_stressTestLabel,
|
|
_stressTestIterationLabel,
|
|
_stressTestItertionEntry,
|
|
_stressTestImage,
|
|
_startStressTestButton,
|
|
_stressTestProgressBar,
|
|
_stopStressTestButton
|
|
}
|
|
};
|
|
}
|
|
|
|
async void runStressTest()
|
|
{
|
|
for (int i = 0; i < _stressTestIterationCount && !_abortStressTest; i++)
|
|
{
|
|
Device.BeginInvokeOnMainThread(() =>
|
|
{
|
|
if (i % 2 == 0)
|
|
{
|
|
_stressTestImage.Source = "GifTwo.gif";
|
|
}
|
|
else
|
|
{
|
|
_stressTestImage.Source = "GifOne.gif";
|
|
}
|
|
|
|
_stressTestProgressBar.Progress = (double)i / (double)_stressTestIterationCount;
|
|
|
|
_nextStressTest.Set();
|
|
});
|
|
|
|
_nextStressTest.WaitOne();
|
|
|
|
while (_stressTestImage.IsLoading)
|
|
await Task.Delay(10).ConfigureAwait(false);
|
|
|
|
await Task.Delay(10).ConfigureAwait(false);
|
|
}
|
|
|
|
Device.BeginInvokeOnMainThread(() =>
|
|
{
|
|
_startStressTestButton.Text = "Run Stress Test";
|
|
_startStressTestButton.IsEnabled = true;
|
|
_stopStressTestButton.IsEnabled = false;
|
|
if (!_abortStressTest)
|
|
_stressTestProgressBar.Progress = 1;
|
|
});
|
|
}
|
|
}
|
|
}
|