feature: login support for Android

feature: display profile information
This commit is contained in:
2020-05-18 13:29:51 +08:00
parent a13c1deca6
commit d743e5e393
25 changed files with 473 additions and 85 deletions

View File

@ -80,6 +80,7 @@
<Compile Include="SplashActivity.cs" /> <Compile Include="SplashActivity.cs" />
<Compile Include="Renderers\RoundImageRenderer.cs" /> <Compile Include="Renderers\RoundImageRenderer.cs" />
<Compile Include="Renderers\AdaptedPageRenderer.cs" /> <Compile Include="Renderers\AdaptedPageRenderer.cs" />
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Resources\AboutResources.txt" /> <None Include="Resources\AboutResources.txt" />
@ -95,10 +96,6 @@
<SubType></SubType> <SubType></SubType>
<Generator></Generator> <Generator></Generator>
</AndroidResource> </AndroidResource>
<AndroidResource Include="Resources\drawable\userprofile.jpg">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
<AndroidResource Include="Resources\mipmap-xxxhdpi\icon_foreground.png"> <AndroidResource Include="Resources\mipmap-xxxhdpi\icon_foreground.png">
<SubType></SubType> <SubType></SubType>
<Generator></Generator> <Generator></Generator>
@ -207,6 +204,10 @@
<SubType></SubType> <SubType></SubType>
<Generator></Generator> <Generator></Generator>
</AndroidResource> </AndroidResource>
<AndroidResource Include="Resources\drawable\no_profile.png">
<SubType></SubType>
<Generator></Generator>
</AndroidResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Resources\drawable\" /> <Folder Include="Resources\drawable\" />

View File

@ -0,0 +1,117 @@
using Android.Content;
using Android.Webkit;
using Pixiview.Droid.Renderers;
using Pixiview.Login;
using Pixiview.Utils;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace Pixiview.Droid.Renderers
{
public class HybridWebViewRenderer : WebViewRenderer
{
public HybridWebViewRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement is HybridWebView webView)
{
Control.SetWebViewClient(new PixivWebViewClient(this));
Control.LoadUrl(webView.Uri);
}
}
private class PixivWebViewClient : FormsWebViewClient
{
private readonly HybridWebView webView;
public PixivWebViewClient(HybridWebViewRenderer renderer) : base(renderer)
{
if (renderer.Element is HybridWebView view)
{
webView = view;
}
}
//public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
//{
// var uri = request.Url;
// if (uri.Host == "www.pixiv.net" && uri.Path == "/")
// {
// var client = new HttpClient(new HttpClientHandler { UseCookies = false })
// {
// BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}"),
// Timeout = TimeSpan.FromSeconds(30)
// };
// var pathAndQuery = uri.Path;
// if (!string.IsNullOrEmpty(uri.Query))
// {
// pathAndQuery += "?" + uri.EncodedQuery;
// }
// using (var req = new HttpRequestMessage(
// request.Method == "POST" ? HttpMethod.Post : HttpMethod.Get,
// pathAndQuery))
// {
// var headers = req.Headers;
// if (request.RequestHeaders != null)
// {
// foreach (var h in request.RequestHeaders)
// {
// headers.Add(h.Key, h.Value);
// }
// }
// using (var response = client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead).Result)
// {
// if (response.Headers.TryGetValues("x-userid", out var vals))
// {
// Configs.SetUserId(string.Join(';', vals), true);
// }
// }
// }
// return true;
// }
// return base.ShouldOverrideUrlLoading(view, request);
//}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
if (url == "https://www.pixiv.net/" ||
url == "https://www.pixiv.net/en/")
{
var cookieManager = CookieManager.Instance;
var cookie = cookieManager.GetCookie(url);
Configs.SetCookie(cookie, true);
// user-id
if (cookie != null)
{
var index = cookie.IndexOf("PHPSESSID=");
if (index >= 0)
{
var session = cookie.Substring(index + 10);
index = session.IndexOf('_');
if (index > 0)
{
session = session.Substring(0, index);
Configs.SetUserId(session, true);
}
}
}
if (webView != null)
{
webView.OnLoginHandle();
}
}
}
}
}
}

View File

@ -8337,65 +8337,65 @@ namespace Pixiview.Droid
// aapt resource value: 0x7F070069 // aapt resource value: 0x7F070069
public const int navigation_empty_icon = 2131165289; public const int navigation_empty_icon = 2131165289;
// aapt resource value: 0x7F07006A
public const int notification_action_background = 2131165290;
// aapt resource value: 0x7F07006B // aapt resource value: 0x7F07006B
public const int notification_bg = 2131165291; public const int notification_action_background = 2131165291;
// aapt resource value: 0x7F07006C // aapt resource value: 0x7F07006C
public const int notification_bg_low = 2131165292; public const int notification_bg = 2131165292;
// aapt resource value: 0x7F07006D // aapt resource value: 0x7F07006D
public const int notification_bg_low_normal = 2131165293; public const int notification_bg_low = 2131165293;
// aapt resource value: 0x7F07006E // aapt resource value: 0x7F07006E
public const int notification_bg_low_pressed = 2131165294; public const int notification_bg_low_normal = 2131165294;
// aapt resource value: 0x7F07006F // aapt resource value: 0x7F07006F
public const int notification_bg_normal = 2131165295; public const int notification_bg_low_pressed = 2131165295;
// aapt resource value: 0x7F070070 // aapt resource value: 0x7F070070
public const int notification_bg_normal_pressed = 2131165296; public const int notification_bg_normal = 2131165296;
// aapt resource value: 0x7F070071 // aapt resource value: 0x7F070071
public const int notification_icon_background = 2131165297; public const int notification_bg_normal_pressed = 2131165297;
// aapt resource value: 0x7F070072 // aapt resource value: 0x7F070072
public const int notification_template_icon_bg = 2131165298; public const int notification_icon_background = 2131165298;
// aapt resource value: 0x7F070073 // aapt resource value: 0x7F070073
public const int notification_template_icon_low_bg = 2131165299; public const int notification_template_icon_bg = 2131165299;
// aapt resource value: 0x7F070074 // aapt resource value: 0x7F070074
public const int notification_tile_bg = 2131165300; public const int notification_template_icon_low_bg = 2131165300;
// aapt resource value: 0x7F070075 // aapt resource value: 0x7F070075
public const int notify_panel_notification_icon_bg = 2131165301; public const int notification_tile_bg = 2131165301;
// aapt resource value: 0x7F070076 // aapt resource value: 0x7F070076
public const int segmented_control_background = 2131165302; public const int notify_panel_notification_icon_bg = 2131165302;
// aapt resource value: 0x7F07006A
public const int no_profile = 2131165290;
// aapt resource value: 0x7F070077 // aapt resource value: 0x7F070077
public const int segmented_control_first_background = 2131165303; public const int segmented_control_background = 2131165303;
// aapt resource value: 0x7F070078 // aapt resource value: 0x7F070078
public const int segmented_control_last_background = 2131165304; public const int segmented_control_first_background = 2131165304;
// aapt resource value: 0x7F070079 // aapt resource value: 0x7F070079
public const int splash_logo = 2131165305; public const int segmented_control_last_background = 2131165305;
// aapt resource value: 0x7F07007A // aapt resource value: 0x7F07007A
public const int splash_screen = 2131165306; public const int splash_logo = 2131165306;
// aapt resource value: 0x7F07007B // aapt resource value: 0x7F07007B
public const int tooltip_frame_dark = 2131165307; public const int splash_screen = 2131165307;
// aapt resource value: 0x7F07007C // aapt resource value: 0x7F07007C
public const int tooltip_frame_light = 2131165308; public const int tooltip_frame_dark = 2131165308;
// aapt resource value: 0x7F07007D // aapt resource value: 0x7F07007D
public const int userprofile = 2131165309; public const int tooltip_frame_light = 2131165309;
static Drawable() static Drawable()
{ {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -167,7 +167,7 @@
<BundleResource Include="Resources\fa-light-300.ttf" /> <BundleResource Include="Resources\fa-light-300.ttf" />
<BundleResource Include="Resources\fa-regular-400.ttf" /> <BundleResource Include="Resources\fa-regular-400.ttf" />
<BundleResource Include="Resources\fa-solid-900.ttf" /> <BundleResource Include="Resources\fa-solid-900.ttf" />
<BundleResource Include="Resources\userprofile.jpg" />
<BundleResource Include="Resources\download.png" /> <BundleResource Include="Resources\download.png" />
<BundleResource Include="Resources\no_profile.png" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -64,7 +64,7 @@ namespace Pixiview.iOS.Renderers
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation) public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
{ {
var url = webView.Url; var url = webView.Url;
if (url.Host == "www.pixiv.net" && url.Path == "/") if (url.Host == "www.pixiv.net" && (url.Path == "/" || url.Path == "/en/"))
{ {
var store = webView.Configuration.WebsiteDataStore.HttpCookieStore; var store = webView.Configuration.WebsiteDataStore.HttpCookieStore;
var result = await Configs.RequestCookieContainer(store); var result = await Configs.RequestCookieContainer(store);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

View File

@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Pixiview.Illust; using Pixiview.Illust;
using Pixiview.Resources; using Pixiview.Resources;
using Pixiview.UI.Theme; using Pixiview.UI.Theme;
@ -34,6 +35,9 @@ namespace Pixiview
Configs.SetCookie(Preferences.Get(Configs.CookieKey, null)); Configs.SetCookie(Preferences.Get(Configs.CookieKey, null));
Configs.SetUserId(Preferences.Get(Configs.UserIdKey, null)); Configs.SetUserId(Preferences.Get(Configs.UserIdKey, null));
// login info
Task.Run(() => AppShell.Current.DoLoginInformation());
Configs.IsOnR18 = Preferences.Get(Configs.IsOnR18Key, false); Configs.IsOnR18 = Preferences.Get(Configs.IsOnR18Key, false);
var isProxied = Preferences.Get(Configs.IsProxiedKey, false); var isProxied = Preferences.Get(Configs.IsProxiedKey, false);
if (isProxied) if (isProxied)

View File

@ -20,16 +20,16 @@
</Grid.GestureRecognizers> </Grid.GestureRecognizers>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="80"/> <RowDefinition Height="80"/>
<RowDefinition Height="25"/> <RowDefinition Height="30"/>
<RowDefinition Height="20"/> <RowDefinition Height="20"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<u:CircleImage Aspect="AspectFill" Source="userprofile.jpg" <u:CircleImage Aspect="AspectFill" Source="{Binding UserProfileImage}"
HeightRequest="60" WidthRequest="60" HeightRequest="60" WidthRequest="60"
VerticalOptions="End" HorizontalOptions="Start"/> VerticalOptions="End" HorizontalOptions="Start"/>
<Label Grid.Row="1" VerticalOptions="End" FontAttributes="Bold" <Label Grid.Row="1" VerticalOptions="End" FontAttributes="Bold"
Text="Tsanie" TextColor="{DynamicResource TextColor}"/> Text="{Binding UserProfileName}" TextColor="{DynamicResource TextColor}"/>
<Label Grid.Row="2" VerticalOptions="Center" FontSize="Small" <Label Grid.Row="2" VerticalOptions="Center" FontSize="Small"
Text="@tsanie" TextColor="{DynamicResource SubTextColor}"/> Text="{Binding UserProfileId}" TextColor="{DynamicResource SubTextColor}"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</Shell.FlyoutHeaderTemplate> </Shell.FlyoutHeaderTemplate>

View File

@ -1,5 +1,8 @@
using System; using System;
using System.Threading.Tasks;
using Pixiview.Login; using Pixiview.Login;
using Pixiview.Resources;
using Pixiview.UI;
using Pixiview.Utils; using Pixiview.Utils;
using Xamarin.Essentials; using Xamarin.Essentials;
using Xamarin.Forms; using Xamarin.Forms;
@ -14,11 +17,35 @@ namespace Pixiview
public static Thickness HalfNavigationBarOffset { get; private set; } public static Thickness HalfNavigationBarOffset { get; private set; }
public static Thickness TotalBarOffset { get; private set; } public static Thickness TotalBarOffset { get; private set; }
public static readonly BindableProperty UserProfileImageProperty = BindableProperty.Create(
nameof(UserProfileImage), typeof(ImageSource), typeof(AppShell), StyleDefinition.ProfileNone);
public static readonly BindableProperty UserProfileNameProperty = BindableProperty.Create(
nameof(UserProfileName), typeof(string), typeof(AppShell), ResourceHelper.Guest);
public static readonly BindableProperty UserProfileIdProperty = BindableProperty.Create(
nameof(UserProfileId), typeof(string), typeof(AppShell));
public ImageSource UserProfileImage
{
get => (ImageSource)GetValue(UserProfileImageProperty);
set => SetValue(UserProfileImageProperty, value);
}
public string UserProfileName
{
get => (string)GetValue(UserProfileNameProperty);
set => SetValue(UserProfileNameProperty, value);
}
public string UserProfileId
{
get => (string)GetValue(UserProfileIdProperty);
set => SetValue(UserProfileIdProperty, value);
}
public event EventHandler<BarHeightEventArgs> NavigationBarHeightChanged; public event EventHandler<BarHeightEventArgs> NavigationBarHeightChanged;
public event EventHandler<BarHeightEventArgs> StatusBarHeightChanged; public event EventHandler<BarHeightEventArgs> StatusBarHeightChanged;
public AppShell() public AppShell()
{ {
BindingContext = this;
InitializeComponent(); InitializeComponent();
App.DebugPrint($"folder: {Stores.PersonalFolder}"); App.DebugPrint($"folder: {Stores.PersonalFolder}");
@ -47,7 +74,14 @@ namespace Pixiview
private void TapGestureRecognizer_Tapped(object sender, EventArgs e) private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{ {
PushToLogin(null); if (UserProfileId != null)
{
return;
}
PushToLogin(()=>
{
Task.Run(() => DoLoginInformation(true));
});
} }
private bool isLoginOpened; private bool isLoginOpened;
@ -73,6 +107,38 @@ namespace Pixiview
await Navigation.PushModalAsync(loginPage); await Navigation.PushModalAsync(loginPage);
}); });
} }
public void DoLoginInformation(bool force = false)
{
string name = null;
string userId = null;
string img = null;
if (!force)
{
name = Preferences.Get(Configs.ProfileNameKey, null);
userId = Preferences.Get(Configs.ProfileIdKey, null);
img = Preferences.Get(Configs.ProfileImageKey, null);
}
if (name == null || userId == null)
{
var global = Stores.LoadGlobalData(force);
if (global == null || global.userData == null)
{
App.DebugError("login.info", "user data is null");
return;
}
name = global.userData.name;
userId = global.userData.pixivId;
img = global.userData.profileImg;
Preferences.Set(Configs.ProfileNameKey, name);
Preferences.Set(Configs.ProfileIdKey, userId);
Preferences.Set(Configs.ProfileImageKey, img);
}
UserProfileName = name;
UserProfileId = $"@{userId}";
UserProfileImage = Stores.LoadUserProfileImage(img);
}
} }
public class BarHeightEventArgs : EventArgs public class BarHeightEventArgs : EventArgs

View File

@ -73,9 +73,11 @@ namespace Pixiview.Illust
protected readonly Command<IllustItem> commandIllustImageTapped; protected readonly Command<IllustItem> commandIllustImageTapped;
protected DateTime lastUpdated; protected DateTime lastUpdated;
protected double topOffset; protected double topOffset;
protected string lastError;
private T illustData; private T illustData;
private ParallelTask task; private ParallelTask task;
private bool isTrying;
public IllustCollectionPage() public IllustCollectionPage()
{ {
@ -530,15 +532,31 @@ namespace Pixiview.Illust
illustData = DoLoadIllustData(force); illustData = DoLoadIllustData(force);
if (illustData == null) if (illustData == null)
{ {
if (isTrying)
{
MainThread.BeginInvokeOnMainThread(() =>
{
DisplayAlert(ResourceHelper.Title,
ResourceHelper.FailedResponse + "\n" + lastError?.Substring(0, 40),
ResourceHelper.Ok);
});
isTrying = false;
}
else
{
isTrying = true;
AppShell.Current.PushToLogin(() => AppShell.Current.PushToLogin(() =>
{ {
Task.Run(() => AppShell.Current.DoLoginInformation(true));
StartLoad(true); StartLoad(true);
}); });
}
//App.DebugError("illusts.load", "failed to load illusts data."); //App.DebugError("illusts.load", "failed to load illusts data.");
IsLoading = false; IsLoading = false;
IsBottomLoading = false; IsBottomLoading = false;
return; return;
} }
isTrying = false;
if (force && IsFavoriteVisible) if (force && IsFavoriteVisible)
{ {
var now = DateTime.Now; var now = DateTime.Now;

View File

@ -160,7 +160,7 @@ namespace Pixiview.Illust
protected override IllustRankingData DoLoadIllustData(bool force) protected override IllustRankingData DoLoadIllustData(bool force)
{ {
var data = Stores.LoadIllustRankingData(lastQueryKey, queryDate, currentPage, force); var data = Stores.LoadIllustRankingData(lastQueryKey, queryDate, currentPage, out lastError, force);
if (data != null) if (data != null)
{ {
if (int.TryParse(data.next, out int next)) if (int.TryParse(data.next, out int next))

View File

@ -331,16 +331,23 @@ namespace Pixiview.Illust
} }
Illusts = items; Illusts = items;
Task.Run(DoLoadImages); Task.Run(() => DoLoadImages());
} }
private void DoLoadImages() private void DoLoadImages(bool force = false)
{ {
var illustItem = IllustItem; var illustItem = IllustItem;
var pages = Stores.LoadIllustPageData(illustItem.Id); var pages = Stores.LoadIllustPageData(illustItem.Id, out string error, force);
if (pages == null) if (pages == null)
{ {
App.DebugError("illustPage.load", $"failed to load illust page data, id: {illustItem.Id}"); App.DebugError("illustPage.load", $"failed to load illust page data, id: {illustItem.Id}");
if (error != null)
{
MainThread.BeginInvokeOnMainThread(() =>
{
DisplayAlert(ResourceHelper.Title, error, ResourceHelper.Ok);
});
}
return; return;
} }
var items = Illusts; var items = Illusts;
@ -385,9 +392,13 @@ namespace Pixiview.Illust
var count = illustItem.PageCount; var count = illustItem.PageCount;
pageCount = count; pageCount = count;
Title = illustItem.Title; Title = illustItem.Title;
IsPageVisible = count > 1;
IsAnimateSliderVisible = illustItem.IsAnimeVisible; IsAnimateSliderVisible = illustItem.IsAnimeVisible;
ProgressVisible = count > 1; if (count > 1)
{
IsPageVisible = true;
ProgressVisible = true;
PagePositionText = $"1/{count}";
}
}); });
if (preload.user.TryGetValue(illust.userId, out var user)) if (preload.user.TryGetValue(illust.userId, out var user))
{ {
@ -423,6 +434,12 @@ namespace Pixiview.Illust
private void DoLoadImage(int index, bool force = false) private void DoLoadImage(int index, bool force = false)
{ {
var items = Illusts; var items = Illusts;
if (index == 0 && force)
{
// error, refresh all
Task.Run(() => DoLoadImages(true));
return;
}
if (index < 0 || index >= items.Length) if (index < 0 || index >= items.Length)
{ {
App.DebugPrint($"invalid index: {index}"); App.DebugPrint($"invalid index: {index}");

View File

@ -1,4 +1,5 @@
using System; using System;
using Xamarin.Essentials;
using Xamarin.Forms; using Xamarin.Forms;
namespace Pixiview.Login namespace Pixiview.Login
@ -29,7 +30,10 @@ namespace Pixiview.Login
public void OnLoginHandle() public void OnLoginHandle()
{ {
LoginHandle?.Invoke(this, EventArgs.Empty); if (LoginHandle != null)
{
MainThread.BeginInvokeOnMainThread(() => LoginHandle(this, EventArgs.Empty));
}
} }
} }
} }

View File

@ -5,6 +5,10 @@
xmlns:r="clr-namespace:Pixiview.Resources" xmlns:r="clr-namespace:Pixiview.Resources"
x:Class="Pixiview.OptionPage" x:Class="Pixiview.OptionPage"
Title="{r:Text Option}"> Title="{r:Text Option}">
<ContentPage.ToolbarItems>
<ToolbarItem Order="Primary" Clicked="ShareCookie_Clicked"
IconImageSource="{DynamicResource FontIconShare}"/>
</ContentPage.ToolbarItems>
<TableView Intent="Settings" VerticalOptions="Start" <TableView Intent="Settings" VerticalOptions="Start"
BackgroundColor="{DynamicResource OptionBackColor}"> BackgroundColor="{DynamicResource OptionBackColor}">
<TableRoot> <TableRoot>
@ -24,6 +28,11 @@
Text="{Binding Port, Mode=TwoWay}" Text="{Binding Port, Mode=TwoWay}"
Keyboard="Numeric" Placeholder="8080"/> Keyboard="Numeric" Placeholder="8080"/>
</TableSection> </TableSection>
<TableSection Title="{r:Text Privacy}">
<u:OptionEntryCell Title="{r:Text Cookie}"
Text="{Binding Cookie, Mode=TwoWay}"
Keyboard="Text"/>
</TableSection>
</TableRoot> </TableRoot>
</TableView> </TableView>
</u:AdaptedPage> </u:AdaptedPage>

View File

@ -1,4 +1,5 @@
using Pixiview.UI; using System;
using Pixiview.UI;
using Pixiview.Utils; using Pixiview.Utils;
using Xamarin.Essentials; using Xamarin.Essentials;
using Xamarin.Forms; using Xamarin.Forms;
@ -15,6 +16,8 @@ namespace Pixiview
nameof(Host), typeof(string), typeof(OptionPage)); nameof(Host), typeof(string), typeof(OptionPage));
public static readonly BindableProperty PortProperty = BindableProperty.Create( public static readonly BindableProperty PortProperty = BindableProperty.Create(
nameof(Port), typeof(string), typeof(OptionPage)); nameof(Port), typeof(string), typeof(OptionPage));
public static readonly BindableProperty CookieProperty = BindableProperty.Create(
nameof(Cookie), typeof(string), typeof(OptionPage));
public bool IsOnR18 public bool IsOnR18
{ {
@ -36,6 +39,11 @@ namespace Pixiview
get => (string)GetValue(PortProperty); get => (string)GetValue(PortProperty);
set => SetValue(PortProperty, value); set => SetValue(PortProperty, value);
} }
public string Cookie
{
get => (string)GetValue(CookieProperty);
set => SetValue(CookieProperty, value);
}
public OptionPage() public OptionPage()
{ {
@ -47,14 +55,22 @@ namespace Pixiview
{ {
base.OnAppearing(); base.OnAppearing();
IsOnR18 = Preferences.Get(Configs.IsOnR18Key, false); IsOnR18 = Configs.IsOnR18;
IsProxied = Preferences.Get(Configs.IsProxiedKey, false); var proxy = Configs.Proxy;
Host = Preferences.Get(Configs.HostKey, string.Empty); if (proxy != null)
int pt = Preferences.Get(Configs.PortKey, 0);
if (pt > 0)
{ {
Port = pt.ToString(); IsProxied = true;
Host = proxy.Address.Host;
Port = proxy.Address.Port.ToString();
} }
else
{
IsProxied = false;
Host = string.Empty;
Port = string.Empty;
}
Cookie = Configs.Cookie;
} }
protected override void OnDisappearing() protected override void OnDisappearing()
@ -63,30 +79,70 @@ namespace Pixiview
var r18 = IsOnR18; var r18 = IsOnR18;
var proxied = IsProxied; var proxied = IsProxied;
var h = Host?.Trim();
if (Configs.IsOnR18 != r18)
{
Preferences.Set(Configs.IsOnR18Key, r18); Preferences.Set(Configs.IsOnR18Key, r18);
Configs.IsOnR18 = r18; Configs.IsOnR18 = r18;
App.DebugPrint($"r-18 filter: {r18}"); App.DebugPrint($"r-18 filter: {r18}");
}
Preferences.Set(Configs.IsProxiedKey, proxied); var proxy = Configs.Proxy;
var h = Host?.Trim();
_ = int.TryParse(Port, out int pt);
if (proxied)
{
if (proxy == null ||
proxy.Address.Host != h ||
proxy.Address.Port != pt)
{
Preferences.Set(Configs.IsProxiedKey, true);
Preferences.Set(Configs.HostKey, h); Preferences.Set(Configs.HostKey, h);
var p = Port; if (pt > 0)
if (int.TryParse(p, out int pt) && pt > 0)
{ {
Preferences.Set(Configs.PortKey, pt); Preferences.Set(Configs.PortKey, pt);
} }
if (!string.IsNullOrEmpty(h) && pt > 0)
if (proxied && !string.IsNullOrEmpty(h) && pt > 0)
{ {
Configs.Proxy = new System.Net.WebProxy(h, pt); Configs.Proxy = new System.Net.WebProxy(h, pt);
App.DebugPrint($"set proxy to: {h}:{pt}"); App.DebugPrint($"set proxy to: {h}:{pt}");
} }
else }
}
else if (proxy != null)
{ {
Preferences.Set(Configs.HostKey, false);
if (pt > 0)
{
Preferences.Set(Configs.PortKey, pt);
}
Configs.Proxy = null; Configs.Proxy = null;
App.DebugPrint($"clear proxy"); }
var cookie = Cookie;
if (Configs.Cookie != cookie)
{
Configs.SetCookie(cookie, true);
var index = cookie.IndexOf("PHPSESSID=");
if (index >= 0)
{
var session = cookie.Substring(index + 10);
index = session.IndexOf('_');
if (index > 0)
{
session = session.Substring(0, index);
Configs.SetUserId(session, true);
} }
} }
} }
} }
private async void ShareCookie_Clicked(object sender, EventArgs e)
{
await Share.RequestAsync(new ShareTextRequest
{
Text = Cookie
});
}
}
}

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<root> <root>
<Title>Pixiview</Title> <Title>Pixiview</Title>
<Guest>游客</Guest>
<Ok>OK</Ok> <Ok>OK</Ok>
<Cancel>取消</Cancel> <Cancel>取消</Cancel>
<Yes></Yes> <Yes></Yes>
@ -12,6 +13,8 @@
<Enabled>启用</Enabled> <Enabled>启用</Enabled>
<Host>主机</Host> <Host>主机</Host>
<Port>端口</Port> <Port>端口</Port>
<Privacy>隐私</Privacy>
<Cookie>Cookie</Cookie>
<Daily>今日</Daily> <Daily>今日</Daily>
<Weekly>本周</Weekly> <Weekly>本周</Weekly>
<Monthly>本月</Monthly> <Monthly>本月</Monthly>
@ -49,4 +52,5 @@
<CantExportVideo>无法导出视频,请先下载完成。</CantExportVideo> <CantExportVideo>无法导出视频,请先下载完成。</CantExportVideo>
<AlreadySavedVideo>视频已保存,是否继续?</AlreadySavedVideo> <AlreadySavedVideo>视频已保存,是否继续?</AlreadySavedVideo>
<ExportSuccess>视频已导出到照片库。</ExportSuccess> <ExportSuccess>视频已导出到照片库。</ExportSuccess>
<FailedResponse>无法获取返回结果。</FailedResponse>
</root> </root>

View File

@ -11,6 +11,7 @@ namespace Pixiview.Resources
public class ResourceHelper public class ResourceHelper
{ {
public static string Title => GetResource(nameof(Title)); public static string Title => GetResource(nameof(Title));
public static string Guest => GetResource(nameof(Guest));
public static string Ok => GetResource(nameof(Ok)); public static string Ok => GetResource(nameof(Ok));
public static string Cancel => GetResource(nameof(Cancel)); public static string Cancel => GetResource(nameof(Cancel));
public static string Yes => GetResource(nameof(Yes)); public static string Yes => GetResource(nameof(Yes));
@ -32,6 +33,7 @@ namespace Pixiview.Resources
public static string CantExportVideo => GetResource(nameof(CantExportVideo)); public static string CantExportVideo => GetResource(nameof(CantExportVideo));
public static string AlreadySavedVideo => GetResource(nameof(AlreadySavedVideo)); public static string AlreadySavedVideo => GetResource(nameof(AlreadySavedVideo));
public static string ExportSuccess => GetResource(nameof(ExportSuccess)); public static string ExportSuccess => GetResource(nameof(ExportSuccess));
public static string FailedResponse => GetResource(nameof(FailedResponse));
static readonly Dictionary<string, LanguageResource> dict = new Dictionary<string, LanguageResource>(); static readonly Dictionary<string, LanguageResource> dict = new Dictionary<string, LanguageResource>();

View File

@ -16,6 +16,7 @@ namespace Pixiview.UI
public static readonly Color ColorRedBackground = Color.FromRgb(0xfd, 0x43, 0x63); public static readonly Color ColorRedBackground = Color.FromRgb(0xfd, 0x43, 0x63);
public static readonly Color ColorDownloadBackground = Color.FromRgb(0xd7, 0xd9, 0xe0); public static readonly Color ColorDownloadBackground = Color.FromRgb(0xd7, 0xd9, 0xe0);
public static readonly ImageSource DownloadBackground = ImageSource.FromFile("download.png"); public static readonly ImageSource DownloadBackground = ImageSource.FromFile("download.png");
public static readonly ImageSource ProfileNone = ImageSource.FromFile("no_profile.png");
public static readonly double FontSizeMicro = Device.GetNamedSize(NamedSize.Micro, typeof(Label)); public static readonly double FontSizeMicro = Device.GetNamedSize(NamedSize.Micro, typeof(Label));
public static readonly double FontSizeSmall = Device.GetNamedSize(NamedSize.Small, typeof(Label)); public static readonly double FontSizeSmall = Device.GetNamedSize(NamedSize.Small, typeof(Label));

View File

@ -97,6 +97,10 @@ namespace Pixiview.Utils
{ {
public static ParallelTask Start(int from, int toExclusive, int maxCount, Predicate<int> action) public static ParallelTask Start(int from, int toExclusive, int maxCount, Predicate<int> action)
{ {
if (toExclusive <= from)
{
return null;
}
var task = new ParallelTask(from, toExclusive, maxCount, action); var task = new ParallelTask(from, toExclusive, maxCount, action);
Task.Run(task.Start); Task.Run(task.Start);
return task; return task;

View File

@ -11,7 +11,7 @@ namespace Pixiview.Utils
{ {
public class HttpUtility public class HttpUtility
{ {
public static T LoadObject<T>(string file, string url, string referer, public static T LoadObject<T>(string file, string url, string referer, out string error,
bool force = false, bool force = false,
Action<HttpRequestHeaders> header = null, Action<HttpRequestHeaders> header = null,
Func<T, string> namehandler = null, Func<T, string> namehandler = null,
@ -59,6 +59,7 @@ namespace Pixiview.Utils
}); });
if (response == null) if (response == null)
{ {
error = "response is null";
return default; return default;
} }
using (response) using (response)
@ -73,7 +74,8 @@ namespace Pixiview.Utils
} }
catch (Exception ex) catch (Exception ex)
{ {
App.DebugError("load.strea", $"failed to read stream, error: {ex.Message}"); App.DebugError("load.stream", $"failed to read stream, error: {ex.Message}");
error = ex.Message;
return default; return default;
} }
@ -89,7 +91,9 @@ namespace Pixiview.Utils
} }
catch (Exception ex) catch (Exception ex)
{ {
App.DebugError("load", $"failed to parse illust JSON object, error: {ex.Message}"); App.DebugError("load", $"failed to parse illust JSON object, content: {content}, error: {ex.Message}");
error = content;
return default;
} }
} }
@ -112,17 +116,20 @@ namespace Pixiview.Utils
if (rtn) if (rtn)
{ {
error = null;
return result; return result;
} }
} }
} }
try try
{ {
error = null;
return JsonConvert.DeserializeObject<T>(content); return JsonConvert.DeserializeObject<T>(content);
} }
catch (Exception ex) catch (Exception ex)
{ {
App.DebugError("load", $"failed to parse illust JSON object, error: {ex.Message}"); App.DebugError("load", $"failed to parse illust JSON object, content: {content}, error: {ex.Message}");
error = content;
return default; return default;
} }
} }

View File

@ -115,4 +115,24 @@ namespace Pixiview.Utils
} }
} }
} }
public class IllustGlobalData
{
public string token;
public string oneSignalAppId;
public UserData userData;
public class UserData
{
public string id;
public string pixivId;
public string name;
public string profileImg;
public string profileImgBig;
public bool premium;
public int xRestrict;
public bool adult;
public bool safeMode;
}
}
} }

View File

@ -4,7 +4,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using Pixiview.Illust; using Pixiview.Illust;
using Xamarin.Essentials; using Xamarin.Essentials;
@ -22,6 +21,7 @@ namespace Pixiview.Utils
#endif #endif
private const string favoriteFile = "favorites.json"; private const string favoriteFile = "favorites.json";
private const string globalFile = "global.json";
private const string imageFolder = "img-original"; private const string imageFolder = "img-original";
private const string previewFolder = "img-master"; private const string previewFolder = "img-master";
private const string ugoiraFolder = "img-zip-ugoira"; private const string ugoiraFolder = "img-zip-ugoira";
@ -199,6 +199,7 @@ namespace Pixiview.Utils
file, file,
Configs.UrlIllustList, Configs.UrlIllustList,
Configs.Referer, Configs.Referer,
out _,
force: force); force: force);
if (result == null || result.error) if (result == null || result.error)
{ {
@ -207,7 +208,7 @@ namespace Pixiview.Utils
return result; return result;
} }
public static IllustRankingData LoadIllustRankingData(string mode, string date, int page, bool force = false) public static IllustRankingData LoadIllustRankingData(string mode, string date, int page, out string error, bool force = false)
{ {
var file = Path.Combine(CacheFolder, mode, $"{date}_{page}.json"); var file = Path.Combine(CacheFolder, mode, $"{date}_{page}.json");
string query = $"mode={mode}"; string query = $"mode={mode}";
@ -229,6 +230,7 @@ namespace Pixiview.Utils
file, file,
string.Format(Configs.UrlIllustRanking, query), string.Format(Configs.UrlIllustRanking, query),
referer, referer,
out error,
namehandler: rst => namehandler: rst =>
{ {
return Path.Combine(CacheFolder, mode, $"{rst.date}_{page}.json"); return Path.Combine(CacheFolder, mode, $"{rst.date}_{page}.json");
@ -252,6 +254,7 @@ namespace Pixiview.Utils
file, file,
string.Format(Configs.UrlIllustRecommendsInit, id), string.Format(Configs.UrlIllustRecommendsInit, id),
string.Format(Configs.RefererIllust, id), string.Format(Configs.RefererIllust, id),
out _,
force: force); force: force);
if (result == null || result.error) if (result == null || result.error)
{ {
@ -270,7 +273,8 @@ namespace Pixiview.Utils
var result = HttpUtility.LoadObject<IllustRecommendsData>( var result = HttpUtility.LoadObject<IllustRecommendsData>(
null, null,
string.Format(Configs.UrlIllustRecommendsList, ps), string.Format(Configs.UrlIllustRecommendsList, ps),
string.Format(Configs.RefererIllust, id)); string.Format(Configs.RefererIllust, id),
out _);
if (result == null || result.error) if (result == null || result.error)
{ {
App.DebugPrint($"error when load recommends list data: {result?.message}"); App.DebugPrint($"error when load recommends list data: {result?.message}");
@ -278,6 +282,37 @@ namespace Pixiview.Utils
return result; return result;
} }
public static IllustGlobalData LoadGlobalData(bool force = false)
{
var file = Path.Combine(PersonalFolder, globalFile);
var result = HttpUtility.LoadObject<IllustGlobalData>(
file,
Configs.Prefix,
null,
out _,
force: force,
header: h => { },
action: content =>
{
var index = content.IndexOf(Configs.SuffixGlobal);
if (index > 0)
{
index += Configs.SuffixGlobalLength;
var end = content.IndexOf('\'', index);
if (end > index)
{
content = content.Substring(index, end - index);
}
}
return content;
});
if (result == null)
{
App.DebugPrint($"error when load global data, is null");
}
return result;
}
public static IllustPreloadBody LoadIllustPreloadData(string id, bool force = false) public static IllustPreloadBody LoadIllustPreloadData(string id, bool force = false)
{ {
var file = Path.Combine(CacheFolder, preloadsFolder, $"{id}.json"); var file = Path.Combine(CacheFolder, preloadsFolder, $"{id}.json");
@ -285,6 +320,7 @@ namespace Pixiview.Utils
file, file,
string.Format(Configs.UrlIllust, id), string.Format(Configs.UrlIllust, id),
null, null,
out _,
force: force, force: force,
action: content => action: content =>
{ {
@ -307,18 +343,22 @@ namespace Pixiview.Utils
return result; return result;
} }
public static IllustPageData LoadIllustPageData(string id, bool force = false) public static IllustPageData LoadIllustPageData(string id, out string error, bool force = false)
{ {
var file = Path.Combine(CacheFolder, pagesFolder, $"{id}.json"); var file = Path.Combine(CacheFolder, pagesFolder, $"{id}.json");
var result = HttpUtility.LoadObject<IllustPageData>( var result = HttpUtility.LoadObject<IllustPageData>(
file, file,
string.Format(Configs.UrlIllustPage, id), string.Format(Configs.UrlIllustPage, id),
string.Format(Configs.RefererIllust, id), string.Format(Configs.RefererIllust, id),
out _,
force: force); force: force);
if (result == null || result.error) if (result == null || result.error)
{ {
App.DebugPrint($"error when load page data: {result?.message}, force({force})"); error = result?.message ?? "result is null";
App.DebugPrint($"error when load page data: {error}, force({force})");
return null;
} }
error = null;
return result; return result;
} }
@ -329,6 +369,7 @@ namespace Pixiview.Utils
file, file,
string.Format(Configs.UrlIllustUgoira, id), string.Format(Configs.UrlIllustUgoira, id),
string.Format(Configs.RefererIllust, id), string.Format(Configs.RefererIllust, id),
out _,
force: force); force: force);
if (result == null || result.error) if (result == null || result.error)
{ {
@ -342,7 +383,8 @@ namespace Pixiview.Utils
var list = HttpUtility.LoadObject<IllustUserListData>( var list = HttpUtility.LoadObject<IllustUserListData>(
null, null,
string.Format(Configs.UrlIllustUserAll, userId), string.Format(Configs.UrlIllustUserAll, userId),
string.Format(Configs.RefererIllustUser, userId)); string.Format(Configs.RefererIllustUser, userId),
out _);
if (list == null || list.error) if (list == null || list.error)
{ {
App.DebugPrint($"error when load user data: {list?.message}"); App.DebugPrint($"error when load user data: {list?.message}");
@ -360,7 +402,8 @@ namespace Pixiview.Utils
var result = HttpUtility.LoadObject<IllustUserData>( var result = HttpUtility.LoadObject<IllustUserData>(
null, null,
string.Format(Configs.UrlIllustUserArtworks, userId, ps, firstPage ? 1 : 0), string.Format(Configs.UrlIllustUserArtworks, userId, ps, firstPage ? 1 : 0),
string.Format(Configs.RefererIllustUser, userId)); string.Format(Configs.RefererIllustUser, userId),
out _);
if (result == null || result.error) if (result == null || result.error)
{ {
App.DebugPrint($"error when load user illust data: {result?.message}"); App.DebugPrint($"error when load user illust data: {result?.message}");
@ -471,6 +514,9 @@ namespace Pixiview.Utils
public static class Configs public static class Configs
{ {
public const string ProfileNameKey = "name";
public const string ProfileIdKey = "pixiv_id";
public const string ProfileImageKey = "profile_img";
public const string CookieKey = "cookies"; public const string CookieKey = "cookies";
public const string UserIdKey = "user_id"; public const string UserIdKey = "user_id";
public const string IsOnR18Key = "is_on_r18"; public const string IsOnR18Key = "is_on_r18";
@ -490,7 +536,7 @@ namespace Pixiview.Utils
public static bool IsOnR18; public static bool IsOnR18;
public static WebProxy Proxy; public static WebProxy Proxy;
private static string Prefix => Proxy == null ? public static string Prefix => Proxy == null ?
"https://hk.tsanie.us/reverse/" : "https://hk.tsanie.us/reverse/" :
"https://www.pixiv.net/"; "https://www.pixiv.net/";
public static string UserId { get; private set; } public static string UserId { get; private set; }
@ -513,15 +559,27 @@ namespace Pixiview.Utils
} }
} }
public static void SetCookie(string cookie) public static void SetCookie(string cookie, bool save = false)
{ {
Cookie = cookie; Cookie = cookie;
if (!save)
{
return;
}
if (cookie == null)
{
Preferences.Remove(CookieKey);
}
else
{
Preferences.Set(CookieKey, cookie);
}
} }
#if __IOS__ #if __IOS__
public static Task<bool> RequestCookieContainer(WebKit.WKHttpCookieStore cookieStore) public static System.Threading.Tasks.Task<bool> RequestCookieContainer(WebKit.WKHttpCookieStore cookieStore)
{ {
var task = new TaskCompletionSource<bool>(); var task = new System.Threading.Tasks.TaskCompletionSource<bool>();
cookieStore.GetAllCookies(cookies => cookieStore.GetAllCookies(cookies =>
{ {
var list = new List<string>(); var list = new List<string>();
@ -549,10 +607,10 @@ namespace Pixiview.Utils
}); });
return task.Task; return task.Task;
} }
#elif __ANDROID__
task.SetResult(false);
#endif #endif
public const string SuffixGlobal = " id=\"meta-global-data\" content='";
public const int SuffixGlobalLength = 32;
public const string SuffixPreload = " id=\"meta-preload-data\" content='"; public const string SuffixPreload = " id=\"meta-preload-data\" content='";
public const int SuffixPreloadLength = 33; // SuffixPreload.Length public const int SuffixPreloadLength = 33; // SuffixPreload.Length
public static string UrlIllustList => Prefix + "ajax/top/illust?mode=all&lang=zh"; public static string UrlIllustList => Prefix + "ajax/top/illust?mode=all&lang=zh";