Archived
1
0

Core update. Base core features are done (main app doesn't compile)

Related Work Items: #416, #422, #423, #424
This commit is contained in:
Michael Gordeev
2020-06-11 21:17:18 +03:00
parent b3212738e8
commit c58d846057
18 changed files with 386 additions and 281 deletions
+3 -2
View File
@@ -1,4 +1,5 @@
using FoxTube.Utils;
using FoxTube.Services;
using FoxTube.Utils;
using Google.Apis.YouTube.v3.Data;
using SQLitePCL;
using System;
@@ -131,7 +132,7 @@ namespace FoxTube
public static async Task<Channel> GetChannel(string channelId, string part)
{
var request = UserManagement.Service.Channels.List(part);
var request = UserService.Service.Channels.List(part);
request.Id = channelId;
request.MaxResults = 1;
+16 -9
View File
@@ -130,17 +130,20 @@
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Include="Models\Collections\InboxCollection.cs" />
<Compile Include="Models\Collections\ViewCollection.cs" />
<Compile Include="Models\Subscription.cs" />
<Compile Include="Services\DownloadsCenter.cs" />
<Compile Include="Services\DownloadsService.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="Utils\Feedback.cs" />
<Compile Include="Utils\FeedbackIterop.cs" />
<Compile Include="Services\History.cs" />
<Compile Include="Services\Inbox.cs" />
<Compile Include="Services\InboxService.cs" />
<Compile Include="Utils\Metrics.cs" />
<Compile Include="Services\Search.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Utils\StoreInterop.cs" />
<Compile Include="UserManagement.cs" />
<Compile Include="Services\Storage.cs" />
<Compile Include="Utils\AddonsInterop.cs" />
<Compile Include="Services\UserService.cs" />
<Compile Include="Utils\SecretConstants.cs" />
<Compile Include="Utils\Utils.cs" />
<Compile Include="Models\DownloadItem.cs" />
<Compile Include="Models\InboxItem.cs" />
@@ -168,6 +171,9 @@
<PackageReference Include="Google.Apis.YouTube.v3">
<Version>1.45.0.1929</Version>
</PackageReference>
<PackageReference Include="Microsoft.Advertising.XAML">
<Version>10.1811.22001</Version>
</PackageReference>
<PackageReference Include="Microsoft.AppCenter.Analytics">
<Version>3.2.1</Version>
</PackageReference>
@@ -191,13 +197,14 @@
<WCFMetadata Include="Connected Services\" />
</ItemGroup>
<ItemGroup>
<SDKReference Include="Microsoft.Advertising.Xaml, Version=10.0">
<Name>Microsoft Advertising SDK for XAML</Name>
</SDKReference>
<SDKReference Include="Microsoft.Services.Store.Engagement, Version=10.0">
<Name>Microsoft Engagement Framework</Name>
</SDKReference>
</ItemGroup>
<ItemGroup>
<Folder Include="ValueConverters\" />
</ItemGroup>
<ItemGroup />
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
@@ -0,0 +1,45 @@
using Newtonsoft.Json;
using System.Threading.Tasks;
using System.Net.Http;
using Windows.UI.Xaml.Data;
using FoxTube.Services;
using FoxTube.Utils;
namespace FoxTube.Models.Collections
{
public class InboxCollection : ViewCollection<InboxItem>
{
private int _pageNumber = 0;
private HttpClient _httpClient = new HttpClient();
public override async Task<LoadMoreItemsResult> LoadItems()
{
// TODO: Add backend
HttpResponseMessage response = await _httpClient.GetAsync($"https://xfox111.net/API/FoxTube/Inbox?" +
$"lang={Storage.GetValue<string>(Storage.Settings.UILanguage)}&" +
$"currentVersion={Metrics.CurrentVersion}&" +
$"itemsCount={ItemsPerRequest}&" +
$"iteration={_pageNumber}");
if (!response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.NoContent)
{
HasMoreItems = false;
return new LoadMoreItemsResult
{
Count = 0
};
}
InboxItem[] newItems = JsonConvert.DeserializeObject<InboxItem[]>(await response.Content.ReadAsStringAsync());
foreach (InboxItem item in newItems)
Items.Add(item);
_pageNumber++;
return new LoadMoreItemsResult
{
Count = (uint)newItems.Length
};
}
}
}
@@ -0,0 +1,19 @@
using System.Collections.ObjectModel;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml.Data;
namespace FoxTube.Models.Collections
{
public abstract class ViewCollection<T> : ObservableCollection<T>, ISupportIncrementalLoading
{
public int ItemsPerRequest { get; set; }
public bool HasMoreItems { get; protected set; } = true;
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) =>
AsyncInfo.Run((c) => LoadItems());
public abstract Task<LoadMoreItemsResult> LoadItems();
}
}
+3 -2
View File
@@ -1,4 +1,5 @@
using System;
using FoxTube.Services;
using System;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;
@@ -20,7 +21,7 @@ namespace FoxTube.Models
public async Task CommenceDownload(IStreamInfo stream, IStorageFile destination)
{
Path = destination.Path;
YoutubeClient client = new YoutubeClient(UserManagement.Service.HttpClient);
YoutubeClient client = new YoutubeClient(UserService.Service.HttpClient);
State = DownloadState.Downloading;
Task task = client.Videos.Streams.DownloadAsync(stream, Path, DownloadPercentage, CTS.Token);
+5 -4
View File
@@ -13,6 +13,7 @@ using System.Threading.Tasks;
using Windows.Storage;
using YouTube;
using YoutubeExplode;
using FoxTube.Services;
namespace FoxTube.Models
{
@@ -36,10 +37,10 @@ namespace FoxTube.Models
catch (Exception e)
{
Metrics.SendReport(new Exception("Failed to unsubscribe", e));
return true;
return true;
}
UserManagement.SubscriptionsChangedInvoker(this, subscription);
UserService.SubscriptionsChangedInvoker(this, subscription);
Subscriptions.Remove(subscription);
SaveSubscriptions();
@@ -77,7 +78,7 @@ namespace FoxTube.Models
};
Subscriptions.Add(subscription);
UserManagement.SubscriptionsChangedInvoker(this, subscription);
UserService.SubscriptionsChangedInvoker(this, subscription);
SaveSubscriptions();
return true;
@@ -86,7 +87,7 @@ namespace FoxTube.Models
private void SaveSubscriptions()
{
Dictionary<string, string> subs = Subscriptions.Select(i =>
Dictionary<string, string> subs = Subscriptions.Select(i =>
new KeyValuePair<string, string>(i.ChannelId, i.Avatar.Default__?.Url))
as Dictionary<string, string>;
+3 -2
View File
@@ -1,4 +1,5 @@
using Google.Apis.YouTube.v3.Data;
using FoxTube.Services;
using Google.Apis.YouTube.v3.Data;
using System;
using YoutubeExplode;
@@ -36,7 +37,7 @@ namespace FoxTube.Models
private async void LoadInfo()
{
YoutubeClient client = new YoutubeClient(UserManagement.Service.HttpClient);
YoutubeClient client = new YoutubeClient(UserService.Service.HttpClient);
AdditionalMeta = await client.Videos.GetAsync(Meta.Id);
ChannelMeta = await client.Channels.GetByVideoAsync(Meta.Id);
@@ -9,23 +9,25 @@ using YoutubeExplode.Videos;
using YoutubeExplode.Videos.Streams;
using FoxTube.Utils;
using FoxTube.Models;
using Windows.Storage.Pickers;
namespace FoxTube.Services
{
public static class DownloadsCenter
public static class DownloadsService
{
public static List<SavedVideo> History { get; private set; }
public static List<SavedVideo> History { get; } = new List<SavedVideo>();
public static List<DownloadItem> Queue { get; } = new List<DownloadItem>();
static DownloadsCenter() =>
static DownloadsService() =>
Initialize();
private static async void Initialize()
{
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists);
StorageFile file = await Storage.Folder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists);
try
{
History = JsonConvert.DeserializeObject<List<SavedVideo>>(File.ReadAllText(file.Path) ?? "") ?? new List<SavedVideo>();
List<SavedVideo> savedVideos = JsonConvert.DeserializeObject<List<SavedVideo>>(File.ReadAllText(file.Path) ?? "") ?? new List<SavedVideo>();
History.AddRange(savedVideos);
foreach (SavedVideo i in History)
try { i.IsPathValid = await StorageApplicationPermissions.MostRecentlyUsedList.GetFileAsync(i.AccessToken) != null; }
@@ -33,7 +35,6 @@ namespace FoxTube.Services
}
catch (Exception e)
{
History = new List<SavedVideo>();
await file.DeleteAsync(StorageDeleteOption.PermanentDelete);
StorageApplicationPermissions.MostRecentlyUsedList.Clear();
Metrics.SendReport(new Exception("Failed to load downloads history", e));
@@ -64,12 +65,12 @@ namespace FoxTube.Services
{
await item.CommenceDownload(streamInfo, destination);
SavedVideo savedItem = item as SavedVideo;
SavedVideo savedItem = item;
savedItem.AccessToken = StorageApplicationPermissions.MostRecentlyUsedList.Add(destination);
History.Add(savedItem);
StorageFile file = await ApplicationData.Current.LocalFolder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists);
StorageFile file = await Storage.Folder.CreateFileAsync("DownloadHistory.json", CreationCollisionOption.OpenIfExists);
File.WriteAllText(file.Path, JsonConvert.SerializeObject(History));
}
catch (OperationCanceledException) { }
@@ -111,10 +112,11 @@ namespace FoxTube.Services
public static async Task<StorageFolder> GetDefaultDownloadsFolder()
{
if (string.IsNullOrWhiteSpace(Settings.DefaultDownloadsFolder))
return await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
if (Storage.GetValue<string>(Storage.Settings.DefaultDownloadsFolder) is string token && !string.IsNullOrWhiteSpace(token))
return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(token) ??
await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
else
return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(Settings.DefaultDownloadsFolder);
return await KnownFolders.VideosLibrary.CreateFolderAsync("FoxTube", CreationCollisionOption.OpenIfExists);
}
public static async Task CancelAll()
@@ -123,5 +125,28 @@ namespace FoxTube.Services
while (Queue.Count > 0)
await Task.Delay(500);
}
public static async Task<StorageFolder> ChangeDefaultFolder()
{
FolderPicker picker = new FolderPicker
{
SuggestedStartLocation = PickerLocationId.Downloads
};
StorageFolder folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
if (Storage.GetValue<string>(Storage.Settings.DefaultDownloadsFolder) is string token && !string.IsNullOrWhiteSpace(token))
StorageApplicationPermissions.FutureAccessList.AddOrReplace(token, folder);
else
{
token = StorageApplicationPermissions.FutureAccessList.Add(folder);
Storage.SetValue(Storage.Settings.DefaultDownloadsFolder, token);
}
}
return folder;
}
}
}
@@ -1,27 +1,36 @@
using FoxTube.Models;
using FoxTube.Models.Collections;
using FoxTube.Utils;
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Notifications;
namespace FoxTube.Services
{
public static class Inbox
public static class InboxService
{
private static readonly HttpClient client = new HttpClient();
private static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings;
public const string lastChangelogVersionKey = "Inbox.lastChangelogVersion";
public const string lastCheckKey = "Inbox.lastChangelogVersion";
public static async void PushNew()
private static readonly HttpClient client = new HttpClient();
public static InboxCollection GetInboxCollection() =>
new InboxCollection();
public static async Task PushNew()
{
try
{
// TODO: Add backend
HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/FoxTube/Inbox?toast=true&publishedAfter={storage.Values["Inbox.lastCheck"]}&lang={Settings.Language}&appVersion={Metrics.CurrentVersion}");
storage.Values["Inbox.lastCheck"] = DateTime.UtcNow.Ticks;
HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/FoxTube/Inbox?" +
$"toast=true&" +
$"publishedAfter={Storage.Registry.Values[lastCheckKey]}&" +
$"lang={Storage.GetValue<string>(Storage.Settings.UILanguage)}&" +
$"appVersion={Metrics.CurrentVersion}");
Storage.Registry.Values[lastCheckKey] = DateTime.UtcNow.Ticks;
if (response.StatusCode == HttpStatusCode.NoContent)
return;
@@ -37,26 +46,6 @@ namespace FoxTube.Services
}
}
public static async Task<InboxItem[]> GetMessages()
{
try
{
// TODO: Add backend
HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/API/FoxTube/Inbox?lang={Settings.Language}&currentVersion={Metrics.CurrentVersion}");
if (response.StatusCode == HttpStatusCode.NoContent)
return new InboxItem[0];
return JsonConvert.DeserializeObject<InboxItem[]>(await response.Content.ReadAsStringAsync());
}
catch (Exception e)
{
Metrics.SendReport(new Exception("Unable to retrieve inbox messages", e));
return new InboxItem[0];
}
}
/// <summary>
/// Fires toast notification with the last changelog content
/// </summary>
@@ -65,9 +54,14 @@ namespace FoxTube.Services
try
{
// TODO: Add backend
Settings.LastReviewedVersion = Metrics.CurrentVersion;
if ((string)Storage.Registry.Values[lastChangelogVersionKey] == Metrics.CurrentVersion)
return;
HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/API/FoxTube/Changelog?lang={Settings.Language}&version={Metrics.CurrentVersion}");
Storage.Registry.Values[lastChangelogVersionKey] = Metrics.CurrentVersion;
HttpResponseMessage response = await client.GetAsync($"https://xfox111.net/API/FoxTube/Changelog?" +
$"lang={Storage.GetValue<string>(Storage.Settings.UILanguage)}&" +
$"version={Metrics.CurrentVersion}");
if (response.StatusCode == HttpStatusCode.NoContent)
return;
+1 -1
View File
@@ -16,7 +16,7 @@ namespace FoxTube.Services
try
{
using HttpClient client = new HttpClient();
string results = await client.GetStringAsync($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={term}&hl={Settings.RelevanceLanguage}");
string results = await client.GetStringAsync($"http://suggestqueries.google.com/complete/search?ds=yt&client=toolbar&q={term}&hl={Storage.GetValue<string>(Storage.Settings.RelevanceLanguage)}");
XmlDocument doc = new XmlDocument();
doc.LoadXml(results);
+101
View File
@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI.Xaml;
namespace FoxTube.Services
{
public static class Storage
{
public static event EventHandler<Settings> SettingsChanged;
public static StorageFolder Folder => ApplicationData.Current.RoamingFolder;
public static ApplicationDataContainer Registry { get; } = ApplicationData.Current.RoamingSettings;
private static readonly Dictionary<Settings, object> _defaultSettings = new Dictionary<Settings, object>
{
{ Settings.Theme, ElementTheme.Default },
{ Settings.UILanguage, GetDefaultLanguage() },
{ Settings.RelevanceLanguage, GetDefaultLanguage() },
{ Settings.PromptFeedback, true },
{ Settings.PromptReview, true },
{ Settings.AllowAnalytics, true }
};
public enum Settings
{
/// <summary>
/// ElementTheme
/// </summary>
Theme,
/// <summary>
/// string
/// </summary>
UILanguage,
/// <summary>
/// string
/// </summary>
RelevanceLanguage,
/// <summary>
/// string
/// </summary>
DefaultDownloadsFolder,
/// <summary>
/// bool
/// </summary>
PromptFeedback,
/// <summary>
/// bool
/// </summary>
PromptReview,
/// <summary>
/// bool
/// </summary>
AllowAnalytics,
/// <summary>
/// string
/// </summary>
Region
}
public enum Metrics
{
/// <summary>
/// TimeSpan
/// </summary>
Uptime
}
public static void SetValue(Settings key, object value)
{
Registry.Values[$"{key.GetType().Name}.{key}"] = value;
SettingsChanged?.Invoke(value, key);
}
public static void SetValue(Metrics key, object value) =>
Registry.Values[$"{key.GetType().Name}.{key}"] = value;
public static T GetValue<T>(Settings key) =>
(T)(Registry.Values[$"{key.GetType().Name}.{key}"] ?? (_defaultSettings.ContainsKey(key) ? _defaultSettings[key] : null));
public static T GetValue<T>(Metrics key) =>
(T)Registry.Values[$"{key.GetType().Name}.{key}"];
private static string GetDefaultLanguage()
{
if (CultureInfo.InstalledUICulture.TwoLetterISOLanguageName.Belongs("ua", "ru", "by", "kz", "kg", "md", "lv", "ee")) //Languages for Russian-speaking countries
return "ru-RU";
else
return "en-US";
}
public static async Task ResetStorage()
{
Registry.Values.Clear();
foreach (IStorageItem i in await Folder.GetItemsAsync())
await i.DeleteAsync();
}
}
}
@@ -14,25 +14,24 @@ using FoxTube.Utils;
using YoutubeExplode;
using System.Linq;
using Newtonsoft.Json;
using Windows.Storage;
using Google.Apis.Oauth2.v2.Data;
namespace FoxTube
namespace FoxTube.Services
{
public static class UserManagement
public static class UserService
{
public const string UsersStorageKey = "UserService.Users";
public const string LastUserInfoKey = "UserService.LastUser";
public const int MaxUsersCount = 1;
#region Private members
private static readonly ApplicationDataContainer storage = ApplicationData.Current.LocalSettings;
private static readonly ExtendedYouTubeService _defaultService = new ExtendedYouTubeService(new Google.Apis.Services.BaseClientService.Initializer
{
ApplicationName = "FoxTube",
ApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0",
//ApiKey = "AIzaSyD7tpbuvmYDv9h4udo9L_g3r0sLPFAnN00"
ApiKey = SecretConstants.YoutubeApiKey, // TODO: Replace with an actual API key
});
private static readonly YoutubeClient _defaultYteClient = new YoutubeClient();
private static readonly YoutubeClient _defaultYtClient = new YoutubeClient();
private static string[] Scopes { get; } = new string[]
{
@@ -41,14 +40,7 @@ namespace FoxTube
YouTubeService.Scope.YoutubeForceSsl
};
private static ClientSecrets[] ClientSecrets { get; } = new ClientSecrets[MaxUsersCount]
{
new ClientSecrets
{
ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com",
ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_"
}
};
private static ClientSecrets[] ClientSecrets { get; } = SecretConstants.ClientSecrets;
#endregion
public static Userinfoplus[] Users { get; private set; } = new Userinfoplus[MaxUsersCount];
@@ -58,12 +50,33 @@ namespace FoxTube
public static bool CanAddAccounts => Users.Any(i => i == null);
public static User CurrentUser { get; set; }
public static bool Authorized => CurrentUser != null;
public static ExtendedYouTubeService Service => IncognitoMode ? _defaultService : (CurrentUser?.Service ?? _defaultService);
public static YoutubeClient YoutubeClient => IncognitoMode ? _defaultYteClient : (CurrentUser?.Client ?? _defaultYteClient);
public static ExtendedYouTubeService Service
{
get
{
if (IncognitoMode || CurrentUser == null)
return _defaultService;
else
return CurrentUser.Service;
}
}
public static YoutubeClient YouTubeClient
{
get
{
if (IncognitoMode || CurrentUser == null)
return _defaultYtClient;
else
return CurrentUser.Client;
}
}
public static event EventHandler<bool> UserStateUpdated;
public static event EventHandler<Subscription> SubscriptionsChanged;
static UserService() =>
Initialize();
public static async Task<bool> AddUser()
{
int queueIndex = Users.ToList().FindIndex(i => i == null);
@@ -100,14 +113,14 @@ namespace FoxTube
("Error details", result.ResponseErrorDetail.ToString()));
break;
}
return false;
}
public static async Task Initialize()
public static async void Initialize()
{
Users = JsonConvert.DeserializeObject<Userinfoplus[]>(storage.Values["UserManagement.Users"] as string ?? "") ?? new Userinfoplus[MaxUsersCount];
int? lastUserIndex = storage.Values["UserManagement.LastUser"] as int?;
Users = JsonConvert.DeserializeObject<Userinfoplus[]>(Storage.Registry.Values[UsersStorageKey] as string ?? "") ?? new Userinfoplus[MaxUsersCount];
int? lastUserIndex = Storage.Registry.Values[LastUserInfoKey] as int?;
if (lastUserIndex.HasValue && Users[lastUserIndex.Value] != null ||
(lastUserIndex = Users.ToList().FindIndex(i => i != null)) > -1)
@@ -133,12 +146,12 @@ namespace FoxTube
await CurrentUser.Credential.RevokeTokenAsync(CancellationToken.None);
storage.Values.Remove($"Subscriptions.{CurrentUser.UserInfo.Id}");
Storage.Registry.Values.Remove($"Subscriptions.{CurrentUser.UserInfo.Id}");
CurrentUser = null;
Users[Users.ToList().FindIndex(i => i.Id == userId)] = null;
storage.Values["UserManagement.Users"] = JsonConvert.SerializeObject(Users);
storage.Values["UserManagement.LastUser"] = null;
Storage.Registry.Values[UsersStorageKey] = JsonConvert.SerializeObject(Users);
Storage.Registry.Values[LastUserInfoKey] = null;
if (Users.Any(i => i != null))
await SwitchUser(Users.ToList().FindIndex(i => i != null));
@@ -189,8 +202,8 @@ namespace FoxTube
CurrentUser = await User.GetUser(credential);
Users[userIndex] = CurrentUser.UserInfo;
storage.Values["UserManagement.Users"] = JsonConvert.SerializeObject(Users);
storage.Values["UserManagement.LastUser"] = userIndex;
Storage.Registry.Values[UsersStorageKey] = JsonConvert.SerializeObject(Users);
Storage.Registry.Values[LastUserInfoKey] = userIndex;
credential.RefreshTokenUpdated += (s, e) => UpdateToken(CurrentUser.UserInfo.Id, credential.Token.RefreshToken);
UpdateToken(CurrentUser.UserInfo.Id, credential.Token.RefreshToken);
-142
View File
@@ -1,142 +0,0 @@
using FoxTube.Utils;
using System.Globalization;
using Windows.Storage;
namespace FoxTube
{
public static class Settings
{
static readonly ApplicationDataContainer settings = ApplicationData.Current.RoamingSettings;
public static string DefaultDownloadsFolder
{
get => (string)settings.Values["DefaultDownloadsFolder"] ?? "";
set => settings.Values["DefaultDownloadsFolder"] = value;
}
public static bool AskBeforeDownloading
{
get => (bool?)settings.Values["AskBeforeDownloading"] ?? true;
set => settings.Values["AskBeforeDownloading"] = value;
}
public static bool AllowAnalytics
{
get => (bool?)settings.Values["AllowAnalytics"] ?? true;
set => settings.Values["AllowAnalytics"] = value;
}
public static string DesiredVideoQuality
{
get => (string)settings.Values["DesiredVideoQuality"] ?? "auto";
set => settings.Values["DesiredVideoQuality"] = value;
}
public static string RememberedQuality
{
get => (string)settings.Values["RememberedVideoQuality"] ?? "1080p";
set => settings.Values["RememberedVideoQuality"] = value;
}
public static bool VideoNotifications
{
get => (bool?)settings.Values["NewVideosNotificationsAll"] ?? true;
set => settings.Values["NewVideosNotificationsAll"] = value;
}
public static bool DevNotifications
{
get => (bool?)settings.Values["DevelopersNewsNotifications"] ?? true;
set => settings.Values["DevelopersNewsNotifications"] = value;
}
public static bool CheckConnection
{
get => (bool?)settings.Values["WarnIfOnMeteredConnection"] ?? false;
set => settings.Values["WarnIfOnMeteredConnection"] = value;
}
public static bool Autoplay
{
get => (bool?)settings.Values["VideoAutoplay"] ?? true;
set => settings.Values["VideoAutoplay"] = value;
}
public static double Volume
{
get => (double?)settings.Values["Volume"] ?? 1;
set => settings.Values["Volume"] = value;
}
public static string Language
{
get => (string)settings.Values["InterfaceLanguage"] ?? GetDefaultLanguage();
set => settings.Values["InterfaceLanguage"] = value;
}
public static string RelevanceLanguage
{
get => (string)settings.Values["DesiredContentLanguage"] ?? CultureInfo.InstalledUICulture.TwoLetterISOLanguageName;
set => settings.Values["DesiredContentLanguage"] = value;
}
public static string Region
{
get => (string)settings.Values["Region"] ?? CultureInfo.InstalledUICulture.Name.Split('-')[1];
set => settings.Values["Region"] = value;
}
public static int SafeSearch
{
get => (int?)settings.Values["SafeSearch"] ?? 0; // Moderate
set => settings.Values["SafeSearch"] = value;
}
public static int DefaultHomeTab
{
get => (int?)settings.Values["DefaultHomeTab"] ?? 0; // Recommendations
set => settings.Values["DefaultHomeTab"] = value;
}
public static bool BlockExplicitContent
{
get => (bool?)settings.Values["BlockExplicitContent"] ?? true;
set => settings.Values["BlockExplicitContent"] = value;
}
public static bool HasAccount
{
get => (bool?)settings.Values["HasAccount"] ?? false;
set => settings.Values["HasAccount"] = value;
}
public static int Theme
{
get => (int?)settings.Values["PreferedUITheme"] ?? 2; // System
set => settings.Values["PreferedUITheme"] = value;
}
public static bool PromptReview
{
get => (bool?)settings.Values["PromptReview"] ?? Metrics.Uptime.TotalHours > 24;
set => settings.Values["PromptReview"] = value;
}
public static bool PromptFeedback
{
get => (bool?)settings.Values["PromptFeedback"] ?? Metrics.Uptime.TotalHours > 12;
set => settings.Values["PromptFeedback"] = value;
}
public static bool ProcessClipboard
{
get => (bool?)settings.Values["ProcessClipboardEntry"] ?? true;
set => settings.Values["ProcessClipboardEntry"] = value;
}
public static string LastReviewedVersion
{
get
{
if (settings.Values["LastReviewedVersion"] == null)
settings.Values["LastReviewedVersion"] = Metrics.CurrentVersion;
return (string)settings.Values["LastReviewedVersion"];
}
set => settings.Values["LastReviewedVersion"] = value;
}
static string GetDefaultLanguage()
{
if (CultureInfo.InstalledUICulture.TwoLetterISOLanguageName.Belongs("ua", "ru", "by", "kz", "kg", "md", "lv", "ee")) //Languages for Russian-speaking countries
return "ru-RU";
else
return "en-US";
}
public static void ResetSettings()
{
settings.Values.Clear();
ApplicationData.Current.LocalSettings.Values.Clear();
}
}
}
@@ -1,23 +1,27 @@
using Microsoft.AppCenter.Crashes;
using Microsoft.Advertising.WinRT.UI;
using System;
using System.Threading.Tasks;
using Windows.Services.Store;
namespace FoxTube.Utils
{
public static class StoreInterop
public static class AddonsInterop
{
public static bool AdsDisabled { get; private set; } = true;
public static string Price { get; private set; }
private const bool UseTestAds = true;
private static bool UseTestAds => true;
public static string ApplicationId => UseTestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : SecretConstants.ApplicationProductId;
public static string AdsId => UseTestAds ? "test" : SecretConstants.AdsUnitId;
private const string ProProductId = SecretConstants.ProAddonId;
public static string ApplicationId => UseTestAds ? "d25517cb-12d4-4699-8bdc-52040c712cab" : "9ncqqxjtdlfh";
public static string AdsId => UseTestAds ? "test" : "1100044398";
private static string ProProductId => "9NP1QK556625";
public static NativeAdsManagerV2 AdsManager => new NativeAdsManagerV2(ApplicationId, AdsId);
public static async Task UpdateStoreState()
static AddonsInterop() =>
UpdateStoreState();
public static async void UpdateStoreState()
{
StoreProductQueryResult requset = await StoreContext.GetDefault().GetAssociatedStoreProductsAsync(new[] { "Durable" });
@@ -42,17 +46,5 @@ namespace FoxTube.Utils
return false;
}
}
public static async void RequestReview()
{
StoreRateAndReviewResult result = await StoreContext.GetDefault().RequestRateAndReviewAppAsync();
if (result.Status == StoreRateAndReviewStatus.Error)
Metrics.SendReport(result.ExtendedError, new[] { ErrorAttachmentLog.AttachmentWithText(result.ExtendedJsonData, "extendedJsonData.json") },
("Status", result.Status.ToString()),
("WasReviewUpdated", result.WasUpdated.ToString()));
Metrics.AddEvent("Store review request has been recieved");
}
}
}
@@ -1,11 +1,13 @@
using System;
using FoxTube.Services;
using Microsoft.AppCenter.Crashes;
using Microsoft.Services.Store.Engagement;
using Windows.System;
using Windows.Services.Store;
using Windows.UI.Xaml.Controls;
namespace FoxTube.Utils
{
public static class Feedback
public static class FeedbackInterop
{
public static bool HasFeedbackHub => StoreServicesFeedbackLauncher.IsSupported();
@@ -13,15 +15,25 @@ namespace FoxTube.Utils
{
if (HasFeedbackHub)
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
else
await Launcher.LaunchUriAsync("mailto:feedback@xfox111.net".ToUri());
}
public static async void RequestStoreReview()
{
StoreRateAndReviewResult result = await StoreContext.GetDefault().RequestRateAndReviewAppAsync();
if (result.Status == StoreRateAndReviewStatus.Error)
Metrics.SendReport(result.ExtendedError, new[] { ErrorAttachmentLog.AttachmentWithText(result.ExtendedJsonData, "extendedJsonData.json") },
("Status", result.Status.ToString()),
("WasReviewUpdated", result.WasUpdated.ToString()));
Metrics.AddEvent("Store review request has been received");
}
public static async void PromptFeedback()
{
if (!HasFeedbackHub)
{
Settings.PromptFeedback = false;
Storage.SetValue(Storage.Settings.PromptFeedback, false);
return;
}
@@ -41,7 +53,7 @@ namespace FoxTube.Utils
ContentDialogResult result = await dialog.ShowAsync();
if (result != ContentDialogResult.None)
Settings.PromptFeedback = false;
Storage.SetValue(Storage.Settings.PromptFeedback, false);
if (result == ContentDialogResult.Primary)
OpenFeedbackHub();
@@ -61,17 +73,17 @@ namespace FoxTube.Utils
Content = new TextBlock
{
Text = "Could you leave a feedback on Microsfot Store page? It's very important for me :)"
Text = "Could you leave a feedback on Microsoft Store page? It's very important for me :)"
}
};
ContentDialogResult result = await dialog.ShowAsync();
if (result != ContentDialogResult.None)
Settings.PromptReview = false;
Storage.SetValue(Storage.Settings.PromptReview, false);
if (result == ContentDialogResult.Primary)
StoreInterop.RequestReview();
RequestStoreReview();
}
}
}
+25 -20
View File
@@ -6,19 +6,17 @@ using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.ApplicationModel;
using Windows.Storage;
using FoxTube.Services;
namespace FoxTube.Utils
{
public static class Metrics
{
static readonly ApplicationDataContainer storage = ApplicationData.Current.RoamingSettings;
static readonly Stopwatch sw = new Stopwatch();
public static TimeSpan Uptime
{
get => (TimeSpan?)storage.Values["Metrics.SpentTime"] ?? TimeSpan.FromSeconds(0);
set => storage.Values["Metrics.SpentTime"] = value;
get => Storage.GetValue<TimeSpan?>(Storage.Metrics.Uptime) ?? TimeSpan.FromSeconds(0);
set => Storage.SetValue(Storage.Metrics.Uptime, value);
}
public static string CurrentVersion
{
@@ -32,11 +30,11 @@ namespace FoxTube.Utils
static Metrics()
{
sw.Start();
if (!Settings.AllowAnalytics)
if (!Storage.GetValue<bool>(Storage.Settings.AllowAnalytics))
return;
AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics), typeof(Crashes));
AppCenter.SetCountryCode(Settings.Region);
AppCenter.Start(SecretConstants.MetricsId, typeof(Analytics), typeof(Crashes));
AppCenter.SetCountryCode(Storage.GetValue<string>(Storage.Settings.Region));
AppCenter.LogLevel = LogLevel.Verbose;
}
@@ -45,23 +43,30 @@ namespace FoxTube.Utils
sw.Stop();
Uptime += sw.Elapsed;
AddEvent("Session closed",
("Duration", sw.Elapsed.ToString()),
("Spend time total", Uptime.ToString()));
if (Storage.GetValue<bool>(Storage.Settings.AllowAnalytics))
AddEvent("Session closed",
("Duration", sw.Elapsed.ToString()),
("Spend time total", Uptime.ToString()));
}
public static void AddEvent(string eventName, params (string key, string value)[] details) =>
Analytics.TrackEvent(eventName,
details.Length < 1 ? null :
details.Select(i => new KeyValuePair<string, string>(i.key, i.value)) as Dictionary<string, string>);
public static void AddEvent(string eventName, params (string key, string value)[] details)
{
if (Storage.GetValue<bool>(Storage.Settings.AllowAnalytics))
Analytics.TrackEvent(eventName,
details.Length < 1 ? null :
details.Select(i => new KeyValuePair<string, string>(i.key, i.value)) as Dictionary<string, string>);
}
public static void SendReport(Exception exception, ErrorAttachmentLog[] logs = null, params (string key, string value)[] details)
{
logs ??= new ErrorAttachmentLog[0];
Crashes.TrackError(exception,
details.Length < 1 ? null :
details.Select(i => new KeyValuePair<string, string>(i.key, i.value)) as Dictionary<string, string>,
logs);
if (Storage.GetValue<bool>(Storage.Settings.AllowAnalytics))
{
logs ??= new ErrorAttachmentLog[0];
Crashes.TrackError(exception ?? new Exception("Unknown exception"),
details.Length < 1 ? null :
details.Select(i => new KeyValuePair<string, string>(i.key, i.value)) as Dictionary<string, string>,
logs);
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
using Google.Apis.Auth.OAuth2;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FoxTube.Services;
namespace FoxTube.Utils
{
public static class SecretConstants
{
public const string YoutubeApiKey = "AIzaSyBgHrCnrlzlVmk0cJKL8RqP9Y8x6XSuk_0";
public const string MetricsId = "45774462-9ea7-438a-96fc-03982666f39e";
public const string ProAddonId = "9NP1QK556625";
public const string AdsUnitId = "1100044398";
public const string ApplicationProductId = "9ncqqxjtdlfh";
public static ClientSecrets[] ClientSecrets { get; } = new ClientSecrets[UserService.MaxUsersCount]
{
// TODO: Replace with actual secrets
new ClientSecrets
{
ClientId = "349735264870-2ekqlm0a4mkg3mmrfcv90s3qp3o15dq0.apps.googleusercontent.com",
ClientSecret = "BkVZOAaCU2Zclf0Zlicg6y2_"
}
};
}
}
+4 -3
View File
@@ -1,4 +1,5 @@
using System;
using FoxTube.Services;
using System;
using Windows.ApplicationModel.Core;
using Windows.Security.Credentials;
@@ -25,10 +26,10 @@ namespace FoxTube.Utils
public static async void RestartApp(string args) =>
await CoreApplication.RequestRestartAsync(args ?? "");
public static void InitializeFailsafeProtocol()
public static async void InitializeFailsafeProtocol()
{
Metrics.AddEvent("Failsafe protocol initiated");
Settings.ResetSettings();
await Storage.ResetStorage();
PasswordVault passwordVault = new PasswordVault();
foreach (PasswordCredential credential in passwordVault.RetrieveAll())
passwordVault.Remove(credential);