feature: long press to save original illust

This commit is contained in:
Tsanie Lily 2020-05-08 14:27:15 +08:00
parent e2ecabc224
commit 59cc3a77c9
14 changed files with 237 additions and 102 deletions

View File

@ -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);
}
}
}
}
}

View File

@ -79,6 +79,7 @@
<Compile Include="Renderers\AppShellRenderer.cs" />
<Compile Include="Renderers\AppShellSection\AppShellSectionRootHeader.cs" />
<Compile Include="Renderers\SegmentedControlRenderer.cs" />
<Compile Include="Effects\LongPressEffectImplement.cs" />
</ItemGroup>
<ItemGroup>
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
@ -148,6 +149,7 @@
<Folder Include="Renderers\" />
<Folder Include="Services\" />
<Folder Include="Renderers\AppShellSection\" />
<Folder Include="Effects\" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\fa-light-300.ttf" />

View File

@ -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 = "<Pending>")]
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)

View File

@ -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<string, object> 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;
}

View File

@ -363,7 +363,6 @@ namespace Pixiview.Illust
public class IllustCollection : List<IllustItem>
{
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;

View File

@ -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}">
<ContentPage.ToolbarItems>
<ToolbarItem Order="Primary" Clicked="Favorite_Clicked"
IconImageSource="{Binding IsFavorite}"/>
IconImageSource="{Binding FavoriteIcon}"/>
</ContentPage.ToolbarItems>
<Grid Padding="{Binding PageTopMargin}">
<CarouselView ItemsSource="{Binding Illusts}" HorizontalScrollBarVisibility="Never"
@ -24,7 +25,13 @@
<Grid>
<Image Source="{Binding Image}"
HorizontalOptions="Fill" VerticalOptions="Fill"
Aspect="AspectFit"/>
Aspect="AspectFit"
util:LongPressEffect.Command="{Binding LongPressed}"
util:LongPressEffect.CommandParameter="{Binding .}">
<Image.Effects>
<util:LongPressEffect/>
</Image.Effects>
</Image>
<Frame HasShadow="False" Margin="0" Padding="20" CornerRadius="8"
IsVisible="{Binding Loading}"
HorizontalOptions="Center" VerticalOptions="Center"

View File

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Pixiview.Resources;
using Pixiview.UI;
using Pixiview.UI.Theme;
@ -13,8 +14,8 @@ namespace Pixiview.Illust
[QueryProperty("IllustId", "id")]
public partial class ViewIllustPage : AdaptedPage
{
public static readonly BindableProperty IsFavoriteProperty = BindableProperty.Create(
nameof(IsFavorite), typeof(ImageSource), typeof(ViewIllustPage));
public static readonly BindableProperty FavoriteIconProperty = BindableProperty.Create(
nameof(FavoriteIcon), typeof(ImageSource), typeof(ViewIllustPage));
public static readonly BindableProperty IllustsProperty = BindableProperty.Create(
nameof(Illusts), typeof(IllustDetailItem[]), typeof(ViewIllustPage));
public static readonly BindableProperty PagePositionTextProperty = BindableProperty.Create(
@ -24,7 +25,11 @@ namespace Pixiview.Illust
public static readonly BindableProperty IllustItemProperty = BindableProperty.Create(
nameof(IllustItem), typeof(IllustItem), typeof(ViewIllustPage));
public ImageSource IsFavorite => (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<IllustDetailItem>(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<Permissions.Photos>();
if (status != PermissionStatus.Granted)
var saveOriginal = ResourceHelper.SaveOriginal;
var result = await DisplayActionSheet(
IllustItem.Title,
ResourceHelper.Cancel,
saveOriginal);
if (result == saveOriginal)
{
status = await Permissions.RequestAsync<Permissions.Photos>();
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<Permissions.Photos>();
if (status != PermissionStatus.Granted)
{
App.DebugPrint("access denied to gallery.");
status = await Permissions.RequestAsync<Permissions.Photos>();
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; }
}

View File

@ -2,6 +2,9 @@
<root>
<Title>Pixiview</Title>
<Ok>OK</Ok>
<Cancel>取消</Cancel>
<Yes></Yes>
<No></No>
<R18>R-18</R18>
<Follow>已关注</Follow>
<Recommends>推荐</Recommends>
@ -11,5 +14,8 @@
<Favorites>收藏夹</Favorites>
<Option>选项</Option>
<Preview>预览</Preview>
<Operation>操作</Operation>
<SaveOriginal>保存原图</SaveOriginal>
<SaveSuccess>成功保存图片到照片库。</SaveSuccess>
<AlreadySavedQuestion>原图已保存,是否继续?</AlreadySavedQuestion>
</root>

View File

@ -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<string, LanguageResource> dict = new Dictionary<string, LanguageResource>();

View File

@ -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()
{

View File

@ -1,7 +1,6 @@
using System;
using System.Globalization;
using Pixiview.UI;
using Pixiview.UI.Theme;
using Xamarin.Forms;
namespace Pixiview.Utils

View File

@ -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
}
}

View File

@ -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")
{
}
}
}

View File

@ -182,7 +182,10 @@ namespace Pixiview.Utils
public static IllustFavorite LoadFavoritesIllusts()
{
var file = Path.Combine(PersonalFolder, favoriteFile);
return ReadObject<IllustFavorite>(file);
lock (sync)
{
return ReadObject<IllustFavorite>(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));