feature: login support for Android
feature: display profile information
This commit is contained in:
parent
a13c1deca6
commit
d743e5e393
@ -80,6 +80,7 @@
|
||||
<Compile Include="SplashActivity.cs" />
|
||||
<Compile Include="Renderers\RoundImageRenderer.cs" />
|
||||
<Compile Include="Renderers\AdaptedPageRenderer.cs" />
|
||||
<Compile Include="Renderers\HybridWebViewRenderer.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Resources\AboutResources.txt" />
|
||||
@ -95,10 +96,6 @@
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\drawable\userprofile.jpg">
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\icon_foreground.png">
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
@ -207,6 +204,10 @@
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\drawable\no_profile.png">
|
||||
<SubType></SubType>
|
||||
<Generator></Generator>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\drawable\" />
|
||||
|
117
Pixiview.Android/Renderers/HybridWebViewRenderer.cs
Normal file
117
Pixiview.Android/Renderers/HybridWebViewRenderer.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
Pixiview.Android/Resources/Resource.designer.cs
generated
44
Pixiview.Android/Resources/Resource.designer.cs
generated
@ -8337,65 +8337,65 @@ namespace Pixiview.Droid
|
||||
// aapt resource value: 0x7F070069
|
||||
public const int navigation_empty_icon = 2131165289;
|
||||
|
||||
// aapt resource value: 0x7F07006A
|
||||
public const int notification_action_background = 2131165290;
|
||||
|
||||
// aapt resource value: 0x7F07006B
|
||||
public const int notification_bg = 2131165291;
|
||||
public const int notification_action_background = 2131165291;
|
||||
|
||||
// aapt resource value: 0x7F07006C
|
||||
public const int notification_bg_low = 2131165292;
|
||||
public const int notification_bg = 2131165292;
|
||||
|
||||
// aapt resource value: 0x7F07006D
|
||||
public const int notification_bg_low_normal = 2131165293;
|
||||
public const int notification_bg_low = 2131165293;
|
||||
|
||||
// aapt resource value: 0x7F07006E
|
||||
public const int notification_bg_low_pressed = 2131165294;
|
||||
public const int notification_bg_low_normal = 2131165294;
|
||||
|
||||
// aapt resource value: 0x7F07006F
|
||||
public const int notification_bg_normal = 2131165295;
|
||||
public const int notification_bg_low_pressed = 2131165295;
|
||||
|
||||
// aapt resource value: 0x7F070070
|
||||
public const int notification_bg_normal_pressed = 2131165296;
|
||||
public const int notification_bg_normal = 2131165296;
|
||||
|
||||
// aapt resource value: 0x7F070071
|
||||
public const int notification_icon_background = 2131165297;
|
||||
public const int notification_bg_normal_pressed = 2131165297;
|
||||
|
||||
// aapt resource value: 0x7F070072
|
||||
public const int notification_template_icon_bg = 2131165298;
|
||||
public const int notification_icon_background = 2131165298;
|
||||
|
||||
// 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
|
||||
public const int notification_tile_bg = 2131165300;
|
||||
public const int notification_template_icon_low_bg = 2131165300;
|
||||
|
||||
// aapt resource value: 0x7F070075
|
||||
public const int notify_panel_notification_icon_bg = 2131165301;
|
||||
public const int notification_tile_bg = 2131165301;
|
||||
|
||||
// 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
|
||||
public const int segmented_control_first_background = 2131165303;
|
||||
public const int segmented_control_background = 2131165303;
|
||||
|
||||
// aapt resource value: 0x7F070078
|
||||
public const int segmented_control_last_background = 2131165304;
|
||||
public const int segmented_control_first_background = 2131165304;
|
||||
|
||||
// aapt resource value: 0x7F070079
|
||||
public const int splash_logo = 2131165305;
|
||||
public const int segmented_control_last_background = 2131165305;
|
||||
|
||||
// aapt resource value: 0x7F07007A
|
||||
public const int splash_screen = 2131165306;
|
||||
public const int splash_logo = 2131165306;
|
||||
|
||||
// aapt resource value: 0x7F07007B
|
||||
public const int tooltip_frame_dark = 2131165307;
|
||||
public const int splash_screen = 2131165307;
|
||||
|
||||
// aapt resource value: 0x7F07007C
|
||||
public const int tooltip_frame_light = 2131165308;
|
||||
public const int tooltip_frame_dark = 2131165308;
|
||||
|
||||
// aapt resource value: 0x7F07007D
|
||||
public const int userprofile = 2131165309;
|
||||
public const int tooltip_frame_light = 2131165309;
|
||||
|
||||
static Drawable()
|
||||
{
|
||||
|
BIN
Pixiview.Android/Resources/drawable/no_profile.png
Normal file
BIN
Pixiview.Android/Resources/drawable/no_profile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
@ -167,7 +167,7 @@
|
||||
<BundleResource Include="Resources\fa-light-300.ttf" />
|
||||
<BundleResource Include="Resources\fa-regular-400.ttf" />
|
||||
<BundleResource Include="Resources\fa-solid-900.ttf" />
|
||||
<BundleResource Include="Resources\userprofile.jpg" />
|
||||
<BundleResource Include="Resources\download.png" />
|
||||
<BundleResource Include="Resources\no_profile.png" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -64,7 +64,7 @@ namespace Pixiview.iOS.Renderers
|
||||
public override async void DidFinishNavigation(WKWebView webView, WKNavigation navigation)
|
||||
{
|
||||
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 result = await Configs.RequestCookieContainer(store);
|
||||
|
BIN
Pixiview.iOS/Resources/no_profile.png
Normal file
BIN
Pixiview.iOS/Resources/no_profile.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
@ -3,6 +3,7 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Pixiview.Illust;
|
||||
using Pixiview.Resources;
|
||||
using Pixiview.UI.Theme;
|
||||
@ -34,6 +35,9 @@ namespace Pixiview
|
||||
Configs.SetCookie(Preferences.Get(Configs.CookieKey, null));
|
||||
Configs.SetUserId(Preferences.Get(Configs.UserIdKey, null));
|
||||
|
||||
// login info
|
||||
Task.Run(() => AppShell.Current.DoLoginInformation());
|
||||
|
||||
Configs.IsOnR18 = Preferences.Get(Configs.IsOnR18Key, false);
|
||||
var isProxied = Preferences.Get(Configs.IsProxiedKey, false);
|
||||
if (isProxied)
|
||||
|
@ -20,16 +20,16 @@
|
||||
</Grid.GestureRecognizers>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="80"/>
|
||||
<RowDefinition Height="25"/>
|
||||
<RowDefinition Height="30"/>
|
||||
<RowDefinition Height="20"/>
|
||||
</Grid.RowDefinitions>
|
||||
<u:CircleImage Aspect="AspectFill" Source="userprofile.jpg"
|
||||
<u:CircleImage Aspect="AspectFill" Source="{Binding UserProfileImage}"
|
||||
HeightRequest="60" WidthRequest="60"
|
||||
VerticalOptions="End" HorizontalOptions="Start"/>
|
||||
<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"
|
||||
Text="@tsanie" TextColor="{DynamicResource SubTextColor}"/>
|
||||
Text="{Binding UserProfileId}" TextColor="{DynamicResource SubTextColor}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Shell.FlyoutHeaderTemplate>
|
||||
|
@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Pixiview.Login;
|
||||
using Pixiview.Resources;
|
||||
using Pixiview.UI;
|
||||
using Pixiview.Utils;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
@ -14,11 +17,35 @@ namespace Pixiview
|
||||
public static Thickness HalfNavigationBarOffset { 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> StatusBarHeightChanged;
|
||||
|
||||
public AppShell()
|
||||
{
|
||||
BindingContext = this;
|
||||
InitializeComponent();
|
||||
|
||||
App.DebugPrint($"folder: {Stores.PersonalFolder}");
|
||||
@ -47,7 +74,14 @@ namespace Pixiview
|
||||
|
||||
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
|
||||
{
|
||||
PushToLogin(null);
|
||||
if (UserProfileId != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
PushToLogin(()=>
|
||||
{
|
||||
Task.Run(() => DoLoginInformation(true));
|
||||
});
|
||||
}
|
||||
|
||||
private bool isLoginOpened;
|
||||
@ -73,6 +107,38 @@ namespace Pixiview
|
||||
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
|
||||
|
@ -73,9 +73,11 @@ namespace Pixiview.Illust
|
||||
protected readonly Command<IllustItem> commandIllustImageTapped;
|
||||
protected DateTime lastUpdated;
|
||||
protected double topOffset;
|
||||
protected string lastError;
|
||||
|
||||
private T illustData;
|
||||
private ParallelTask task;
|
||||
private bool isTrying;
|
||||
|
||||
public IllustCollectionPage()
|
||||
{
|
||||
@ -530,15 +532,31 @@ namespace Pixiview.Illust
|
||||
illustData = DoLoadIllustData(force);
|
||||
if (illustData == null)
|
||||
{
|
||||
AppShell.Current.PushToLogin(()=>
|
||||
if (isTrying)
|
||||
{
|
||||
StartLoad(true);
|
||||
});
|
||||
MainThread.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
DisplayAlert(ResourceHelper.Title,
|
||||
ResourceHelper.FailedResponse + "\n" + lastError?.Substring(0, 40),
|
||||
ResourceHelper.Ok);
|
||||
});
|
||||
isTrying = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isTrying = true;
|
||||
AppShell.Current.PushToLogin(() =>
|
||||
{
|
||||
Task.Run(() => AppShell.Current.DoLoginInformation(true));
|
||||
StartLoad(true);
|
||||
});
|
||||
}
|
||||
//App.DebugError("illusts.load", "failed to load illusts data.");
|
||||
IsLoading = false;
|
||||
IsBottomLoading = false;
|
||||
return;
|
||||
}
|
||||
isTrying = false;
|
||||
if (force && IsFavoriteVisible)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
|
@ -160,7 +160,7 @@ namespace Pixiview.Illust
|
||||
|
||||
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 (int.TryParse(data.next, out int next))
|
||||
|
@ -331,16 +331,23 @@ namespace Pixiview.Illust
|
||||
}
|
||||
|
||||
Illusts = items;
|
||||
Task.Run(DoLoadImages);
|
||||
Task.Run(() => DoLoadImages());
|
||||
}
|
||||
|
||||
private void DoLoadImages()
|
||||
private void DoLoadImages(bool force = false)
|
||||
{
|
||||
var illustItem = IllustItem;
|
||||
var pages = Stores.LoadIllustPageData(illustItem.Id);
|
||||
var pages = Stores.LoadIllustPageData(illustItem.Id, out string error, force);
|
||||
if (pages == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
var items = Illusts;
|
||||
@ -385,9 +392,13 @@ namespace Pixiview.Illust
|
||||
var count = illustItem.PageCount;
|
||||
pageCount = count;
|
||||
Title = illustItem.Title;
|
||||
IsPageVisible = count > 1;
|
||||
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))
|
||||
{
|
||||
@ -423,6 +434,12 @@ namespace Pixiview.Illust
|
||||
private void DoLoadImage(int index, bool force = false)
|
||||
{
|
||||
var items = Illusts;
|
||||
if (index == 0 && force)
|
||||
{
|
||||
// error, refresh all
|
||||
Task.Run(() => DoLoadImages(true));
|
||||
return;
|
||||
}
|
||||
if (index < 0 || index >= items.Length)
|
||||
{
|
||||
App.DebugPrint($"invalid index: {index}");
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Pixiview.Login
|
||||
@ -29,7 +30,10 @@ namespace Pixiview.Login
|
||||
|
||||
public void OnLoginHandle()
|
||||
{
|
||||
LoginHandle?.Invoke(this, EventArgs.Empty);
|
||||
if (LoginHandle != null)
|
||||
{
|
||||
MainThread.BeginInvokeOnMainThread(() => LoginHandle(this, EventArgs.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,10 @@
|
||||
xmlns:r="clr-namespace:Pixiview.Resources"
|
||||
x:Class="Pixiview.OptionPage"
|
||||
Title="{r:Text Option}">
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Order="Primary" Clicked="ShareCookie_Clicked"
|
||||
IconImageSource="{DynamicResource FontIconShare}"/>
|
||||
</ContentPage.ToolbarItems>
|
||||
<TableView Intent="Settings" VerticalOptions="Start"
|
||||
BackgroundColor="{DynamicResource OptionBackColor}">
|
||||
<TableRoot>
|
||||
@ -24,6 +28,11 @@
|
||||
Text="{Binding Port, Mode=TwoWay}"
|
||||
Keyboard="Numeric" Placeholder="8080"/>
|
||||
</TableSection>
|
||||
<TableSection Title="{r:Text Privacy}">
|
||||
<u:OptionEntryCell Title="{r:Text Cookie}"
|
||||
Text="{Binding Cookie, Mode=TwoWay}"
|
||||
Keyboard="Text"/>
|
||||
</TableSection>
|
||||
</TableRoot>
|
||||
</TableView>
|
||||
</u:AdaptedPage>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Pixiview.UI;
|
||||
using System;
|
||||
using Pixiview.UI;
|
||||
using Pixiview.Utils;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
@ -15,6 +16,8 @@ namespace Pixiview
|
||||
nameof(Host), typeof(string), typeof(OptionPage));
|
||||
public static readonly BindableProperty PortProperty = BindableProperty.Create(
|
||||
nameof(Port), typeof(string), typeof(OptionPage));
|
||||
public static readonly BindableProperty CookieProperty = BindableProperty.Create(
|
||||
nameof(Cookie), typeof(string), typeof(OptionPage));
|
||||
|
||||
public bool IsOnR18
|
||||
{
|
||||
@ -36,6 +39,11 @@ namespace Pixiview
|
||||
get => (string)GetValue(PortProperty);
|
||||
set => SetValue(PortProperty, value);
|
||||
}
|
||||
public string Cookie
|
||||
{
|
||||
get => (string)GetValue(CookieProperty);
|
||||
set => SetValue(CookieProperty, value);
|
||||
}
|
||||
|
||||
public OptionPage()
|
||||
{
|
||||
@ -47,14 +55,22 @@ namespace Pixiview
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
||||
IsOnR18 = Preferences.Get(Configs.IsOnR18Key, false);
|
||||
IsProxied = Preferences.Get(Configs.IsProxiedKey, false);
|
||||
Host = Preferences.Get(Configs.HostKey, string.Empty);
|
||||
int pt = Preferences.Get(Configs.PortKey, 0);
|
||||
if (pt > 0)
|
||||
IsOnR18 = Configs.IsOnR18;
|
||||
var proxy = Configs.Proxy;
|
||||
if (proxy != null)
|
||||
{
|
||||
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()
|
||||
@ -63,30 +79,70 @@ namespace Pixiview
|
||||
|
||||
var r18 = IsOnR18;
|
||||
var proxied = IsProxied;
|
||||
|
||||
if (Configs.IsOnR18 != r18)
|
||||
{
|
||||
Preferences.Set(Configs.IsOnR18Key, r18);
|
||||
Configs.IsOnR18 = r18;
|
||||
App.DebugPrint($"r-18 filter: {r18}");
|
||||
}
|
||||
|
||||
var proxy = Configs.Proxy;
|
||||
var h = Host?.Trim();
|
||||
|
||||
Preferences.Set(Configs.IsOnR18Key, r18);
|
||||
Configs.IsOnR18 = r18;
|
||||
App.DebugPrint($"r-18 filter: {r18}");
|
||||
|
||||
Preferences.Set(Configs.IsProxiedKey, proxied);
|
||||
Preferences.Set(Configs.HostKey, h);
|
||||
var p = Port;
|
||||
if (int.TryParse(p, out int pt) && pt > 0)
|
||||
_ = int.TryParse(Port, out int pt);
|
||||
if (proxied)
|
||||
{
|
||||
Preferences.Set(Configs.PortKey, pt);
|
||||
if (proxy == null ||
|
||||
proxy.Address.Host != h ||
|
||||
proxy.Address.Port != pt)
|
||||
{
|
||||
Preferences.Set(Configs.IsProxiedKey, true);
|
||||
Preferences.Set(Configs.HostKey, h);
|
||||
if (pt > 0)
|
||||
{
|
||||
Preferences.Set(Configs.PortKey, pt);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(h) && pt > 0)
|
||||
{
|
||||
Configs.Proxy = new System.Net.WebProxy(h, pt);
|
||||
App.DebugPrint($"set proxy to: {h}:{pt}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (proxied && !string.IsNullOrEmpty(h) && pt > 0)
|
||||
{
|
||||
Configs.Proxy = new System.Net.WebProxy(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;
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<root>
|
||||
<Title>Pixiview</Title>
|
||||
<Guest>游客</Guest>
|
||||
<Ok>OK</Ok>
|
||||
<Cancel>取消</Cancel>
|
||||
<Yes>是</Yes>
|
||||
@ -12,6 +13,8 @@
|
||||
<Enabled>启用</Enabled>
|
||||
<Host>主机</Host>
|
||||
<Port>端口</Port>
|
||||
<Privacy>隐私</Privacy>
|
||||
<Cookie>Cookie</Cookie>
|
||||
<Daily>今日</Daily>
|
||||
<Weekly>本周</Weekly>
|
||||
<Monthly>本月</Monthly>
|
||||
@ -49,4 +52,5 @@
|
||||
<CantExportVideo>无法导出视频,请先下载完成。</CantExportVideo>
|
||||
<AlreadySavedVideo>视频已保存,是否继续?</AlreadySavedVideo>
|
||||
<ExportSuccess>视频已导出到照片库。</ExportSuccess>
|
||||
<FailedResponse>无法获取返回结果。</FailedResponse>
|
||||
</root>
|
@ -11,6 +11,7 @@ namespace Pixiview.Resources
|
||||
public class ResourceHelper
|
||||
{
|
||||
public static string Title => GetResource(nameof(Title));
|
||||
public static string Guest => GetResource(nameof(Guest));
|
||||
public static string Ok => GetResource(nameof(Ok));
|
||||
public static string Cancel => GetResource(nameof(Cancel));
|
||||
public static string Yes => GetResource(nameof(Yes));
|
||||
@ -32,6 +33,7 @@ namespace Pixiview.Resources
|
||||
public static string CantExportVideo => GetResource(nameof(CantExportVideo));
|
||||
public static string AlreadySavedVideo => GetResource(nameof(AlreadySavedVideo));
|
||||
public static string ExportSuccess => GetResource(nameof(ExportSuccess));
|
||||
public static string FailedResponse => GetResource(nameof(FailedResponse));
|
||||
|
||||
static readonly Dictionary<string, LanguageResource> dict = new Dictionary<string, LanguageResource>();
|
||||
|
||||
|
@ -16,6 +16,7 @@ namespace Pixiview.UI
|
||||
public static readonly Color ColorRedBackground = Color.FromRgb(0xfd, 0x43, 0x63);
|
||||
public static readonly Color ColorDownloadBackground = Color.FromRgb(0xd7, 0xd9, 0xe0);
|
||||
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 FontSizeSmall = Device.GetNamedSize(NamedSize.Small, typeof(Label));
|
||||
|
||||
|
@ -97,6 +97,10 @@ namespace Pixiview.Utils
|
||||
{
|
||||
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);
|
||||
Task.Run(task.Start);
|
||||
return task;
|
||||
|
@ -11,7 +11,7 @@ namespace Pixiview.Utils
|
||||
{
|
||||
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,
|
||||
Action<HttpRequestHeaders> header = null,
|
||||
Func<T, string> namehandler = null,
|
||||
@ -59,6 +59,7 @@ namespace Pixiview.Utils
|
||||
});
|
||||
if (response == null)
|
||||
{
|
||||
error = "response is null";
|
||||
return default;
|
||||
}
|
||||
using (response)
|
||||
@ -73,7 +74,8 @@ namespace Pixiview.Utils
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -89,7 +91,9 @@ namespace Pixiview.Utils
|
||||
}
|
||||
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)
|
||||
{
|
||||
error = null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
try
|
||||
{
|
||||
error = null;
|
||||
return JsonConvert.DeserializeObject<T>(content);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Pixiview.Illust;
|
||||
using Xamarin.Essentials;
|
||||
@ -22,6 +21,7 @@ namespace Pixiview.Utils
|
||||
#endif
|
||||
|
||||
private const string favoriteFile = "favorites.json";
|
||||
private const string globalFile = "global.json";
|
||||
private const string imageFolder = "img-original";
|
||||
private const string previewFolder = "img-master";
|
||||
private const string ugoiraFolder = "img-zip-ugoira";
|
||||
@ -199,6 +199,7 @@ namespace Pixiview.Utils
|
||||
file,
|
||||
Configs.UrlIllustList,
|
||||
Configs.Referer,
|
||||
out _,
|
||||
force: force);
|
||||
if (result == null || result.error)
|
||||
{
|
||||
@ -207,7 +208,7 @@ namespace Pixiview.Utils
|
||||
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");
|
||||
string query = $"mode={mode}";
|
||||
@ -229,6 +230,7 @@ namespace Pixiview.Utils
|
||||
file,
|
||||
string.Format(Configs.UrlIllustRanking, query),
|
||||
referer,
|
||||
out error,
|
||||
namehandler: rst =>
|
||||
{
|
||||
return Path.Combine(CacheFolder, mode, $"{rst.date}_{page}.json");
|
||||
@ -252,6 +254,7 @@ namespace Pixiview.Utils
|
||||
file,
|
||||
string.Format(Configs.UrlIllustRecommendsInit, id),
|
||||
string.Format(Configs.RefererIllust, id),
|
||||
out _,
|
||||
force: force);
|
||||
if (result == null || result.error)
|
||||
{
|
||||
@ -270,7 +273,8 @@ namespace Pixiview.Utils
|
||||
var result = HttpUtility.LoadObject<IllustRecommendsData>(
|
||||
null,
|
||||
string.Format(Configs.UrlIllustRecommendsList, ps),
|
||||
string.Format(Configs.RefererIllust, id));
|
||||
string.Format(Configs.RefererIllust, id),
|
||||
out _);
|
||||
if (result == null || result.error)
|
||||
{
|
||||
App.DebugPrint($"error when load recommends list data: {result?.message}");
|
||||
@ -278,6 +282,37 @@ namespace Pixiview.Utils
|
||||
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)
|
||||
{
|
||||
var file = Path.Combine(CacheFolder, preloadsFolder, $"{id}.json");
|
||||
@ -285,6 +320,7 @@ namespace Pixiview.Utils
|
||||
file,
|
||||
string.Format(Configs.UrlIllust, id),
|
||||
null,
|
||||
out _,
|
||||
force: force,
|
||||
action: content =>
|
||||
{
|
||||
@ -307,18 +343,22 @@ namespace Pixiview.Utils
|
||||
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 result = HttpUtility.LoadObject<IllustPageData>(
|
||||
file,
|
||||
string.Format(Configs.UrlIllustPage, id),
|
||||
string.Format(Configs.RefererIllust, id),
|
||||
out _,
|
||||
force: force);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -329,6 +369,7 @@ namespace Pixiview.Utils
|
||||
file,
|
||||
string.Format(Configs.UrlIllustUgoira, id),
|
||||
string.Format(Configs.RefererIllust, id),
|
||||
out _,
|
||||
force: force);
|
||||
if (result == null || result.error)
|
||||
{
|
||||
@ -342,7 +383,8 @@ namespace Pixiview.Utils
|
||||
var list = HttpUtility.LoadObject<IllustUserListData>(
|
||||
null,
|
||||
string.Format(Configs.UrlIllustUserAll, userId),
|
||||
string.Format(Configs.RefererIllustUser, userId));
|
||||
string.Format(Configs.RefererIllustUser, userId),
|
||||
out _);
|
||||
if (list == null || list.error)
|
||||
{
|
||||
App.DebugPrint($"error when load user data: {list?.message}");
|
||||
@ -360,7 +402,8 @@ namespace Pixiview.Utils
|
||||
var result = HttpUtility.LoadObject<IllustUserData>(
|
||||
null,
|
||||
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)
|
||||
{
|
||||
App.DebugPrint($"error when load user illust data: {result?.message}");
|
||||
@ -471,6 +514,9 @@ namespace Pixiview.Utils
|
||||
|
||||
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 UserIdKey = "user_id";
|
||||
public const string IsOnR18Key = "is_on_r18";
|
||||
@ -490,7 +536,7 @@ namespace Pixiview.Utils
|
||||
|
||||
public static bool IsOnR18;
|
||||
public static WebProxy Proxy;
|
||||
private static string Prefix => Proxy == null ?
|
||||
public static string Prefix => Proxy == null ?
|
||||
"https://hk.tsanie.us/reverse/" :
|
||||
"https://www.pixiv.net/";
|
||||
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;
|
||||
if (!save)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (cookie == null)
|
||||
{
|
||||
Preferences.Remove(CookieKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
Preferences.Set(CookieKey, cookie);
|
||||
}
|
||||
}
|
||||
|
||||
#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 =>
|
||||
{
|
||||
var list = new List<string>();
|
||||
@ -549,10 +607,10 @@ namespace Pixiview.Utils
|
||||
});
|
||||
return task.Task;
|
||||
}
|
||||
#elif __ANDROID__
|
||||
task.SetResult(false);
|
||||
#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 int SuffixPreloadLength = 33; // SuffixPreload.Length
|
||||
public static string UrlIllustList => Prefix + "ajax/top/illust?mode=all&lang=zh";
|
||||
|
Loading…
x
Reference in New Issue
Block a user