diff --git a/FluentWeather.Tasks/NotifyTask.cs b/FluentWeather.Tasks/NotifyTask.cs index b946d74..de5805d 100644 --- a/FluentWeather.Tasks/NotifyTask.cs +++ b/FluentWeather.Tasks/NotifyTask.cs @@ -13,6 +13,9 @@ using Windows.ApplicationModel.Resources; using TileHelper = FluentWeather.Uwp.Shared.Helpers.TileHelper; using FluentWeather.Uwp.Shared.Helpers; +using Windows.UI.Notifications; +using Windows.UI.StartScreen; +using System.Linq; namespace FluentWeather.Tasks { @@ -54,7 +57,7 @@ public async void Run(IBackgroundTaskInstance taskInstance) await PushDaily(lon,lat); await PushWarnings(lon, lat); - + await UpdateSecondaryTiles(); deferral.Complete(); } @@ -193,6 +196,27 @@ private void PushTomorrow(List data) } + + + private async Task UpdateSecondaryTiles() + { + var tiles = await SecondaryTile.FindAllAsync(); + foreach (var tile in tiles) + { + + var geolocation = Settings.SavedCities.FirstOrDefault(p => p.Location.GetHashCode().ToString() == tile.TileId); + if (Settings.DefaultGeolocation.Location.GetHashCode().ToString() == tile.TileId) + { + geolocation = Settings.DefaultGeolocation; + } + if (geolocation is null) + continue; + var daily = await _dailyForecastProvider.GetDailyForecasts(geolocation.Location.Longitude, geolocation.Location.Latitude); + var dailyNotification= new TileNotification(TileHelper.GenerateForecastTileContent(daily).GetXml()) { Tag = "forecast" }; + var updater = TileUpdateManager.CreateTileUpdaterForSecondaryTile(tile.TileId); + updater.Update(dailyNotification); + } + } } diff --git a/FluentWeather.Uwp.Shared/Helpers/TileHelper.cs b/FluentWeather.Uwp.Shared/Helpers/TileHelper.cs index aeda18d..1bd10a0 100644 --- a/FluentWeather.Uwp.Shared/Helpers/TileHelper.cs +++ b/FluentWeather.Uwp.Shared/Helpers/TileHelper.cs @@ -8,11 +8,57 @@ using FluentWeather.Uwp.Shared.Helpers.ValueConverters; using Microsoft.Toolkit.Uwp.Notifications; using Microsoft.Toolkit.Uwp; +using Windows.ApplicationModel; +using Windows.UI.Shell; +using System.Threading.Tasks; +using Windows.UI.StartScreen; +using TileSize = Microsoft.Toolkit.Uwp.Notifications.TileSize; +using System.Linq; namespace FluentWeather.Uwp.Shared.Helpers { public static class TileHelper { + public static async Task PinSecondaryTileToTaskBarAsync(GeolocationBase geolocation) + { + var access = Windows.ApplicationModel.LimitedAccessFeatures.TryUnlockFeature("com.microsoft.windows.taskbar.requestPinSecondaryTile","jf0w/jdkHVCaehrYkTQLMg==","we3nmnswjxrbg has registered their use of com.microsoft.windows.taskbar.requestPinSecondaryTile with Microsoft and agrees to the terms of use."); + + if ((access.Status == LimitedAccessFeatureStatus.Available)||(access.Status == LimitedAccessFeatureStatus.AvailableWithoutToken)) + { + TaskbarManager taskbarManager = TaskbarManager.GetDefault(); + + if (taskbarManager != null) + { + // Initialize the tile (all properties below are required) + var tile = CreateSecondaryTile(geolocation); + // Pin it to the taskbar + bool isPinnedToTaskBar = await taskbarManager.RequestPinSecondaryTileAsync(tile); + } + } + } + public static async Task PinSecondaryTileToStartAsync(GeolocationBase geolocation) + { + if (SecondaryTile.Exists(geolocation.Location.GetHashCode().ToString())) return; + // Pin it to the taskbar + var tile = CreateSecondaryTile(geolocation); + bool isPinned = await tile.RequestCreateAsync(); + } + + public static SecondaryTile CreateSecondaryTile(GeolocationBase geolocation) + { + var tile = new SecondaryTile(geolocation.Location.GetHashCode().ToString()); + tile.DisplayName = geolocation.Name; + tile.Arguments = $"location={geolocation.Location.GetHashCode()}"; + tile.VisualElements.Square44x44Logo = new Uri("ms-appx:///Assets/Square44x44Logo.png"); + tile.VisualElements.Square150x150Logo = new Uri("ms-appx:///Assets/Square150x150Logo.png"); + tile.VisualElements.Square310x310Logo = new Uri("ms-appx:///Assets/LargeTile.png"); + tile.VisualElements.Wide310x150Logo = new Uri("ms-appx:///Assets/Wide310x150Logo.png"); + + tile.VisualElements.ShowNameOnSquare150x150Logo = true; + tile.VisualElements.ShowNameOnWide310x150Logo = true; + tile.VisualElements.ShowNameOnSquare310x310Logo = true; + return tile; + } public static void UpdateBadge(int value) { diff --git a/FluentWeather.Uwp/App.xaml.cs b/FluentWeather.Uwp/App.xaml.cs index 4d35ce3..b3b1616 100644 --- a/FluentWeather.Uwp/App.xaml.cs +++ b/FluentWeather.Uwp/App.xaml.cs @@ -14,6 +14,7 @@ using FluentWeather.Uwp.Shared.Helpers; using Microsoft.Extensions.DependencyInjection; using Microsoft.Gaming.XboxGameBar; +using FluentWeather.Uwp.ViewModels; namespace FluentWeather.Uwp; @@ -96,7 +97,8 @@ protected override void OnActivated(IActivatedEventArgs e) { widgetArgs = e as XboxGameBarWidgetActivatedEventArgs; } - if (scheme.Equals("weather")){ + if (scheme.Equals("weather")) + { } } @@ -203,6 +205,11 @@ protected override void OnLaunched(LaunchActivatedEventArgs e) } ActiveArguments = e.Arguments; + if(e.TileId is not null or "") + { + ActiveArguments = e.TileId; + } + if(Common.Settings.IsAnalyticsEnabled) { var service = DIContainer.Locator.ServiceProvider.GetService(); diff --git a/FluentWeather.Uwp/FluentWeather.Uwp.csproj b/FluentWeather.Uwp/FluentWeather.Uwp.csproj index e2cec63..f96be64 100644 --- a/FluentWeather.Uwp/FluentWeather.Uwp.csproj +++ b/FluentWeather.Uwp/FluentWeather.Uwp.csproj @@ -4,7 +4,7 @@ latestMajor False - 273B17C3FD4B2E52382899B29331EFADCD351294 + E39CF1FCA51EDC5BD1FCA218F4565B737622C059 SHA256 False True @@ -226,6 +226,7 @@ + @@ -444,6 +445,7 @@ + @@ -695,7 +697,6 @@ - diff --git a/FluentWeather.Uwp/FluentWeather.Uwp_TemporaryKey.pfx b/FluentWeather.Uwp/FluentWeather.Uwp_TemporaryKey.pfx index f985cbb..5da3826 100644 Binary files a/FluentWeather.Uwp/FluentWeather.Uwp_TemporaryKey.pfx and b/FluentWeather.Uwp/FluentWeather.Uwp_TemporaryKey.pfx differ diff --git a/FluentWeather.Uwp/Helpers/FeatureTokenGenerator.cs b/FluentWeather.Uwp/Helpers/FeatureTokenGenerator.cs new file mode 100644 index 0000000..74ca14e --- /dev/null +++ b/FluentWeather.Uwp/Helpers/FeatureTokenGenerator.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Windows.ApplicationModel; +using System.Security.Cryptography; + + +namespace FluentWeather.Uwp.Helpers +{ + static class FeatureTokenGenerator + { + static readonly Dictionary LimitedAccessFeaturesMap = new Dictionary() + { + { "com.microsoft.windows.windowdecorations", "425261a8-7f73-4319-8a53-fc13f87e1717" }, + { "com.microsoft.windows.updateorchestrator.1", "20C662033A4007A55375BF00D986C280B41A418F" }, + { "com.microsoft.windows.update.1", "01B2AEA8-7DD5-4066-A081-1E4CD1479CCA" }, + { "com.microsoft.windows.taskbar.requestPinSecondaryTile", "04c19204-10d9-450a-95c4-2910c8f72be3" }, + { "com.microsoft.windows.richeditmath", "RDZCQjY2M0YtQkFDMi00NkIwLUI3NzEtODg4NjMxMEVENkFF" }, + { "com.microsoft.windows.holographic.xrruntime.2", "58AA36EF-7C1A-4A56-9308-FC882F56465A" }, + { "com.microsoft.windows.holographic.xrruntime.1", "036EFF74-8BF2-4249-82AF-92235C6E1A10" }, + { "com.microsoft.windows.holographic.shell", "527f4968-f193-419a-b91f-46b9106e1129" }, + { "com.microsoft.windows.holographic.keyboardcursor_v1", "FE676B8B-E396-4A80-9573-B67542840E5C" }, + { "com.microsoft.windows.callcontrolpublicapi_v1", "6e7e52aa-cddb-4e57-9f1c-7dd511ad7d01" }, + { "com.microsoft.windows.applicationwindow", "e5a85131-319b-4a56-9577-1c1d9c781218" }, + { "com.microsoft.windows.applicationmodel.phonelinetransportdevice_v1", "cb9WIvVfhp+8lFhaSrB6V6zUBGqctteKi/f/9AIeoZ4" }, + { "com.microsoft.windows.applicationmodel.conversationalagent_v1", "hhrovbOc/z8TgeoWheL4RF5vLLJrKNAQpdyvhlTee6I" }, + { "com.microsoft.services.cortana.cortanaactionableinsights_v1", "nEVyyzytE6ankNk1CIAu6sZsh8vKLw3Q7glTOHB11po=" } + }; + + public static string GenerateTokenFromFeatureId(string featureId) + => GenerateFeatureToken(featureId, LimitedAccessFeaturesMap[featureId], AppInfo.Current.PackageFamilyName); + + public static string GenerateAttestation(string featureId) + => $"{AppInfo.Current.PackageFamilyName.Split('_').Last()} has registered their use of {featureId} with Microsoft and agrees to the terms of use."; + + private static string GenerateFeatureToken(string featureId, string featureKey, string packageIdentity) + { + var fullBytes = Encoding.UTF8.GetBytes($"{featureId}!{featureKey}!{packageIdentity}"); + var tokenBytes = new byte[16]; + using (var shaCsp = new SHA256CryptoServiceProvider()) + { + shaCsp.TransformFinalBlock(fullBytes, 0, fullBytes.Length); + Array.Copy(shaCsp.Hash, tokenBytes, tokenBytes.Length); + } + + return Convert.ToBase64String(tokenBytes); + } + } +} diff --git a/FluentWeather.Uwp/Helpers/JumpListHelper.cs b/FluentWeather.Uwp/Helpers/JumpListHelper.cs index 140662d..ddcbe61 100644 --- a/FluentWeather.Uwp/Helpers/JumpListHelper.cs +++ b/FluentWeather.Uwp/Helpers/JumpListHelper.cs @@ -20,14 +20,14 @@ public static async Task SetJumpList(GeolocationBase defaultLocation,IEnumerable } private static JumpListItem CreateDefaultItem(GeolocationBase geolocation) { - var item = JumpListItem.CreateWithArguments("City_" + geolocation.Name,geolocation.Name); + var item = JumpListItem.CreateWithArguments(geolocation.Location.GetHashCode().ToString(),geolocation.Name); item.GroupName = ResourceLoader.GetForCurrentView().GetString("CurrentLocation"); item.Logo = new Uri("ms-appx:///Assets/Icons/CurrentLocation.png"); return item; } private static JumpListItem CreateItem(GeolocationBase geolocation) { - var item = JumpListItem.CreateWithArguments("City_" + geolocation.Name, geolocation.Name); + var item = JumpListItem.CreateWithArguments(geolocation.Location.GetHashCode().ToString(), geolocation.Name); item.GroupName = ResourceLoader.GetForCurrentView().GetString("SavedLocations"); item.Logo = new Uri("ms-appx:///Assets/Icons/SavedCity.png"); return item; diff --git a/FluentWeather.Uwp/Pages/CitiesPage.xaml b/FluentWeather.Uwp/Pages/CitiesPage.xaml index 18d9c91..6dc3345 100644 --- a/FluentWeather.Uwp/Pages/CitiesPage.xaml +++ b/FluentWeather.Uwp/Pages/CitiesPage.xaml @@ -55,6 +55,11 @@ x:Uid="GetHistoricalWeatherItem" Click="GetHistoricalWeatherItem_Click" Icon="Download" /> + @@ -92,6 +97,11 @@ Command="{x:Bind vm:CitiesPageViewModel.Instance.DeleteCityCommand}" CommandParameter="{x:Bind}" Icon="Delete" /> + diff --git a/FluentWeather.Uwp/Pages/CitiesPage.xaml.cs b/FluentWeather.Uwp/Pages/CitiesPage.xaml.cs index 97f7666..7ba6e6d 100644 --- a/FluentWeather.Uwp/Pages/CitiesPage.xaml.cs +++ b/FluentWeather.Uwp/Pages/CitiesPage.xaml.cs @@ -26,25 +26,30 @@ protected override void OnNavigatedTo(NavigationEventArgs e) CitiesView.SelectionChanged += CitiesView_SelectionChanged; if (App.ActiveArguments is null) { - SetSelectedLocation(Common.Settings.DefaultGeolocation?.Name); + SetSelectedLocation(Common.Settings.DefaultGeolocation?.Location.GetHashCode().ToString()); return; } SetSelectedLocation(App.ActiveArguments.Replace("City_", "")); + + if (MainPageViewModel.Instance.CurrentGeolocation is null) + { + CitiesPageViewModel.Instance.GetCurrentCity(); + } } - public void SetSelectedLocation(string name) + public void SetSelectedLocation(string hash) { if (Common.Settings.DefaultGeolocation?.Name is null) { CurrentCityView.SelectedIndex = 0; return; } - if (ViewModel.CurrentCity is null || name == ViewModel.CurrentCity.Name) - { - CurrentCityView.SelectedIndex = 0; - return; - } - var location = ViewModel.Cities.FirstOrDefault(p => p.Name == name); + //if (ViewModel.CurrentCity is null || hash == ViewModel.CurrentCity.Location.GetHashCode().ToString()) + //{ + // CurrentCityView.SelectedIndex = 0; + // return; + //} + var location = ViewModel.Cities.FirstOrDefault(p => p.Location.GetHashCode().ToString() == hash); if (location is null) return; var index = ViewModel.Cities.IndexOf(location); CitiesView.SelectedIndex = index; @@ -55,25 +60,25 @@ private void SettingsButton_Click(object sender, RoutedEventArgs e) ((Frame)Parent)?.Navigate(typeof(SettingsPage),null,Theme.GetSplitPaneNavigationTransition()); } - private void CitiesView_SelectionChanged(object sender, SelectionChangedEventArgs e) + private async void CitiesView_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (CitiesView.SelectedIndex == -1) return; CurrentCityView.SelectedIndex = -1; MainPageViewModel.Instance.CurrentGeolocation = CitiesPageViewModel.Instance.Cities[CitiesView.SelectedIndex]; if (MainPageViewModel.Instance.CurrentGeolocation is null) { - CitiesPageViewModel.Instance.GetCurrentCity(); + await CitiesPageViewModel.Instance.GetCurrentCity(); } } - private void CurrentCityView_SelectionChanged(object sender, SelectionChangedEventArgs e) + private async void CurrentCityView_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (CurrentCityView.SelectedIndex != 0) return; MainPageViewModel.Instance.CurrentGeolocation = CitiesPageViewModel.Instance.CurrentCity; CitiesView.SelectedIndex = -1; if (MainPageViewModel.Instance.CurrentGeolocation is null) { - CitiesPageViewModel.Instance.GetCurrentCity(); + await CitiesPageViewModel.Instance.GetCurrentCity(); } } diff --git a/FluentWeather.Uwp/Strings/en-US/Resources.resw b/FluentWeather.Uwp/Strings/en-US/Resources.resw index 70b173b..7745ad9 100644 --- a/FluentWeather.Uwp/Strings/en-US/Resources.resw +++ b/FluentWeather.Uwp/Strings/en-US/Resources.resw @@ -465,6 +465,9 @@ Split with "|", support Regex Personalization + + Pin to Start + Pressure diff --git a/FluentWeather.Uwp/Strings/zh-CN/Resources.resw b/FluentWeather.Uwp/Strings/zh-CN/Resources.resw index d389b47..cb98bd9 100644 --- a/FluentWeather.Uwp/Strings/zh-CN/Resources.resw +++ b/FluentWeather.Uwp/Strings/zh-CN/Resources.resw @@ -462,6 +462,9 @@ 个性化 + + 固定到开始菜单 + 气压 diff --git a/FluentWeather.Uwp/ViewModels/CitiesPageViewModel.cs b/FluentWeather.Uwp/ViewModels/CitiesPageViewModel.cs index 21f963c..f774189 100644 --- a/FluentWeather.Uwp/ViewModels/CitiesPageViewModel.cs +++ b/FluentWeather.Uwp/ViewModels/CitiesPageViewModel.cs @@ -9,6 +9,12 @@ using Microsoft.Extensions.DependencyInjection; using FluentWeather.Uwp.Shared; using FluentWeather.Uwp.Helpers.Analytics; +using FluentWeather.Uwp.Shared.Helpers; +using Windows.ApplicationModel; +using Windows.UI.StartScreen; +using Windows.Networking.Sockets; +using FluentWeather.Tasks; +using Windows.UI.Xaml; namespace FluentWeather.Uwp.ViewModels; @@ -73,13 +79,21 @@ public void SaveCity(GeolocationBase city) Query = city.Name; Locator.ServiceProvider.GetService()?.TrackCitySaved(city.Name); } + [RelayCommand] public void DeleteCity(GeolocationBase item) { Cities.Remove(item); } - public async void GetCurrentCity() + [RelayCommand] + public async Task PinSecondaryTileAsync(GeolocationBase item) + { + await TileHelper.PinSecondaryTileToStartAsync(item); + } + + + public async Task GetCurrentCity() { var location = await LocationHelper.GetGeolocation(); if (Common.Settings.DefaultGeolocation?.Name is null)