diff --git a/GUT.Schedule/GUT.Schedule/Calendar.cs b/GUT.Schedule/GUT.Schedule/Calendar.cs index 384bb6f..1657cb1 100644 --- a/GUT.Schedule/GUT.Schedule/Calendar.cs +++ b/GUT.Schedule/GUT.Schedule/Calendar.cs @@ -1,8 +1,11 @@ using System.Collections.Generic; using Android.App; +using Android.Content; using Android.Database; +using Android.Net; using Android.Provider; using Android.Support.V4.Content; +using Java.Util; namespace GUT.Schedule { @@ -21,7 +24,7 @@ namespace GUT.Schedule CalendarContract.Calendars.InterfaceConsts.AccountType, }; - using CursorLoader loader = new CursorLoader(Application.Context, calendarsUri, calendarsProjection, null, null, null); + using Android.Support.V4.Content.CursorLoader loader = new Android.Support.V4.Content.CursorLoader(Application.Context, calendarsUri, calendarsProjection, null, null, null); ICursor cursor = (ICursor)loader.LoadInBackground(); cursor.MoveToNext(); @@ -32,5 +35,44 @@ namespace GUT.Schedule cursor.MoveToNext(); } } + + public static void Export(string calendarId, IEnumerable schedule, int? remindBefore, bool addGroupToTitle) + { + foreach (Subject item in schedule) + AddEvent(calendarId, item, remindBefore, addGroupToTitle); + } + + static void AddEvent(string calendarId, Subject subject, int? reminderMinutes, bool addHeader) + { + ContentValues eventValues = new ContentValues(); + + eventValues.Put(CalendarContract.Events.InterfaceConsts.CalendarId, calendarId); + eventValues.Put(CalendarContract.Events.InterfaceConsts.Title, $"{subject.Order}.{(addHeader ? $" [{subject.Group}]" : "")} {subject.Name} ({subject.Type})"); + eventValues.Put(CalendarContract.Events.InterfaceConsts.Description, subject.Professor); + eventValues.Put(CalendarContract.Events.InterfaceConsts.EventLocation, string.Join(';', subject.Cabinets)); + + eventValues.Put(CalendarContract.Events.InterfaceConsts.Availability, 0); + + if(reminderMinutes.HasValue) + eventValues.Put(CalendarContract.Events.InterfaceConsts.HasAlarm, true); + + eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtstart, subject.StartTime.ToUnixTime()); + eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtend, Extensions.ToUnixTime(subject.EndTime)); + + eventValues.Put(CalendarContract.Events.InterfaceConsts.EventTimezone, TimeZone.Default.ID); + eventValues.Put(CalendarContract.Events.InterfaceConsts.EventEndTimezone, TimeZone.Default.ID); + + Uri response = Application.Context.ContentResolver.Insert(CalendarContract.Events.ContentUri, eventValues); + + if (reminderMinutes.HasValue) + { + ContentValues reminderValues = new ContentValues(); + reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.EventId, long.Parse(response.LastPathSegment)); + reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.Method, 1); + reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.Minutes, reminderMinutes.Value); + + Application.Context.ContentResolver.Insert(CalendarContract.Reminders.ContentUri, reminderValues); + } + } } } \ No newline at end of file diff --git a/GUT.Schedule/GUT.Schedule/Data.cs b/GUT.Schedule/GUT.Schedule/Data.cs index 8c7ddfd..94e6ec0 100644 --- a/GUT.Schedule/GUT.Schedule/Data.cs +++ b/GUT.Schedule/GUT.Schedule/Data.cs @@ -8,9 +8,16 @@ namespace GUT.Schedule public static List<(string Id, string Name)> Faculties { get; set; } public static List<(string Id, string Name)> Groups { get; set; } public static List<(string Id, string Name)> Calendars { get; set; } + public static List Schedule { get; set; } + public static int FirstWeekDay { get; set; } public static DateTime StartDate { get; set; } = DateTime.Today; public static DateTime EndDate { get; set; } = DateTime.Today.AddDays(7); - public static (int faculty, int group, int calendar, int reminder) ExportData { get; set; } + public static int Faculty { get; set; } + public static int Group { get; set; } + public static int Course { get; set; } + public static int Calendar { get; set; } + public static int Reminder { get; set; } + public static bool AddTitle { get; set; } } } \ No newline at end of file diff --git a/GUT.Schedule/GUT.Schedule/ExportActivity.cs b/GUT.Schedule/GUT.Schedule/ExportActivity.cs index 6370cd9..46dda66 100644 --- a/GUT.Schedule/GUT.Schedule/ExportActivity.cs +++ b/GUT.Schedule/GUT.Schedule/ExportActivity.cs @@ -1,5 +1,6 @@ using Android.App; using Android.OS; +using Android.Widget; using System.Threading.Tasks; namespace GUT.Schedule @@ -7,13 +8,30 @@ namespace GUT.Schedule [Activity(Theme = "@style/AppTheme.NoActionBar")] public class ExportActivity : Activity { + TextView status; protected override async void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource.Layout.export_progress); - await Task.Delay(5000); + status = FindViewById(Resource.Id.status); + status.Text = "Загрузка расписания"; + await Parser.LoadSchedule(); + + status.Text = "Экспортирование в календарь"; + int minutes = Data.Reminder switch + { + 1 => 0, + 2 => 5, + 3 => 10, + _ => -1 + }; + Calendar.Export(Data.Calendars[Data.Calendar].Id, Data.Schedule, minutes < 0 ? (int?)null : minutes, Data.AddTitle); + + status.Text = "Готово"; + + await Task.Delay(3000); base.OnBackPressed(); } diff --git a/GUT.Schedule/GUT.Schedule/Extensions.cs b/GUT.Schedule/GUT.Schedule/Extensions.cs index 93eb589..3483b87 100644 --- a/GUT.Schedule/GUT.Schedule/Extensions.cs +++ b/GUT.Schedule/GUT.Schedule/Extensions.cs @@ -1,13 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; - -using Android.App; using Android.Content; -using Android.OS; -using Android.Runtime; -using Android.Views; using Android.Widget; namespace GUT.Schedule @@ -19,5 +13,16 @@ namespace GUT.Schedule ArrayAdapter adapter = new ArrayAdapter(context, Resource.Layout.support_simple_spinner_dropdown_item, array.ToList()); spinner.Adapter = adapter; } + public static DateTime GetDateFromWeeks(int week, int weekday) + { + DateTime dt = new DateTime(DateTime.Today.Year, 9, Data.FirstWeekDay); + + dt = dt.AddDays(--week * 7); + dt = dt.AddDays(--weekday); + + return dt; + } + public static long ToUnixTime(this DateTime dt) => + (long)dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; } } \ No newline at end of file diff --git a/GUT.Schedule/GUT.Schedule/GUT.Schedule.csproj b/GUT.Schedule/GUT.Schedule/GUT.Schedule.csproj index 4fee7e4..41bd59f 100644 --- a/GUT.Schedule/GUT.Schedule/GUT.Schedule.csproj +++ b/GUT.Schedule/GUT.Schedule/GUT.Schedule.csproj @@ -69,6 +69,7 @@ + @@ -104,6 +105,9 @@ + + 0.13.0 + 4.3.4 diff --git a/GUT.Schedule/GUT.Schedule/MainActivity.cs b/GUT.Schedule/GUT.Schedule/MainActivity.cs index 8ec1f37..d04b49e 100644 --- a/GUT.Schedule/GUT.Schedule/MainActivity.cs +++ b/GUT.Schedule/GUT.Schedule/MainActivity.cs @@ -3,7 +3,6 @@ using System.Linq; using Android.App; using Android.Content; using Android.OS; -using Android.Support.Design.Widget; using Android.Support.V7.App; using Android.Views; using Android.Widget; @@ -70,6 +69,11 @@ namespace GUT.Schedule error.Visibility = ViewStates.Visible; } + Data.Faculty = faculty.SelectedItemPosition; + Data.Group = group.SelectedItemPosition; + Data.Course = course.SelectedItemPosition + 1; + Data.Reminder = reminder.SelectedItemPosition; + Data.AddTitle = groupTitle.Checked; StartActivity(new Intent(this, typeof(ExportActivity))); } @@ -113,6 +117,7 @@ namespace GUT.Schedule calendar = FindViewById(Resource.Id.calendar); error = FindViewById(Resource.Id.error); + groupTitle = FindViewById(Resource.Id.groupTitle); } private void AddEvents() diff --git a/GUT.Schedule/GUT.Schedule/Parser.cs b/GUT.Schedule/GUT.Schedule/Parser.cs index c783edd..dc1e845 100644 --- a/GUT.Schedule/GUT.Schedule/Parser.cs +++ b/GUT.Schedule/GUT.Schedule/Parser.cs @@ -1,5 +1,9 @@ -using System; +using AngleSharp.Dom; +using AngleSharp.Html.Dom; +using AngleSharp.Html.Parser; +using System; using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; @@ -8,6 +12,52 @@ namespace GUT.Schedule { public static class Parser { + public static async Task LoadSchedule() + { + Data.Schedule = new List(); + using HttpClient client = new HttpClient(); + Dictionary requestBody = new Dictionary + { + { "group_el", "0" }, + { "kurs", Data.Course.ToString() }, + { "type_z", "1" }, + { "faculty", Data.Faculties[Data.Faculty].Id }, + { "group", Data.Groups[Data.Group].Id }, + { "ok", "Показать" }, + { "schet", GetCurrentSemester() } + }; + HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://cabinet.sut.ru/raspisanie_all_new") + { + Content = new FormUrlEncodedContent(requestBody) + }; + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded"); + + HttpResponseMessage response = await client.SendAsync(request); + string responseBody = await response.Content.ReadAsStringAsync(); + + HtmlParser parser = new HtmlParser(); + IHtmlDocument doc = parser.ParseDocument(responseBody); + + string groupName = doc.QuerySelectorAll("#group option").FirstOrDefault(i => i.HasAttribute("selected")).TextContent; + + IHtmlCollection pairs = doc.QuerySelectorAll(".pair"); + foreach (IElement item in pairs) + { + string name, type, professor, place; + int order, weekday; + string[] weeks; + + name = item.QuerySelector(".subect strong")?.TextContent ?? "Неизвестный предмет (см. Расписание)"; + type = item.QuerySelector(".type").TextContent.Replace("(", "").Replace(")", ""); + professor = item.QuerySelector(".teacher")?.GetAttribute("title") ?? ""; + place = item.QuerySelector(".aud")?.TextContent ?? "СПбГУТ"; + order = int.Parse(item.GetAttribute("pair") ?? "2") - 1; + weeks = item.QuerySelector(".weeks").TextContent.Replace("(", "").Replace("н)", "").Split(", "); + weekday = int.Parse(item.GetAttribute("weekday")); + Data.Schedule.AddRange(Subject.GetSubject(name, type, professor, place, order, weeks, weekday, groupName)); + } + } + public static async Task LoadFaculties() { Data.Faculties = new List<(string, string)>(); diff --git a/GUT.Schedule/GUT.Schedule/Resources/layout/export_progress.xml b/GUT.Schedule/GUT.Schedule/Resources/layout/export_progress.xml index aeefc36..c7106fa 100644 --- a/GUT.Schedule/GUT.Schedule/Resources/layout/export_progress.xml +++ b/GUT.Schedule/GUT.Schedule/Resources/layout/export_progress.xml @@ -18,6 +18,7 @@ android:layout_margin="20dp"/> GetSubject(string name, string type, string professor, string place, int order, string[] weeks, int weekday, string group) + { + List subjects = new List(); + string[] cabinets = place.Replace("ауд.: ", "").Replace("; Б22", "").Split(';'); + string pair = order < 10 ? order.ToString() : $"Ф{order - 81}"; + + foreach (string week in weeks) + subjects.Add(new Subject(name, type, professor, cabinets, pair, int.Parse(week), weekday, group)); + + return subjects; + } + + public Subject(string name, string type, string prof, string[] cabs, string order, int week, int weekday, string group) + { + Name = name; + Type = type; + Professor = prof; + Cabinets = cabs; + Order = order; + Group = group; + + StartTime = Extensions.GetDateFromWeeks(week, weekday); + StartTime = StartTime.Add(TimeSpan.Parse(order switch + { + "1" => "9:00", + "2" => "10:45", + "3" => "13:00", + "4" => "14:45", + "5" => "16:30", + "6" => "18:15", + "7" => "20:00", + "Ф1" => "9:00", //Расписание для пар по физ-ре + "Ф2" => "10:30", + "Ф3" => "12:00", + "Ф4" => "13:30", + "Ф5" => "15:00", + "Ф6" => "16:30", + "Ф7" => "18:00", + _ => "9:00" + })); + EndTime = StartTime + TimeSpan.FromMinutes(order.Contains("Ф") ? 90 : 95); + } + } +} \ No newline at end of file