using System; using System.Threading; using System.Threading.Tasks; using Xamarin.Forms; namespace Gallery.Utils { public static class Extensions { public static T Binding(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(this T view, BindableProperty property, string key) where T : Element { view.SetDynamicResource(property, key); return view; } public static T GridRow(this T view, int row) where T : BindableObject { Grid.SetRow(view, row); return view; } public static T GridColumn(this T view, int column) where T : BindableObject { Grid.SetColumn(view, column); return view; } public static T GridColumnSpan(this T view, int columnSpan) where T : BindableObject { Grid.SetColumnSpan(view, columnSpan); return view; } public static int IndexOf(this T[] array, Predicate predicate) { for (var i = 0; i < array.Length; i++) { if (predicate(array[i])) { return i; } } return -1; } public static int LastIndexOf(this T[] array, Predicate predicate) { for (var i = array.Length - 1; i >= 0; i--) { if (predicate(array[i])) { return i; } } return -1; } public static bool All(this T[] array, Predicate predicate) { for (var i = 0; i < array.Length; i++) { if (!predicate(array[i])) { return false; } } return true; } public static bool AnyFor(this T[] array, int from, int to, Predicate 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 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 action; private readonly Action complete; private ParallelTask(string tag, int from, int to, int maxCount, Predicate 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 } }