1
0
mirror of https://github.com/XFox111/GUTSchedule.git synced 2026-04-22 06:58:01 +03:00

Complete UWP app (without translations)

This commit is contained in:
Michael Gordeev
2020-02-18 18:00:44 +03:00
parent 4363d25ff3
commit c23fd04f2d
58 changed files with 592 additions and 128 deletions
@@ -79,7 +79,7 @@
<!-- AboutActivity -->
<string name="aboutTitle">About application</string>
<string name="appDescription">Application for SPbSUT professors\' and students\' schedule export</string>
<string name="developedBy">Developed by Michael Gordeev (IS-907, INS) in the \"Technologies of Informational and Educational Systems\" Research Facility</string>
<string name="developedBy">Developed by Michael Gordeev (ICT-907, INS) in the \"Technologies of Informational and Educational Systems\" Research Facility</string>
<string name="contributorsTitle">Contributors</string>
<string name="specialThanksTitle">Special thanks</string>
+48 -5
View File
@@ -2,21 +2,64 @@
x:Class="GUTSchedule.UWP.AboutPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:GUTSchedule.UWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
Loaded="Page_Loaded">
<Page.TopAppBar>
<CommandBar ClosedDisplayMode="Compact" Background="{StaticResource SystemAccentColor}" RequestedTheme="Dark">
<CommandBar.Content>
<TextBlock Text="About application" Style="{StaticResource TitleTextBlockStyle}" Margin="10,6"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Width="48" Height="48" Margin="0" Background="Transparent" Click="BackRequested">
<SymbolIcon Symbol="Back"/>
</Button>
<TextBlock Text="About application" Style="{StaticResource TitleTextBlockStyle}" Margin="10,6" Grid.Column="1"/>
</Grid>
</CommandBar.Content>
</CommandBar>
</Page.TopAppBar>
<Grid>
<ScrollViewer>
<StackPanel Margin="10">
<TextBlock Style="{StaticResource TitleTextBlockStyle}" Text="GUT.Schedule"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="v$(Build.BuildNumber) (ci-id #$(Build.BuildId))" Foreground="DarkGray" Margin="0,0,0,10" x:Name="version"/>
</Grid>
<TextBlock Text="appDescription"/>
<TextBlock Text="developedBy"/>
<TextBlock x:Name="contributorsTitle" Style="{StaticResource SubtitleTextBlockStyle}" Text="Contributors" Margin="0,10,0,0" Visibility="Collapsed"/>
<TextBlock x:Name="contributors" Visibility="Collapsed"/>
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Special thanks" Margin="0,10,0,0"/>
<TextBlock Text="specialThanksPeople"/>
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Contacts" Margin="0,10,0,0"/>
<TextBlock>
<Run>Website</Run>: <Hyperlink NavigateUri="https://xfox111.net">https://xfox111.net</Hyperlink><LineBreak/>
<Run>Twitter</Run>: <Hyperlink NavigateUri="https://twitter.com/xfox111">@xfox111</Hyperlink><LineBreak/>
<Run>VKontakte</Run>: <Hyperlink NavigateUri="https://vk.com/xfox.mike">@xfox.mike</Hyperlink><LineBreak/>
<Run>LinkedIn</Run>: <Hyperlink NavigateUri="https://linkedin.com/in/xfox">@xfox</Hyperlink><LineBreak/>
<Run>GitHub</Run>: <Hyperlink NavigateUri="https://github.com/xfox111">@xfox111</Hyperlink>
</TextBlock>
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Useful links" Margin="0,10,0,0"/>
<TextBlock>
<Hyperlink NavigateUri="https://xfox111.net/Projects/GUTSchedule/PrivacyPolicy.txt">Privacy policy</Hyperlink><LineBreak/>
<Hyperlink NavigateUri="https://www.gnu.org/licenses/gpl-3.0">General Public License v3</Hyperlink><LineBreak/>
<Hyperlink NavigateUri="https://github.com/xfox111/gutschedule">GitHub repository</Hyperlink><LineBreak/>
<Hyperlink NavigateUri="http://tios.spbgut.ru/index.php">"TIES" RF</Hyperlink><LineBreak/>
<Hyperlink NavigateUri="https://sut.ru">SPbSUT</Hyperlink><LineBreak/>
</TextBlock>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="©2020 Michael &#x22;XFox&#x22; Gordeev" Margin="0,10,0,0"/>
<Button Content="Leave feedback" HorizontalAlignment="Left" Margin="5" Click="Feedback_Click"/>
</StackPanel>
</ScrollViewer>
</Page>
+77 -17
View File
@@ -1,30 +1,90 @@
using System;
using Microsoft.Services.Store.Engagement;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using System.Net.Http;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Resources;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
using Windows.UI.Xaml.Documents;
namespace GUTSchedule.UWP
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class AboutPage : Page
{
private readonly ResourceLoader resources = ResourceLoader.GetForCurrentView();
public AboutPage()
{
this.InitializeComponent();
InitializeComponent();
KeyDown += (s, e) =>
{
if (e.Key == VirtualKey.Back || e.Key == VirtualKey.GoBack)
BackRequested(s, null);
};
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
PackageVersion ver = Package.Current.Id.Version;
version.Text = $"v{ver.Major}{ver.Major}.{ver.Revision} (ci-id #{ver.Build})";
List<string> contributorsList = new List<string>();
try
{
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/xfox111/gutschedule/contributors");
request.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:72.0) Gecko/20100101 Firefox/72.0");
HttpResponseMessage response = await client.SendAsync(request);
string resposeContent = await response.Content.ReadAsStringAsync();
dynamic parsedResponse = JsonConvert.DeserializeObject(resposeContent);
foreach (var i in parsedResponse)
if (i.type == "User" && ((string)i.login).ToLower() != "xfox111")
contributorsList.Add((string)i.login);
request.Dispose();
client.Dispose();
}
finally
{
if (contributorsList.Count > 0)
{
foreach(string i in contributorsList)
{
Hyperlink link = new Hyperlink
{
NavigateUri = new Uri("https://github.com.i")
};
link.Inlines.Add(new Run
{
Text = "@" + i
});
contributors.Inlines.Add(link);
contributors.Inlines.Add(new Run
{
Text = ", "
});
}
contributors.Inlines.RemoveAt(contributors.Inlines.Count - 1);
contributorsTitle.Visibility = Visibility.Visible;
contributors.Visibility = Visibility.Visible;
}
}
}
private async void Feedback_Click(object sender, RoutedEventArgs e)
{
if (StoreServicesFeedbackLauncher.IsSupported())
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
else
await Launcher.LaunchUriAsync(new Uri("mailto:feedback@xfox111.net"));
}
private void BackRequested(object sender, RoutedEventArgs e) =>
Frame.GoBack();
}
}
+2 -1
View File
@@ -1,7 +1,8 @@
<Application
x:Class="GUTSchedule.UWP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
RequestedTheme="Light">
<Application.Resources>
<Style TargetType="Button">
+1 -3
View File
@@ -38,11 +38,9 @@ namespace GUTSchedule.UWP
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
// Do not repeat app initialization when the Window already has content,
// just ensure that the window is active
if (rootFrame == null)
if (!(Window.Current.Content is Frame rootFrame))
{
// Create a Frame to act as the navigation context and navigate to the first page
rootFrame = new Frame();
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

+45 -3
View File
@@ -1,17 +1,59 @@
using GUTSchedule.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using Windows.ApplicationModel.Appointments;
namespace GUTSchedule.UWP
{
public static class Calendar
{
public static void Export(List<Occupation> schedule)
public static async Task<IReadOnlyList<AppointmentCalendar>> GetCalendars()
{
AppointmentStore calendarStore = await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AllCalendarsReadWrite);
return await calendarStore.FindAppointmentCalendarsAsync();
}
public static async Task Export(List<Occupation> schedule, bool addGroupToTitle, int reminder, string calendar)
{
AppointmentStore calendarStore = await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AllCalendarsReadWrite);
AppointmentCalendar cal = await calendarStore.GetAppointmentCalendarAsync(calendar);
foreach (Occupation item in schedule)
{
Appointment appointment = new Appointment
{
BusyStatus = AppointmentBusyStatus.Busy,
Details = item.Opponent + "\xFEFF",
DetailsKind = AppointmentDetailsKind.PlainText,
Location = item.Cabinet,
Reminder = reminder < 0 ? (TimeSpan?)null : TimeSpan.FromMinutes(reminder),
Subject = string.Format("{0}.{1} {2} ({3})",
item.Order,
addGroupToTitle && !string.IsNullOrWhiteSpace(item.Group) ? $" [{item.Group}]" : "",
item.Name,
item.Type),
StartTime = item.StartTime,
Duration = item.EndTime.Subtract(item.StartTime)
};
await cal.SaveAppointmentAsync(appointment);
}
}
public static async Task Clear(bool keepPrevious = true)
{
AppointmentStore appointmentStore = await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AppCalendarsReadWrite);
List<Appointment> appointments = (await appointmentStore.FindAppointmentsAsync(keepPrevious ? DateTime.Today : DateTime.Today.Subtract(TimeSpan.FromDays(3000)), TimeSpan.FromDays(10000))).ToList();
foreach(Appointment i in appointments)
if (i.Details.Contains('\xFEFF'))
{
AppointmentCalendar cal = await appointmentStore.GetAppointmentCalendarAsync(i.CalendarId);
await cal.DeleteAppointmentAsync(i.LocalId);
}
}
}
}
@@ -134,13 +134,52 @@
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Assets\LargeTile.scale-100.png" />
<Content Include="Assets\LargeTile.scale-125.png" />
<Content Include="Assets\LargeTile.scale-150.png" />
<Content Include="Assets\LargeTile.scale-200.png" />
<Content Include="Assets\LargeTile.scale-400.png" />
<Content Include="Assets\SmallTile.scale-100.png" />
<Content Include="Assets\SmallTile.scale-125.png" />
<Content Include="Assets\SmallTile.scale-150.png" />
<Content Include="Assets\SmallTile.scale-200.png" />
<Content Include="Assets\SmallTile.scale-400.png" />
<Content Include="Assets\SplashScreen.scale-100.png" />
<Content Include="Assets\SplashScreen.scale-125.png" />
<Content Include="Assets\SplashScreen.scale-150.png" />
<Content Include="Assets\SplashScreen.scale-400.png" />
<Content Include="Assets\Square150x150Logo.scale-100.png" />
<Content Include="Assets\Square150x150Logo.scale-125.png" />
<Content Include="Assets\Square150x150Logo.scale-150.png" />
<Content Include="Assets\Square150x150Logo.scale-400.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-16.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-256.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-32.png" />
<Content Include="Assets\Square44x44Logo.altform-unplated_targetsize-48.png" />
<Content Include="Assets\Square44x44Logo.scale-100.png" />
<Content Include="Assets\Square44x44Logo.scale-125.png" />
<Content Include="Assets\Square44x44Logo.scale-150.png" />
<Content Include="Assets\Square44x44Logo.scale-400.png" />
<Content Include="Assets\Square44x44Logo.targetsize-16.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24.png" />
<Content Include="Assets\Square44x44Logo.targetsize-256.png" />
<Content Include="Assets\Square44x44Logo.targetsize-32.png" />
<Content Include="Assets\Square44x44Logo.targetsize-48.png" />
<Content Include="Assets\StoreLogo.scale-100.png" />
<Content Include="Assets\StoreLogo.scale-125.png" />
<Content Include="Assets\StoreLogo.scale-150.png" />
<Content Include="Assets\StoreLogo.scale-200.png" />
<Content Include="Assets\StoreLogo.scale-400.png" />
<Content Include="Assets\Wide310x150Logo.scale-100.png" />
<Content Include="Assets\Wide310x150Logo.scale-125.png" />
<Content Include="Assets\Wide310x150Logo.scale-150.png" />
<Content Include="Assets\Wide310x150Logo.scale-400.png" />
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
@@ -161,6 +200,12 @@
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.9</Version>
</PackageReference>
<PackageReference Include="Microsoft.Services.Store.Engagement">
<Version>10.1901.28001</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>12.0.3</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GUTSchedule\GUTSchedule.csproj">
@@ -168,6 +213,17 @@
<Name>GUTSchedule</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PRIResource Include="Strings\en-US\Resources.resw" />
</ItemGroup>
<ItemGroup>
<SDKReference Include="Microsoft.Services.Store.Engagement, Version=10.0">
<Name>Microsoft Engagement Framework</Name>
</SDKReference>
<SDKReference Include="Microsoft.VCLibs, Version=14.0">
<Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name>
</SDKReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
+17 -15
View File
@@ -5,13 +5,14 @@
xmlns:local="using:GUTSchedule.UWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
mc:Ignorable="d"
Loaded="Page_Loaded">
<Page.Background>
<ImageBrush ImageSource="https://cabs.itut.ru/cabinet/ini/general/0/cabinet/img/bg_n.jpg" Stretch="UniformToFill"/>
</Page.Background>
<VisualStateManager.VisualStateGroups>
<!--<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
@@ -22,7 +23,7 @@
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</VisualStateManager.VisualStateGroups>-->
<Page.TopAppBar>
<CommandBar ClosedDisplayMode="Compact" Background="{StaticResource SystemAccentColor}" RequestedTheme="Dark">
@@ -55,21 +56,21 @@
<StackPanel Padding="10" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Schedule parameters"/>
<CheckBox Content="Authorize via personal cabinet" Checked="ChangeAuthorizationMethod" IsChecked="True" x:Name="authorize"/>
<CheckBox Content="Authorize via personal cabinet" Checked="ChangeAuthorizationMethod" Unchecked="ChangeAuthorizationMethod" IsChecked="True" x:Name="authorize"/>
<StackPanel x:Name="credentialMethod">
<TextBox PlaceholderText="E-mail" x:Name="email"/>
<PasswordBox PlaceholderText="Password" x:Name="password"/>
<CheckBox Content="Remember" x:Name="rememberCredential" IsChecked="True"/>
<CheckBox Content="Remember" x:Name="rememberCredential" Checked="RememberCredential_Checked" Unchecked="RememberCredential_Checked"/>
</StackPanel>
<StackPanel x:Name="defaultMethod" Visibility="Collapsed">
<ComboBox x:Name="faculty" PlaceholderText="No schedule is available" Header="Course" SelectionChanged="Faculty_SelectionChanged"/>
<ComboBox x:Name="course" SelectedIndex="0" Header="Course" SelectionChanged="Course_SelectionChanged">
<ComboBox x:Name="course" Header="Course" SelectionChanged="Course_SelectionChanged">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
<ComboBoxItem Content="3"/>
<ComboBoxItem Content="4"/>
</ComboBox>
<ComboBox x:Name="group" PlaceholderText="No schedule is available" Header="Group"/>
<ComboBox x:Name="group" PlaceholderText="No schedule is available" Header="Group" SelectionChanged="Group_SelectionChanged"/>
</StackPanel>
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Export parameters"/>
@@ -96,21 +97,22 @@
</StackPanel>
</Grid>
<ComboBox Header="Set reminder for:" x:Name="reminder" SelectedIndex="0">
<ComboBoxItem Content="None"/>
<ComboBoxItem Content="None"/>
<ComboBox Header="Set reminder for:" x:Name="reminder" SelectionChanged="Reminder_SelectionChanged">
<ComboBoxItem Content="None"/>
<ComboBoxItem Content="At the begining of event"/>
<ComboBoxItem Content="5 minutes"/>
<ComboBoxItem Content="10 minutes"/>
</ComboBox>
<CheckBox Content="Add group number to event title" x:Name="addGroupToTitle"/>
<CheckBox Content="Add group number to event title" x:Name="addGroupToTitle" Checked="AddGroupToTitle_Checked" Unchecked="AddGroupToTitle_Checked"/>
<ComboBox Header="Destination calendar" x:Name="calendar" SelectedIndex="0" PlaceholderText="No calendar is available"/>
<ComboBox Header="Destination calendar" x:Name="calendar" SelectionChanged="Calendar_SelectionChanged"/>
<TextBlock Foreground="Red" Visibility="Collapsed" Text="Error" x:Name="errorPlaceholder"/>
<Button Content="Add schedule" Background="{StaticResource SystemAccentColor}" RequestedTheme="Dark" FontWeight="Bold" Margin="0,10" Click="Export"/>
<Button Content="Add schedule" Background="{StaticResource SystemAccentColor}" Foreground="White" FontWeight="Bold" Margin="0,10" Click="Export"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="©2020 Michael Gordeev, INS, ICT-907"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="v$(Build.BuildNumber) (ci-id #$(Build.BuildId))"/>
<TextBlock Style="{StaticResource CaptionTextBlockStyle}" Text="v$(Build.BuildNumber) (ci-id #$(Build.BuildId))" x:Name="version"/>
</StackPanel>
</Grid>
</ScrollViewer>
@@ -118,7 +120,7 @@
<Grid Background="{StaticResource SystemAccentColor}" x:Name="loading" Visibility="Collapsed">
<StackPanel VerticalAlignment="Center">
<ProgressRing Width="100" Height="100" Foreground="White" IsActive="True" HorizontalAlignment="Center"/>
<TextBlock Style="{StaticResource TitleTextBlockStyle}" x:Name="status" Text="Processing..." HorizontalAlignment="Center" Foreground="White"/>
<TextBlock Style="{StaticResource TitleTextBlockStyle}" x:Name="status" Text="Processing..." HorizontalAlignment="Center" Foreground="White" Margin="0,20"/>
</StackPanel>
</Grid>
</Grid>
+152 -54
View File
@@ -11,47 +11,75 @@ using Windows.UI.Popups;
using System.Threading.Tasks;
using Windows.ApplicationModel.Resources;
using Windows.Storage;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
using Microsoft.Services.Store.Engagement;
using Windows.ApplicationModel;
namespace GUTSchedule.UWP
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private readonly PasswordVault vault = new PasswordVault();
private readonly ResourceLoader resources = ResourceLoader.GetForCurrentView();
static readonly ApplicationDataContainer settings = ApplicationData.Current.LocalSettings;
public static List<(string id, string name)> Faculties { get; set; }
public MainPage()
{
public MainPage() =>
InitializeComponent();
startDate.Date = DateTime.Today;
endDate.Date = startDate.Date.Value.AddDays(6);
if (vault.FindAllByResource("xfox111.gutschedule") is IReadOnlyList<PasswordCredential> credentials && credentials.Count > 0)
private async void Page_Loaded(object sender, RoutedEventArgs args)
{
try
{
email.Text = credentials.First().UserName;
credentials.First().RetrievePassword();
password.Password = credentials.First().Password;
PackageVersion ver = Package.Current.Id.Version;
version.Text = $"v{ver.Major}{ver.Major}.{ver.Revision} (ci-id #{ver.Build})";
authorize.IsChecked = (bool?)settings.Values["Authorize"] ?? true;
if (vault.RetrieveAll() is IReadOnlyList<PasswordCredential> credentials && credentials.Count > 0)
{
email.Text = credentials.First().UserName;
credentials.First().RetrievePassword();
password.Password = credentials.First().Password;
}
rememberCredential.IsChecked = (bool?)settings.Values["RememberCredential"] ?? true;
faculty.ItemsSource = (await Parser.GetFaculties()).Select(i => new ComboBoxItem
{
Content = i.name,
Tag = i.id,
IsSelected = (string)settings.Values["Faculty"] == i.id
}).ToList();
faculty.SelectedIndex = (faculty.ItemsSource as List<ComboBoxItem>).FindIndex(i => i.IsSelected);
if (faculty.SelectedIndex < 0)
faculty.SelectedIndex = 0;
course.SelectedIndex = (int?)settings.Values["Course"] ?? 0;
startDate.Date = DateTime.Today;
endDate.Date = startDate.Date.Value.AddDays(6);
reminder.SelectedIndex = (int?)settings.Values["Reminder"] ?? 2;
addGroupToTitle.IsChecked = (bool?)settings.Values["AddGroupToTitle"] ?? false;
calendar.ItemsSource = (await Calendar.GetCalendars()).Select(i => new ComboBoxItem
{
Content = i.DisplayName,
Tag = i.LocalId,
IsSelected = (string)settings.Values["Calendar"] == i.LocalId
}).ToList();
calendar.SelectedIndex = (calendar.ItemsSource as List<ComboBoxItem>).FindIndex(i => i.IsSelected);
if (calendar.SelectedIndex < 0)
calendar.SelectedIndex = 0;
}
authorize.IsChecked = (bool?)settings.Values["Authorize"] ?? true;
rememberCredential.IsChecked = (bool?)settings.Values["RememberCredential"] ?? true;
reminder.SelectedIndex = (int?)settings.Values["Reminder"] ?? 1;
addGroupToTitle.IsChecked = (bool?)settings.Values["AddGroupToTitle"] ?? false;
faculty.ItemsSource = Faculties.Select(i => new ComboBoxItem
catch (HttpRequestException e)
{
Content = i.name,
Tag = i.id
});
faculty.SelectedIndex = (int?)settings.Values["Faculty"] ?? 0;
course.SelectedIndex = (int?)settings.Values["Course"] ?? 0;
PushInternetExceptionMessage(e, () => Page_Loaded(sender, args));
return;
}
catch (Exception e)
{
MessageDialog dialog = new MessageDialog(e.Message, e.GetType().ToString());
dialog.Commands.Add(new UICommand("OK", (command) => loading.Visibility = Visibility.Collapsed));
await dialog.ShowAsync();
}
}
private async void AppBarButton_Click(object sender, RoutedEventArgs e)
@@ -59,35 +87,52 @@ namespace GUTSchedule.UWP
switch (((FrameworkElement)sender).Tag)
{
case "clear":
MessageDialog dialog = new MessageDialog(resources.GetString("clearScheduleMessage"), resources.GetString("clearScheduleTitle"));
dialog.Commands.Add(new UICommand(resources.GetString("clearUpcomingOption"), (command) => Clear()));
dialog.Commands.Add(new UICommand(resources.GetString("clearAllOption"), (command) => Clear(true)));
dialog.Commands.Add(new UICommand(resources.GetString("cancelOption")));
dialog.CancelCommandIndex = 2;
dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync();
break;
case "about":
Frame.Navigate(typeof(AboutPage));
break;
case "report":
await Launcher.LaunchUriAsync(new Uri("mailto:feedback@xfox111.net"));
if (StoreServicesFeedbackLauncher.IsSupported())
await StoreServicesFeedbackLauncher.GetDefault().LaunchAsync();
else
await Launcher.LaunchUriAsync(new Uri("mailto:feedback@xfox111.net"));
break;
}
}
private void ChangeAuthorizationMethod(object sender, RoutedEventArgs e)
{
if (credentialMethod == null)
return;
if (authorize.IsChecked.Value)
{
credentialMethod.Visibility = Visibility.Visible;
credentialMethod.Visibility = Visibility.Collapsed;
defaultMethod.Visibility = Visibility.Collapsed;
}
else
{
credentialMethod.Visibility = Visibility.Collapsed;
credentialMethod.Visibility = Visibility.Visible;
defaultMethod.Visibility = Visibility.Visible;
}
settings.Values["Authorize"] = authorize.IsChecked;
}
private void SetTodayDate(object sender, RoutedEventArgs e) =>
startDate.Date = DateTime.Today;
private void SetEndDate(object sender, RoutedEventArgs e) =>
endDate.Date = startDate.Date.Value.AddDays((int)((FrameworkElement)sender).Tag);
endDate.Date = startDate.Date.Value.AddDays(int.Parse(((FrameworkElement)sender).Tag as string));
private void SetForSemester(object sender, RoutedEventArgs e)
{
@@ -130,9 +175,14 @@ namespace GUTSchedule.UWP
};
if (rememberCredential.IsChecked.Value)
vault.Add(new PasswordCredential("xfox111.gutschedule", email.Text, password.Password));
vault.Add(new PasswordCredential
{
UserName = email.Text,
Password = password.Password,
Resource = "xfox111.gutschedule"
});
else
foreach (PasswordCredential credential in vault.FindAllByResource("xfox111.gutschedule"))
foreach (PasswordCredential credential in vault.RetrieveAll())
vault.Remove(credential);
}
else
@@ -154,33 +204,22 @@ namespace GUTSchedule.UWP
};
}
AddGroupToTitle = addGroupToTitle.IsChecked;
SelectedCalendarIndex = calendar.SelectedItemPosition;
Reminder = (reminder.SelectedIndex - 1) * 5;
loading.Visibility = Visibility.Visible;
TopAppBar.Visibility = Visibility.Collapsed;
try
{
status.Text = resources.GetString("loadingStatus");
List<Occupation> schedule = await Parser.GetSchedule(exportParameters);
status.Text = resources.GetString("calendarExportStatus");
Calendar.Export(schedule);
await Calendar.Export(schedule, addGroupToTitle.IsChecked.Value, (reminder.SelectedIndex - 1) * 5, ((ComboBoxItem)calendar.SelectedItem).Tag as string);
status.Text = resources.GetString("doneStatus");
await Task.Delay(1000);
}
catch (HttpRequestException e)
{
MessageDialog dialog = new MessageDialog(resources.GetString("connectionFailMessage"), e.Message);
dialog.Commands.Add(new UICommand(resources.GetString("repeat"), (command) => Export(sender, args)));
dialog.Commands.Add(new UICommand("OK", (command) => loading.Visibility = Visibility.Collapsed));
dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync();
PushInternetExceptionMessage(e, () => Export(sender, args));
return;
}
catch (Exception e)
@@ -192,28 +231,87 @@ namespace GUTSchedule.UWP
}
loading.Visibility = Visibility.Collapsed;
TopAppBar.Visibility = Visibility.Visible;
}
private async void Clear(bool keepPrevious = true)
{
try
{
await Calendar.Clear(keepPrevious);
MessageDialog dialog = new MessageDialog(resources.GetString("clearScheduleDone"), resources.GetString("clearScheduleTitle"));
dialog.Commands.Add(new UICommand("OK", (command) => loading.Visibility = Visibility.Collapsed));
await dialog.ShowAsync();
}
catch (Exception e)
{
MessageDialog dialog = new MessageDialog(e.Message, e.GetType().ToString());
dialog.Commands.Add(new UICommand("OK", (command) => loading.Visibility = Visibility.Collapsed));
await dialog.ShowAsync();
}
}
private void Faculty_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateGroupsList();
settings.Values["Faculty"] = ((ComboBoxItem)faculty.SelectedItem).Tag;
}
private void Course_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateGroupsList();
settings.Values["Course"] = course.SelectedIndex;
}
private void Reminder_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
settings.Values["Reminder"] = reminder.SelectedIndex;
private void Calendar_SelectionChanged(object sender, SelectionChangedEventArgs e) =>
settings.Values["Calendar"] = (calendar.SelectedItem as ComboBoxItem).Tag;
private void Group_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(group.SelectedItem != null)
settings.Values["Group"] = ((ComboBoxItem)group.SelectedItem).Tag;
}
private void RememberCredential_Checked(object sender, RoutedEventArgs e) =>
settings.Values["RememberCredential"] = rememberCredential.IsChecked;
private void AddGroupToTitle_Checked(object sender, RoutedEventArgs e) =>
settings.Values["AddGroupToTitle"] = rememberCredential.IsChecked;
private async void UpdateGroupsList()
{
List<(string id, string name)> groups = await Parser.GetGroups(Faculties[faculty.SelectedIndex].id, (course.SelectedIndex + 1).ToString());
List<(string id, string name)> groups = await Parser.GetGroups(((ComboBoxItem)faculty.SelectedItem).Tag as string, (course.SelectedIndex + 1).ToString());
group.ItemsSource = groups.Select(i => new ComboBoxItem
{
Content = i.name,
Tag = i.id
});
Tag = i.id,
IsSelected = (string)settings.Values["Group"] == i.id
}).ToList();
group.SelectedIndex = (group.ItemsSource as List<ComboBoxItem>).FindIndex(i => i.IsSelected);
if (group.SelectedIndex < 0)
group.SelectedIndex = 0;
}
group.SelectedIndex = (int?)settings.Values["Group"] ?? 0;
public async void PushInternetExceptionMessage(HttpRequestException e, Action retryAction)
{
MessageDialog dialog = new MessageDialog(resources.GetString("connectionFailMessage"), e.Message);
dialog.Commands.Add(new UICommand(resources.GetString("repeat"), (command) => retryAction()));
dialog.Commands.Add(new UICommand("OK", (command) => loading.Visibility = Visibility.Collapsed));
dialog.CancelCommandIndex = 1;
dialog.DefaultCommandIndex = 0;
await dialog.ShowAsync();
}
}
}
}
// TODO: Reminder prefs broken
// TODO: Calendar prefs broken
// TODO: Faculty prefs broken
@@ -4,47 +4,49 @@
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp">
<Identity
<Identity
Name="3f032359-f68f-4a46-b88c-113efc87b82b"
Publisher="CN=Michael Gordeev"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="3f032359-f68f-4a46-b88c-113efc87b82b" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<mp:PhoneIdentity PhoneProductId="3f032359-f68f-4a46-b88c-113efc87b82b" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>GUT.Schedule.UWP</DisplayName>
<PublisherDisplayName>Michael &quot;XFox&quot; Gordeev</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Properties>
<DisplayName>GUT.Schedule.UWP</DisplayName>
<PublisherDisplayName>Michael &quot;XFox&quot; Gordeev</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="GUT.Schedule.UWP.App">
<uap:VisualElements
<uap:VisualElements
DisplayName="GUT.Schedule"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="Application which exports SPbSUT timetable to calendar"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
BackgroundColor="#FF8000">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" Square71x71Logo="Assets\SmallTile.png" Square310x310Logo="Assets\LargeTile.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" BackgroundColor="#FF8000"/>
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<uap:Capability Name="appointments"/>
</Capabilities>
<Capabilities>
<Capability Name="internetClient" />
<uap:Capability Name="appointments"/>
<rescap:Capability Name="appointmentSystem"/>
</Capabilities>
</Package>
@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="calendarExportStatus" xml:space="preserve">
<value>calendarExportStatus</value>
</data>
<data name="cancelOption" xml:space="preserve">
<value>cancelOption</value>
</data>
<data name="clearAllOption" xml:space="preserve">
<value>clearAllOption</value>
</data>
<data name="clearScheduleDone" xml:space="preserve">
<value>clearScheduleDone</value>
</data>
<data name="clearScheduleMessage" xml:space="preserve">
<value>clearScheduleMessage</value>
</data>
<data name="clearScheduleTitle" xml:space="preserve">
<value>clearScheduleTitle</value>
</data>
<data name="clearUpcomingOption" xml:space="preserve">
<value>clearUpcomingOption</value>
</data>
<data name="connectionFailMessage" xml:space="preserve">
<value>connectionFailMessage</value>
</data>
<data name="doneStatus" xml:space="preserve">
<value>doneStatus</value>
</data>
<data name="groupSelectionError" xml:space="preserve">
<value>groupSelectionError</value>
</data>
<data name="invalidAuthorizationError" xml:space="preserve">
<value>invalidAuthorizationError</value>
</data>
<data name="invalidDateRangeError" xml:space="preserve">
<value>invalidDateRangeError</value>
</data>
<data name="loadingStatus" xml:space="preserve">
<value>loadingStatus</value>
</data>
<data name="repeat" xml:space="preserve">
<value>repeat</value>
</data>
</root>
+2 -2
View File
@@ -85,7 +85,7 @@ namespace GUTSchedule
schedule.RemoveAt(k--);
}
schedule = schedule.FindAll(i => i.StartTime.Date >= exportParameters.StartDate && i.StartTime.Date <= exportParameters.EndDate);
schedule = schedule.FindAll(i => i.StartTime.Date >= exportParameters.StartDate.Date && i.EndTime.Date <= exportParameters.EndDate.Date);
if (schedule.Count < 1)
throw new NullReferenceException("Не удалось найти расписание соответствующее критериям. Ничего не экспортировано");
@@ -163,7 +163,7 @@ namespace GUTSchedule
DateTime date = new DateTime(DateTime.Today.Year, DateTime.Today.Month >= 8 ? 9 : 2, offsetDay);
date = date.AddDays(--week * 7);
date = date.AddDays(--weekday);
date = date.AddDays(weekday - 1);
dates.Add(date);
}