280 lines
8.7 KiB
C#
280 lines
8.7 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Xamarin.Forms;
|
|
|
|
namespace Gallery.Utils
|
|
{
|
|
public static class Extensions
|
|
{
|
|
public static T Binding<T>(this T view, BindableProperty property, string name,
|
|
BindingMode mode = BindingMode.Default, IValueConverter converter = null) where T : BindableObject
|
|
{
|
|
if (name == null)
|
|
{
|
|
view.SetValue(property, property.DefaultValue);
|
|
}
|
|
else
|
|
{
|
|
view.SetBinding(property, name, mode, converter);
|
|
}
|
|
return view;
|
|
}
|
|
|
|
public static T DynamicResource<T>(this T view, BindableProperty property, string key) where T : Element
|
|
{
|
|
view.SetDynamicResource(property, key);
|
|
return view;
|
|
}
|
|
|
|
public static T GridRow<T>(this T view, int row) where T : BindableObject
|
|
{
|
|
Grid.SetRow(view, row);
|
|
return view;
|
|
}
|
|
|
|
public static T GridColumn<T>(this T view, int column) where T : BindableObject
|
|
{
|
|
Grid.SetColumn(view, column);
|
|
return view;
|
|
}
|
|
|
|
public static T GridColumnSpan<T>(this T view, int columnSpan) where T : BindableObject
|
|
{
|
|
Grid.SetColumnSpan(view, columnSpan);
|
|
return view;
|
|
}
|
|
|
|
public static int IndexOf<T>(this T[] array, Predicate<T> predicate)
|
|
{
|
|
for (var i = 0; i < array.Length; i++)
|
|
{
|
|
if (predicate(array[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static int LastIndexOf<T>(this T[] array, Predicate<T> predicate)
|
|
{
|
|
for (var i = array.Length - 1; i >= 0; i--)
|
|
{
|
|
if (predicate(array[i]))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public static bool All<T>(this T[] array, Predicate<T> predicate)
|
|
{
|
|
for (var i = 0; i < array.Length; i++)
|
|
{
|
|
if (!predicate(array[i]))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public static bool AnyFor<T>(this T[] array, int from, int to, Predicate<T> predicate)
|
|
{
|
|
for (var i = from; i <= to; i++)
|
|
{
|
|
if (predicate(array[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class ParallelTask : IDisposable
|
|
{
|
|
public static ParallelTask Start(string tag, int from, int toExclusive, int maxCount, Predicate<int> action, int tagIndex = -1, Action complete = null)
|
|
{
|
|
if (toExclusive <= from)
|
|
{
|
|
if (complete != null)
|
|
{
|
|
Task.Run(complete);
|
|
}
|
|
return null;
|
|
}
|
|
var task = new ParallelTask(tag, from, toExclusive, maxCount, action, tagIndex, complete);
|
|
task.Start();
|
|
return task;
|
|
}
|
|
|
|
private readonly object sync = new object();
|
|
private int count;
|
|
private bool disposed;
|
|
|
|
public int TagIndex { get; private set; }
|
|
private readonly string tag;
|
|
private readonly int max;
|
|
private readonly int from;
|
|
private readonly int to;
|
|
private readonly Predicate<int> action;
|
|
private readonly Action complete;
|
|
|
|
private ParallelTask(string tag, int from, int to, int maxCount, Predicate<int> action, int tagIndex, Action complete)
|
|
{
|
|
if (maxCount <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(maxCount));
|
|
}
|
|
max = maxCount;
|
|
if (from >= to)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(from));
|
|
}
|
|
TagIndex = tagIndex;
|
|
this.tag = tag;
|
|
this.from = from;
|
|
this.to = to;
|
|
this.action = action;
|
|
this.complete = complete;
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
_ = ThreadPool.QueueUserWorkItem(DoStart);
|
|
}
|
|
|
|
private void DoStart(object state)
|
|
{
|
|
#if LOG
|
|
var sw = new System.Diagnostics.Stopwatch();
|
|
long lastElapsed = 0;
|
|
sw.Start();
|
|
#endif
|
|
for (int i = from; i < to; i++)
|
|
{
|
|
var index = i;
|
|
while (true)
|
|
{
|
|
if (count < max)
|
|
{
|
|
break;
|
|
}
|
|
#if LOG
|
|
var elapsed = sw.ElapsedMilliseconds;
|
|
if (elapsed - lastElapsed > 60000)
|
|
{
|
|
lastElapsed = elapsed;
|
|
App.DebugPrint($"WARNING: parallel task ({tag}), {count} tasks in queue, cost too much time ({elapsed:n0}ms)");
|
|
}
|
|
#endif
|
|
if (disposed)
|
|
{
|
|
#if LOG
|
|
sw.Stop();
|
|
App.DebugPrint($"parallel task determinate, disposed ({tag}), cost time ({elapsed:n0}ms)");
|
|
#endif
|
|
return;
|
|
}
|
|
Thread.Sleep(16);
|
|
}
|
|
lock (sync)
|
|
{
|
|
count++;
|
|
}
|
|
ThreadPool.QueueUserWorkItem(o =>
|
|
//Task.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
if (!action(index))
|
|
{
|
|
disposed = true;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
App.DebugError($"parallel.start ({tag})", $"failed to run action, index: {index}, error: {ex}");
|
|
}
|
|
finally
|
|
{
|
|
lock (sync)
|
|
{
|
|
count--;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
while (count > 0)
|
|
{
|
|
#if LOG
|
|
var elapsed = sw.ElapsedMilliseconds;
|
|
if (elapsed - lastElapsed > 60000)
|
|
{
|
|
lastElapsed = elapsed;
|
|
App.DebugPrint($"WARNING: parallel task ({tag}), {count} tasks are waiting for end, cost too much time ({elapsed:n0}ms)");
|
|
}
|
|
#endif
|
|
if (disposed)
|
|
{
|
|
#if LOG
|
|
sw.Stop();
|
|
App.DebugPrint($"parallel task determinate, disposed ({tag}), cost time ({elapsed:n0}ms)");
|
|
#endif
|
|
return;
|
|
}
|
|
Thread.Sleep(16);
|
|
}
|
|
#if LOG
|
|
sw.Stop();
|
|
App.DebugPrint($"parallel task done ({tag}), cost time ({sw.ElapsedMilliseconds:n0}ms)");
|
|
#endif
|
|
complete?.Invoke();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
disposed = true;
|
|
}
|
|
}
|
|
|
|
public static class Screen
|
|
{
|
|
private const string StatusBarStyle = nameof(StatusBarStyle);
|
|
private const string HomeIndicatorAutoHidden = nameof(HomeIndicatorAutoHidden);
|
|
|
|
public static readonly BindableProperty StatusBarStyleProperty = BindableProperty.CreateAttached(
|
|
StatusBarStyle,
|
|
typeof(StatusBarStyles),
|
|
typeof(Page),
|
|
StatusBarStyles.WhiteText);
|
|
public static StatusBarStyles GetStatusBarStyle(VisualElement page) => (StatusBarStyles)page.GetValue(StatusBarStyleProperty);
|
|
public static void SetStatusBarStyle(VisualElement page, StatusBarStyles value) => page.SetValue(StatusBarStyleProperty, value);
|
|
|
|
public static readonly BindableProperty HomeIndicatorAutoHiddenProperty = BindableProperty.CreateAttached(
|
|
HomeIndicatorAutoHidden,
|
|
typeof(bool),
|
|
typeof(Shell),
|
|
false);
|
|
public static bool GetHomeIndicatorAutoHidden(VisualElement page) => (bool)page.GetValue(HomeIndicatorAutoHiddenProperty);
|
|
public static void SetHomeIndicatorAutoHidden(VisualElement page, bool value) => page.SetValue(HomeIndicatorAutoHiddenProperty, value);
|
|
}
|
|
|
|
public enum StatusBarStyles
|
|
{
|
|
Default,
|
|
// Will behave as normal.
|
|
// White text on black NavigationBar/in iOS Dark mode and
|
|
// Black text on white NavigationBar/in iOS Light mode
|
|
DarkText,
|
|
// Will switch the color of content of StatusBar to black.
|
|
WhiteText,
|
|
// Will switch the color of content of StatusBar to white.
|
|
Hidden
|
|
// Will hide the StatusBar
|
|
}
|
|
}
|