diff --git a/Pixiview.iOS/Effects/LongPressEffectImplement.cs b/Pixiview.iOS/Effects/LongPressEffectImplement.cs new file mode 100644 index 0000000..00f6ead --- /dev/null +++ b/Pixiview.iOS/Effects/LongPressEffectImplement.cs @@ -0,0 +1,58 @@ +using System.Threading.Tasks; +using Pixiview.iOS.Effects; +using Pixiview.Utils; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ResolutionGroupName("Pixiview")] +[assembly: ExportEffect(typeof(LongPressEffectImplement), "LongPressEffect")] +namespace Pixiview.iOS.Effects +{ + public class LongPressEffectImplement : PlatformEffect + { + private bool attached; + private readonly UILongPressGestureRecognizer longPressGesture; + + public LongPressEffectImplement() + { + longPressGesture = new UILongPressGestureRecognizer(OnLongPressed); + } + + protected override void OnAttached() + { + if (!attached) + { + attached = true; + Container.AddGestureRecognizer(longPressGesture); + } + } + + protected override void OnDetached() + { + if (attached) + { + attached = false; + Container.RemoveGestureRecognizer(longPressGesture); + } + } + + private void OnLongPressed(UILongPressGestureRecognizer e) + { + if (e.State != UIGestureRecognizerState.Began) + { + return; + } + var element = Element; + if (element != null) + { + var command = LongPressEffect.GetCommand(element); + if (command != null) + { + var o = LongPressEffect.GetCommandParameter(element); + command.Execute(o); + } + } + } + } +} diff --git a/Pixiview.iOS/Pixiview.iOS.csproj b/Pixiview.iOS/Pixiview.iOS.csproj index 8b5ff94..e93e687 100644 --- a/Pixiview.iOS/Pixiview.iOS.csproj +++ b/Pixiview.iOS/Pixiview.iOS.csproj @@ -79,6 +79,7 @@ + @@ -148,6 +149,7 @@ + diff --git a/Pixiview.iOS/Services/EnvironmentService.cs b/Pixiview.iOS/Services/EnvironmentService.cs index 9588281..307546e 100644 --- a/Pixiview.iOS/Services/EnvironmentService.cs +++ b/Pixiview.iOS/Services/EnvironmentService.cs @@ -29,29 +29,28 @@ namespace Pixiview.iOS.Services #region - Theme - [SuppressMessage("Code Notifications", "XI0002:Notifies you from using newer Apple APIs when targeting an older OS version", Justification = "")] - public Theme GetApplicationTheme() + public OSAppTheme GetApplicationTheme() { if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) { var currentController = Platform.GetCurrentUIViewController(); if (currentController == null) { - return Theme.Light; + return OSAppTheme.Unspecified; } + var style = currentController.TraitCollection.UserInterfaceStyle; if (style == UIUserInterfaceStyle.Dark) { - return Theme.Dark; + return OSAppTheme.Dark; } - else + else if (style == UIUserInterfaceStyle.Light) { - return Theme.Light; + return OSAppTheme.Light; } } - else - { - return Theme.Light; - } + + return OSAppTheme.Unspecified; } public void SetStatusBarStyle(StatusBarStyles style) diff --git a/Pixiview/App.cs b/Pixiview/App.cs index f924c2f..31ab7ed 100644 --- a/Pixiview/App.cs +++ b/Pixiview/App.cs @@ -11,7 +11,7 @@ namespace Pixiview public class App : Application { // public properties - public static Theme CurrentTheme { get; private set; } + public static OSAppTheme CurrentTheme { get; private set; } public static PlatformCulture CurrentCulture { get; private set; } public static Dictionary ExtraResources { get; private set; } @@ -39,7 +39,7 @@ namespace Pixiview CurrentCulture = new PlatformCulture(ci.Name.ToLower()); } - private void SetTheme(Theme theme, bool force = false) + private void SetTheme(OSAppTheme theme, bool force = false) { if (force || theme != CurrentTheme) { @@ -51,7 +51,7 @@ namespace Pixiview } DebugPrint($"application theme: {theme}"); ThemeBase themeInstance; - if (theme == Theme.Dark) + if (theme == OSAppTheme.Dark) { themeInstance = DarkTheme.Instance; } diff --git a/Pixiview/Illust/IllustCollectionPage.cs b/Pixiview/Illust/IllustCollectionPage.cs index a0212ec..af674df 100644 --- a/Pixiview/Illust/IllustCollectionPage.cs +++ b/Pixiview/Illust/IllustCollectionPage.cs @@ -363,7 +363,6 @@ namespace Pixiview.Illust public class IllustCollection : List { - private static readonly object sync = new object(); private static IllustCollection empty; public static IllustCollection Empty @@ -387,7 +386,8 @@ namespace Pixiview.Illust running = true; } - private bool running; + private readonly object sync = new object(); + private volatile bool running; public bool Running { get => running; diff --git a/Pixiview/Illust/ViewIllustPage.xaml b/Pixiview/Illust/ViewIllustPage.xaml index 7420cbf..0dd2967 100644 --- a/Pixiview/Illust/ViewIllustPage.xaml +++ b/Pixiview/Illust/ViewIllustPage.xaml @@ -3,6 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:mdl="clr-namespace:Pixiview.Illust" xmlns:u="clr-namespace:Pixiview.UI" + xmlns:util="clr-namespace:Pixiview.Utils" xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" x:Class="Pixiview.Illust.ViewIllustPage" ios:Page.UseSafeArea="False" @@ -11,7 +12,7 @@ Title="{Binding IllustItem.Title}"> + IconImageSource="{Binding FavoriteIcon}"/> + Aspect="AspectFit" + util:LongPressEffect.Command="{Binding LongPressed}" + util:LongPressEffect.CommandParameter="{Binding .}"> + + + + (ImageSource)GetValue(IsFavoriteProperty); + public ImageSource FavoriteIcon + { + get => (ImageSource)GetValue(FavoriteIconProperty); + set => SetValue(FavoriteIconProperty, value); + } public IllustDetailItem[] Illusts { @@ -50,22 +55,24 @@ namespace Pixiview.Illust public int CurrentPage { get; private set; } private readonly IIllustCollectionPage collectionPage; - private readonly object fontIconLove; - private readonly object fontIconNotLove; + private readonly ICommand longPressed; + private readonly ImageSource fontIconLove; + private readonly ImageSource fontIconNotLove; public ViewIllustPage(IllustItem illust, IIllustCollectionPage page) { IllustItem = illust; collectionPage = page; + longPressed = new Command(Illust_LongPressed); BindingContext = this; - fontIconLove = Application.Current.Resources[ThemeBase.FontIconLove]; - fontIconNotLove = Application.Current.Resources[ThemeBase.FontIconNotLove]; + fontIconLove = (ImageSource)Application.Current.Resources[ThemeBase.FontIconLove]; + fontIconNotLove = (ImageSource)Application.Current.Resources[ThemeBase.FontIconNotLove]; if (page.Favorites != null) { - SetValue(IsFavoriteProperty, page.Favorites.Any(i => i.Id == illust.Id) + FavoriteIcon = page.Favorites.Any(i => i.Id == illust.Id) ? fontIconLove - : fontIconNotLove); + : fontIconNotLove; } InitializeComponent(); @@ -81,38 +88,6 @@ namespace Pixiview.Illust OnOrientationChanged(CurrentOrientation); } - private void LoadIllust(IllustItem illust) - { - if (illust == null) - { - return; - } - - var items = new IllustDetailItem[illust.PageCount]; - if (items.Length > 1) - { - IsPageVisible = true; - PagePositionText = $"1/{items.Length}"; - } - else - { - IsPageVisible = false; - } - - for (var i = 0; i < items.Length; i++) - { - items[i] = new IllustDetailItem(); - if (i == 0) - { - items[i].Loading = true; - items[i].Image = illust.Image; - } - } - - Illusts = items; - Task.Run(DoLoadImages); - } - protected override void OnAppearing() { base.OnAppearing(); @@ -135,27 +110,39 @@ namespace Pixiview.Illust Screen.SetHomeIndicatorAutoHidden(Shell.Current, false); } - private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e) + private void LoadIllust(IllustItem illust) { - var index = e.CurrentPosition; - CurrentPage = index; - var items = Illusts; - var length = items.Length; - PagePositionText = $"{index + 1}/{length}"; - - var item = items[index]; - if (!item.Loading && item.Image == null) + if (illust == null) { - Task.Run(() => DoLoadImage(index)); + return; } - if (index < length - 1) + + var items = new IllustDetailItem[illust.PageCount]; + if (items.Length > 1) { - item = items[index + 1]; - if (!item.Loading && item.Image == null) + IsPageVisible = true; + PagePositionText = $"1/{items.Length}"; + } + else + { + IsPageVisible = false; + } + + for (var i = 0; i < items.Length; i++) + { + items[i] = new IllustDetailItem { - Task.Run(() => DoLoadImage(index + 1)); + LongPressed = longPressed + }; + if (i == 0) + { + items[i].Loading = true; + items[i].Image = illust.Image; } } + + Illusts = items; + Task.Run(DoLoadImages); } private void DoLoadImages() @@ -174,7 +161,10 @@ namespace Pixiview.Illust items.CopyTo(items, 0); for (var i = items.Length; i < tmp.Length; i++) { - tmp[i] = new IllustDetailItem(); + tmp[i] = new IllustDetailItem + { + LongPressed = longPressed + }; } items = tmp; } @@ -221,6 +211,29 @@ namespace Pixiview.Illust item.Loading = false; } + private void CarouselView_PositionChanged(object sender, PositionChangedEventArgs e) + { + var index = e.CurrentPosition; + CurrentPage = index; + var items = Illusts; + var length = items.Length; + PagePositionText = $"{index + 1}/{length}"; + + var item = items[index]; + if (!item.Loading && item.Image == null) + { + Task.Run(() => DoLoadImage(index)); + } + if (index < length - 1) + { + item = items[index + 1]; + if (!item.Loading && item.Image == null) + { + Task.Run(() => DoLoadImage(index + 1)); + } + } + } + private void Favorite_Clicked(object sender, EventArgs e) { if (collectionPage.Favorites == null) @@ -232,36 +245,55 @@ namespace Pixiview.Illust { collectionPage.Favorites.Insert(0, IllustItem); IllustItem.IsFavorite = true; - SetValue(IsFavoriteProperty, fontIconLove); + FavoriteIcon = fontIconLove; } else { collectionPage.Favorites.RemoveAt(index); IllustItem.IsFavorite = false; - SetValue(IsFavoriteProperty, fontIconNotLove); + FavoriteIcon = fontIconNotLove; } } - private async void Download_Clicked(object sender, EventArgs e) + private async void Illust_LongPressed(IllustDetailItem item) { - var status = await Permissions.CheckStatusAsync(); - if (status != PermissionStatus.Granted) + var saveOriginal = ResourceHelper.SaveOriginal; + var result = await DisplayActionSheet( + IllustItem.Title, + ResourceHelper.Cancel, + saveOriginal); + if (result == saveOriginal) { - status = await Permissions.RequestAsync(); + if (Stores.CheckIllustImage(item.OriginalUrl)) + { + var flag = await DisplayAlert(ResourceHelper.Operation, + ResourceHelper.AlreadySavedQuestion, + ResourceHelper.Yes, + ResourceHelper.No); + if (!flag) + { + return; + } + } + + var status = await Permissions.CheckStatusAsync(); if (status != PermissionStatus.Granted) { - App.DebugPrint("access denied to gallery."); + status = await Permissions.RequestAsync(); + if (status != PermissionStatus.Granted) + { + App.DebugPrint("access denied to gallery."); + return; + } + } + + if (item == null || item.Downloading) + { return; } + item.Downloading = true; + _ = Task.Run(() => DoLoadOriginalImage(item)); } - - var item = Illusts[CurrentPage]; - if (item.Downloading) - { - return; - } - item.Downloading = true; - _ = Task.Run(() => DoLoadOriginalImage(item)); } private void DoLoadOriginalImage(IllustDetailItem item) @@ -306,6 +338,7 @@ namespace Pixiview.Illust get => (bool)GetValue(DownloadingProperty); set => SetValue(DownloadingProperty, value); } + public ICommand LongPressed { get; set; } public string PreviewUrl { get; set; } public string OriginalUrl { get; set; } } diff --git a/Pixiview/Resources/Languages/zh-CN.xml b/Pixiview/Resources/Languages/zh-CN.xml index 7f595a9..5cad518 100644 --- a/Pixiview/Resources/Languages/zh-CN.xml +++ b/Pixiview/Resources/Languages/zh-CN.xml @@ -2,6 +2,9 @@ Pixiview OK + 取消 + + R-18 已关注 推荐 @@ -11,5 +14,8 @@ 收藏夹 预览 + 操作 + 保存原图 成功保存图片到照片库。 + 原图已保存,是否继续? \ No newline at end of file diff --git a/Pixiview/Resources/ResourceHelper.cs b/Pixiview/Resources/ResourceHelper.cs index fa1dc1a..f0722d7 100644 --- a/Pixiview/Resources/ResourceHelper.cs +++ b/Pixiview/Resources/ResourceHelper.cs @@ -12,8 +12,14 @@ namespace Pixiview.Resources { public static string Title => GetResource(nameof(Title)); public static string Ok => GetResource(nameof(Ok)); + public static string Cancel => GetResource(nameof(Cancel)); + public static string Yes => GetResource(nameof(Yes)); + public static string No => GetResource(nameof(No)); public static string R18 => GetResource(nameof(R18)); + public static string Operation => GetResource(nameof(Operation)); + public static string SaveOriginal => GetResource(nameof(SaveOriginal)); public static string SaveSuccess => GetResource(nameof(SaveSuccess)); + public static string AlreadySavedQuestion => GetResource(nameof(AlreadySavedQuestion)); static readonly Dictionary dict = new Dictionary(); diff --git a/Pixiview/UI/AdaptedPage.cs b/Pixiview/UI/AdaptedPage.cs index 453cbe5..abae771 100644 --- a/Pixiview/UI/AdaptedPage.cs +++ b/Pixiview/UI/AdaptedPage.cs @@ -83,16 +83,12 @@ namespace Pixiview.UI public static bool IsBusy => _instance?.isBusy == true; private static readonly object sync = new object(); - private static Tap _instance; + private static readonly Tap _instance = new Tap(); private Tap() { } public static Tap Start() { - if (_instance == null) - { - _instance = new Tap(); - } lock (sync) { _instance.isBusy = true; @@ -100,7 +96,7 @@ namespace Pixiview.UI return _instance; } - private bool isBusy = false; + private volatile bool isBusy = false; public void Dispose() { diff --git a/Pixiview/Utils/Converters.cs b/Pixiview/Utils/Converters.cs index b8fe024..1dbaeda 100644 --- a/Pixiview/Utils/Converters.cs +++ b/Pixiview/Utils/Converters.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using Pixiview.UI; -using Pixiview.UI.Theme; using Xamarin.Forms; namespace Pixiview.Utils diff --git a/Pixiview/Utils/IEnvironmentService.cs b/Pixiview/Utils/IEnvironmentService.cs index 015681b..ea7c9da 100644 --- a/Pixiview/Utils/IEnvironmentService.cs +++ b/Pixiview/Utils/IEnvironmentService.cs @@ -7,7 +7,7 @@ namespace Pixiview.Utils { EnvironmentParameter GetEnvironment(); - Theme GetApplicationTheme(); + OSAppTheme GetApplicationTheme(); void SetStatusBarStyle(StatusBarStyles style); void SetStatusBarColor(Color color); @@ -22,10 +22,4 @@ namespace Pixiview.Utils public string IconSolidFontFamily { get; set; } public string IconLeft { get; set; } } - - public enum Theme - { - Light, - Dark - } } diff --git a/Pixiview/Utils/LongPressEffect.cs b/Pixiview/Utils/LongPressEffect.cs new file mode 100644 index 0000000..e01c879 --- /dev/null +++ b/Pixiview/Utils/LongPressEffect.cs @@ -0,0 +1,26 @@ +using System.Windows.Input; +using Xamarin.Forms; + +namespace Pixiview.Utils +{ + public class LongPressEffect : RoutingEffect + { + private const string Command = nameof(Command); + private const string CommandParameter = nameof(CommandParameter); + + public static readonly BindableProperty CommandProperty = BindableProperty.CreateAttached( + Command, typeof(ICommand), typeof(LongPressEffect), null); + public static readonly BindableProperty CommandParameterProperty = BindableProperty.CreateAttached( + CommandParameter, typeof(object), typeof(LongPressEffect), null); + + public static ICommand GetCommand(BindableObject view) => (ICommand)view.GetValue(CommandProperty); + public static void SetCommand(BindableObject view, ICommand command) => view.SetValue(CommandProperty, command); + + public static object GetCommandParameter(BindableObject view) => view.GetValue(CommandParameterProperty); + public static void SetCommandParameter(BindableObject view, object value) => view.SetValue(CommandParameterProperty, value); + + public LongPressEffect() : base("Pixiview.LongPressEffect") + { + } + } +} diff --git a/Pixiview/Utils/Stores.cs b/Pixiview/Utils/Stores.cs index 6080675..ac7f4d3 100644 --- a/Pixiview/Utils/Stores.cs +++ b/Pixiview/Utils/Stores.cs @@ -182,7 +182,10 @@ namespace Pixiview.Utils public static IllustFavorite LoadFavoritesIllusts() { var file = Path.Combine(PersonalFolder, favoriteFile); - return ReadObject(file); + lock (sync) + { + return ReadObject(file); + } } public static void SaveFavoritesIllusts(IllustFavorite data) @@ -214,6 +217,12 @@ namespace Pixiview.Utils return LoadImage(url, CacheFolder, userFolder); } + public static bool CheckIllustImage(string url) + { + var file = Path.Combine(PersonalFolder, imageFolder, Path.GetFileName(url)); + return File.Exists(file); + } + private static ImageSource LoadImage(string url, string working, string folder) { var file = Path.Combine(working, folder, Path.GetFileName(url));