Splitted and added UWP project
@@ -0,0 +1,86 @@
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Support.V4.Text;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Text.Method;
|
||||
using Android.Widget;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace GUTSchedule.Droid.Activities
|
||||
{
|
||||
[Activity(Label = "@string/aboutTitle")]
|
||||
public class AboutActivity : AppCompatActivity
|
||||
{
|
||||
protected override async void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
(string name, string handle, string link)[] contacts = new (string, string, string)[]
|
||||
{
|
||||
(Resources.GetText(Resource.String.websiteContact), "https://xfox111.net", "https://xfox111.net"),
|
||||
(Resources.GetText(Resource.String.twitterContact), "@xfox111", "https://twitter.com/xfox111"),
|
||||
(Resources.GetText(Resource.String.vkontakteContact), "@xfox.mike", "https://vk.com/xfox.mike"),
|
||||
("LinkedIn", "@xfox", "https://linkedin.com/in/xfox"),
|
||||
("GitHub", "@xfox111", "https://github.com/xfox111"),
|
||||
};
|
||||
(string name, string link)[] links = new (string, string)[]
|
||||
{
|
||||
(Resources.GetText(Resource.String.privacyPolicyLink), "https://xfox111.net/Projects/GUTSchedule/PrivacyPolicy.txt"),
|
||||
("General Public License v3", "https://www.gnu.org/licenses/gpl-3.0"),
|
||||
(Resources.GetText(Resource.String.repositoryLink), "https://github.com/xfox111/gutschedule"),
|
||||
(Resources.GetText(Resource.String.notsLink), "http://tios.spbgut.ru/index.php"),
|
||||
(Resources.GetText(Resource.String.sutLink), "https://sut.ru"),
|
||||
};
|
||||
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetContentView(Resource.Layout.About);
|
||||
PackageInfo version = PackageManager.GetPackageInfo(PackageName, PackageInfoFlags.MatchAll);
|
||||
FindViewById<TextView>(Resource.Id.version).Text = $"v{version.VersionName} (ci-id #{version.VersionCode})";
|
||||
|
||||
FindViewById<Button>(Resource.Id.feedback).Click += (s, e) =>
|
||||
StartActivity(new Intent(Intent.ActionView, Android.Net.Uri.Parse("mailto:feedback@xfox111.net")));
|
||||
|
||||
FindViewById<TextView>(Resource.Id.contacts).SetText(
|
||||
HtmlCompat.FromHtml(string.Join("<br />", contacts.Select(i => $"<span>{i.name}:</span> <a href=\"{i.link}\">{i.handle}</a>")), HtmlCompat.FromHtmlModeLegacy),
|
||||
TextView.BufferType.Normal);
|
||||
FindViewById<TextView>(Resource.Id.contacts).MovementMethod = LinkMovementMethod.Instance;
|
||||
|
||||
FindViewById<TextView>(Resource.Id.links).SetText(
|
||||
HtmlCompat.FromHtml(string.Join("<br />", links.Select(i => $"<a href=\"{i.link}\">{i.name}</a>")), HtmlCompat.FromHtmlModeLegacy),
|
||||
TextView.BufferType.Normal);
|
||||
FindViewById<TextView>(Resource.Id.links).MovementMethod = LinkMovementMethod.Instance;
|
||||
|
||||
List<string> contributors = new List<string>();
|
||||
try
|
||||
{
|
||||
using HttpClient client = new HttpClient();
|
||||
using 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")
|
||||
contributors.Add((string)i.login);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (contributors.Count > 0)
|
||||
{
|
||||
FindViewById<TextView>(Resource.Id.contributors).SetText(
|
||||
HtmlCompat.FromHtml(string.Join(", ", contributors.Select(i => $"<a href=\"https://github.com/{i}\">@{i}</a>")), HtmlCompat.FromHtmlModeLegacy),
|
||||
TextView.BufferType.Normal);
|
||||
FindViewById<TextView>(Resource.Id.contributors).MovementMethod = LinkMovementMethod.Instance;
|
||||
|
||||
FindViewById<TextView>(Resource.Id.contributorsTitle).Visibility = Android.Views.ViewStates.Visible;
|
||||
FindViewById<TextView>(Resource.Id.contributors).Visibility = Android.Views.ViewStates.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using GUTSchedule.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GUTSchedule.Droid.Activities
|
||||
{
|
||||
/// <summary>
|
||||
/// Shows status of schedule export process
|
||||
/// </summary>
|
||||
[Activity(Theme = "@style/AppTheme.Light.SplashScreen")]
|
||||
public class ExportActivity : Activity
|
||||
{
|
||||
TextView status;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetContentView(Resource.Layout.Export);
|
||||
|
||||
status = FindViewById<TextView>(Resource.Id.status);
|
||||
|
||||
Export();
|
||||
}
|
||||
|
||||
private async void Export()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Data.DataSet.IsProfessor == true)
|
||||
status.Text = Resources.GetText(Resource.String.potatoLoadingStatus); // For some reason professors' schedule loads much slower
|
||||
else
|
||||
status.Text = Resources.GetText(Resource.String.loadingStatus);
|
||||
|
||||
if (Data.DataSet.HttpClient != null)
|
||||
{
|
||||
List<CabinetSubject> schedule = new List<CabinetSubject>();
|
||||
|
||||
for (DateTime d = Data.StartDate; d <= Data.EndDate; d = d.AddMonths(1))
|
||||
schedule.AddRange(await Parser.GetCabinetSchedule(Data.DataSet.HttpClient, d, false)); // Even though the user can be professor he can be also PhD student (and have his student schedule)
|
||||
|
||||
if (Data.DataSet.IsProfessor == true)
|
||||
for (DateTime d = Data.StartDate; d <= Data.EndDate; d = d.AddMonths(1))
|
||||
schedule.AddRange(await Parser.GetCabinetSchedule(Data.DataSet.HttpClient, d, true));
|
||||
|
||||
schedule = schedule.FindAll(i => i.StartTime.Date >= Data.StartDate && i.StartTime.Date <= Data.EndDate); // Filtering schedule according to export range
|
||||
|
||||
status.Text = Resources.GetText(Resource.String.calendarExportStatus);
|
||||
Calendar.Export(schedule);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Subject> schedule = await Parser.LoadSchedule();
|
||||
|
||||
schedule = schedule.FindAll(i => i.StartTime.Date >= Data.StartDate && i.StartTime.Date <= Data.EndDate); // Filtering schedule according to export range
|
||||
|
||||
status.Text = Resources.GetText(Resource.String.calendarExportStatus);
|
||||
Calendar.Export(schedule);
|
||||
}
|
||||
|
||||
status.Text = Resources.GetText(Resource.String.doneStatus);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
Android.Support.V7.App.AlertDialog.Builder builder = new Android.Support.V7.App.AlertDialog.Builder(this);
|
||||
builder.SetMessage(Resources.GetText(Resource.String.connectionFailMessage))
|
||||
.SetTitle(e.Message)
|
||||
.SetPositiveButton("ОК", (s, e) => base.OnBackPressed())
|
||||
.SetNegativeButton(Resources.GetText(Resource.String.repeat), (s, e) => Export());
|
||||
|
||||
Android.Support.V7.App.AlertDialog dialog = builder.Create();
|
||||
dialog.Show();
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Android.Support.V7.App.AlertDialog.Builder builder = new Android.Support.V7.App.AlertDialog.Builder(this);
|
||||
builder.SetMessage(e.Message)
|
||||
.SetTitle(e.GetType().ToString())
|
||||
.SetPositiveButton("ОК", (s, e) => base.OnBackPressed());
|
||||
|
||||
Android.Support.V7.App.AlertDialog dialog = builder.Create();
|
||||
dialog.Show();
|
||||
return;
|
||||
}
|
||||
base.OnBackPressed(); // Navigates back to main activity (always because I don't allow backward navigation)
|
||||
}
|
||||
|
||||
public override void OnBackPressed() { } // Disables back button
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Preferences;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Text.Method;
|
||||
using Android.Views;
|
||||
using Android.Widget;
|
||||
using AngleSharp.Html.Dom;
|
||||
using AngleSharp.Html.Parser;
|
||||
using GUTSchedule.Models;
|
||||
using GUTSchedule;
|
||||
using GUTSchedule.Droid.Fragments;
|
||||
|
||||
namespace GUTSchedule.Droid.Activities
|
||||
{
|
||||
[Activity]
|
||||
public class MainActivity : AppCompatActivity
|
||||
{
|
||||
Button start, end, export;
|
||||
Button forDay, forWeek, forMonth, forSemester;
|
||||
Spinner faculty, course, group, reminder, calendar;
|
||||
CheckBox groupTitle, authorize;
|
||||
TextView error;
|
||||
LinearLayout studentParams, profParams;
|
||||
EditText email, password;
|
||||
|
||||
ISharedPreferences prefs;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
base.OnCreate(savedInstanceState);
|
||||
SetContentView(Resource.Layout.Main);
|
||||
PackageInfo version = PackageManager.GetPackageInfo(PackageName, PackageInfoFlags.MatchAll);
|
||||
FindViewById<TextView>(Resource.Id.version).Text = $"v{version.VersionName} (ci-id #{version.VersionCode})";
|
||||
|
||||
prefs = PreferenceManager.GetDefaultSharedPreferences(this);
|
||||
|
||||
AssignVariables();
|
||||
|
||||
faculty.SetList(this, Data.Faculties.Select(i => i.Name));
|
||||
int s = Data.Faculties.FindIndex(i => i.Id == prefs.GetString("Faculty", "-123"));
|
||||
faculty.SetSelection(s == -1 ? 0 : s);
|
||||
|
||||
course.SetList(this, "1234".ToCharArray());
|
||||
course.SetSelection(prefs.GetInt("Course", 0)); // IDK why but this shit triggers events anyway (even if they are set in the next line. It seem to be that there's some asynchronous shit somewhere there)
|
||||
// P.S. Fuck Android
|
||||
|
||||
AddEvents();
|
||||
|
||||
// Settings spinners' dropdown lists content
|
||||
reminder.SetList(this, new[]
|
||||
{
|
||||
Resources.GetText(Resource.String.noReminderOption),
|
||||
Resources.GetText(Resource.String.inTimeReminderOption),
|
||||
Resources.GetText(Resource.String.fiveMinuteReminderOption),
|
||||
Resources.GetText(Resource.String.tenMinuteReminderOption)
|
||||
});
|
||||
reminder.SetSelection(prefs.GetInt("Reminder", 0));
|
||||
|
||||
calendar.SetList(this, Calendar.Calendars.Select(i => i.Name));
|
||||
s = Calendar.Calendars.FindIndex(i => i.Id == prefs.GetString("Calendar", "-123"));
|
||||
calendar.SetSelection(s == -1 ? 0 : s);
|
||||
|
||||
end.Text = Data.EndDate.ToShortDateString();
|
||||
start.Text = Data.StartDate.ToShortDateString();
|
||||
|
||||
groupTitle.Checked = prefs.GetBoolean("AddGroupToHeader", false);
|
||||
authorize.Checked = prefs.GetBoolean("Authorize", true);
|
||||
|
||||
email.Text = prefs.GetString("email", "");
|
||||
password.Text = prefs.GetString("password", "");
|
||||
}
|
||||
|
||||
private async void Export_Click(object sender, EventArgs e)
|
||||
{
|
||||
error.Visibility = ViewStates.Gone;
|
||||
|
||||
if (Data.StartDate > Data.EndDate)
|
||||
{
|
||||
error.Text = Resources.GetText(Resource.String.invalidDateRangeError);
|
||||
error.Visibility = ViewStates.Visible;
|
||||
return;
|
||||
}
|
||||
|
||||
HttpClient client = null;
|
||||
bool? isProf = null;
|
||||
if (authorize.Checked)
|
||||
{
|
||||
Toast.MakeText(ApplicationContext, Resources.GetText(Resource.String.authorizationState), ToastLength.Short).Show();
|
||||
if (string.IsNullOrWhiteSpace(email.Text) || string.IsNullOrWhiteSpace(password.Text))
|
||||
{
|
||||
error.Text = Resources.GetText(Resource.String.invalidAuthorizationError);
|
||||
error.Visibility = ViewStates.Visible;
|
||||
return;
|
||||
}
|
||||
|
||||
export.Enabled = false;
|
||||
client = new HttpClient();
|
||||
|
||||
await client.GetAsync("https://cabs.itut.ru/cabinet/");
|
||||
|
||||
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://cabs.itut.ru/cabinet/lib/autentificationok.php");
|
||||
request.SetContent(
|
||||
("users", email.Text),
|
||||
("parole", password.Text));
|
||||
|
||||
HttpResponseMessage response = await client.SendAsync(request);
|
||||
string responseContent = await response.GetString();
|
||||
export.Enabled = true;
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
error.Text = $"{Resources.GetText(Resource.String.authorizationError)}: {response.StatusCode}: {responseContent}";
|
||||
error.Visibility = ViewStates.Visible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!responseContent.StartsWith("1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
error.Text = $"{Resources.GetText(Resource.String.invalidCredentialError)} ({string.Join("; ", responseContent.Replace("error=", "", StringComparison.OrdinalIgnoreCase).Split('|'))})";
|
||||
error.Visibility = ViewStates.Visible;
|
||||
return;
|
||||
}
|
||||
|
||||
export.Enabled = false;
|
||||
HttpResponseMessage verificationResponse = await client.GetAsync("https://cabs.itut.ru/cabinet/?login=yes");
|
||||
export.Enabled = true;
|
||||
IHtmlDocument doc = new HtmlParser().ParseDocument(await verificationResponse.GetString());
|
||||
if (doc.QuerySelectorAll("option").Any(i => i.TextContent.Contains("Сотрудник")))
|
||||
isProf = true;
|
||||
else
|
||||
isProf = false;
|
||||
|
||||
Data.Groups = null;
|
||||
|
||||
// Если ты это читаешь и у тебя возникли вопросы по типу "А какого хуя творится в коде ниже?!", то во-первых:
|
||||
// According to this SO thread: https://stackoverflow.com/questions/1925486/android-storing-username-and-password
|
||||
// I consider Preferences as safe enough method for storing credentials
|
||||
// А во-вторых, даже такой казалось бы небезопасный метод хранения учетных данных в сто раз надежнее того дерьма,
|
||||
// что творится на серверах Бонча (я не шучу, там все ОЧЕНЬ плохо)
|
||||
// Ну и в-третьих: Андроид - это пиздец и настоящий ад разработчика. И если бы была моя воля, я бы под него никогда не писал #FuckAndroid
|
||||
// З.Ы. Помнишь про второй пункт? Так вот, если ты используешь такой же пароль как в ЛК где-то еще, настоятельно рекомендую его поменять
|
||||
PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("email", email.Text).Apply();
|
||||
PreferenceManager.GetDefaultSharedPreferences(this).Edit().PutString("password", password.Text).Apply();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Data.Groups.Count < 1)
|
||||
{
|
||||
error.Text = Resources.GetText(Resource.String.groupSelectionError);
|
||||
error.Visibility = ViewStates.Visible;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Forming export parameters
|
||||
Data.DataSet = new DataSet
|
||||
{
|
||||
Faculty = Data.Faculties[faculty.SelectedItemPosition].Id,
|
||||
Group = Data.Groups?[group.SelectedItemPosition].Id,
|
||||
Course = course.SelectedItemPosition + 1,
|
||||
AddGroupToTitle = groupTitle.Checked,
|
||||
Calendar = Calendar.Calendars[calendar.SelectedItemPosition].Id,
|
||||
Reminder = (reminder.SelectedItemPosition - 1) * 5,
|
||||
HttpClient = client,
|
||||
IsProfessor = isProf
|
||||
};
|
||||
|
||||
StartActivity(new Intent(this, typeof(ExportActivity)));
|
||||
}
|
||||
|
||||
private async void End_Click(object sender, EventArgs e)
|
||||
{
|
||||
Data.EndDate = await new DatePickerFragment().GetDate(SupportFragmentManager, Data.EndDate);
|
||||
end.Text = Data.EndDate.ToShortDateString();
|
||||
}
|
||||
|
||||
private async void Start_Click(object sender, EventArgs e)
|
||||
{
|
||||
Data.StartDate = await new DatePickerFragment().GetDate(SupportFragmentManager, Data.StartDate);
|
||||
start.Text = Data.StartDate.ToShortDateString();
|
||||
}
|
||||
|
||||
private async void UpdateGroupsList()
|
||||
{
|
||||
if (course.SelectedItem == null)
|
||||
return;
|
||||
|
||||
await Parser.LoadGroups(Data.Faculties[faculty.SelectedItemPosition].Id, course.SelectedItemPosition + 1);
|
||||
group.SetList(this, Data.Groups.Select(i => i.Name));
|
||||
|
||||
int s = Data.Groups?.FindIndex(i => i.Id == prefs.GetString("Group", "-123")) ?? 0;
|
||||
group.SetSelection(s == -1 ? 0 : s);
|
||||
}
|
||||
|
||||
private void SetDate(int days)
|
||||
{
|
||||
Data.EndDate = Data.StartDate.AddDays(days);
|
||||
end.Text = Data.EndDate.ToShortDateString();
|
||||
}
|
||||
|
||||
#region Init stuff
|
||||
private void AssignVariables()
|
||||
{
|
||||
start = FindViewById<Button>(Resource.Id.start);
|
||||
end = FindViewById<Button>(Resource.Id.end);
|
||||
export = FindViewById<Button>(Resource.Id.export);
|
||||
|
||||
forDay = FindViewById<Button>(Resource.Id.forDay);
|
||||
forWeek = FindViewById<Button>(Resource.Id.forWeek);
|
||||
forMonth = FindViewById<Button>(Resource.Id.forMonth);
|
||||
forSemester = FindViewById<Button>(Resource.Id.forSemester);
|
||||
|
||||
faculty = FindViewById<Spinner>(Resource.Id.faculty);
|
||||
course = FindViewById<Spinner>(Resource.Id.course);
|
||||
group = FindViewById<Spinner>(Resource.Id.group);
|
||||
reminder = FindViewById<Spinner>(Resource.Id.reminder);
|
||||
calendar = FindViewById<Spinner>(Resource.Id.calendar);
|
||||
|
||||
error = FindViewById<TextView>(Resource.Id.error);
|
||||
|
||||
groupTitle = FindViewById<CheckBox>(Resource.Id.groupTitle);
|
||||
authorize = FindViewById<CheckBox>(Resource.Id.authorization);
|
||||
|
||||
studentParams = FindViewById<LinearLayout>(Resource.Id.studentParams);
|
||||
profParams = FindViewById<LinearLayout>(Resource.Id.professorParams);
|
||||
|
||||
email = FindViewById<EditText>(Resource.Id.email);
|
||||
password = FindViewById<EditText>(Resource.Id.password);
|
||||
}
|
||||
|
||||
private void AddEvents()
|
||||
{
|
||||
faculty.ItemSelected += (s, e) =>
|
||||
{
|
||||
prefs.Edit().PutString("Faculty", Data.Faculties[e.Position].Id).Apply();
|
||||
UpdateGroupsList();
|
||||
};
|
||||
course.ItemSelected += (s, e) =>
|
||||
{
|
||||
prefs.Edit().PutInt("Course", e.Position).Apply();
|
||||
UpdateGroupsList();
|
||||
};
|
||||
authorize.CheckedChange += (s, e) =>
|
||||
{
|
||||
prefs.Edit().PutBoolean("Authorize", e.IsChecked).Apply();
|
||||
if (e.IsChecked)
|
||||
{
|
||||
studentParams.Visibility = ViewStates.Gone;
|
||||
profParams.Visibility = ViewStates.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
studentParams.Visibility = ViewStates.Visible;
|
||||
profParams.Visibility = ViewStates.Gone;
|
||||
}
|
||||
};
|
||||
calendar.ItemSelected += (s, e) =>
|
||||
prefs.Edit().PutString("Calendar", Calendar.Calendars[e.Position].Id).Apply();
|
||||
reminder.ItemSelected += (s, e) =>
|
||||
prefs.Edit().PutInt("Reminder", e.Position).Apply();
|
||||
group.ItemSelected += (s, e) =>
|
||||
prefs.Edit().PutString("Group", Data.Groups[e.Position].Id).Apply();
|
||||
|
||||
groupTitle.Click += (s, e) =>
|
||||
prefs.Edit().PutBoolean("AddGroupToHeader", groupTitle.Checked).Apply();
|
||||
|
||||
|
||||
forDay.Click += (s, e) => SetDate(0);
|
||||
forWeek.Click += (s, e) => SetDate(6);
|
||||
forMonth.Click += (s, e) => SetDate(30);
|
||||
forSemester.Click += (s, e) =>
|
||||
{
|
||||
Data.EndDate = DateTime.Today.Month > 8 ? new DateTime(DateTime.Today.Year + 1, 1, 1) : new DateTime(DateTime.Today.Year, 8, 31);
|
||||
end.Text = Data.EndDate.ToShortDateString();
|
||||
};
|
||||
|
||||
start.Click += Start_Click;
|
||||
end.Click += End_Click;
|
||||
|
||||
export.Click += Export_Click;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Menu stuff
|
||||
public override bool OnCreateOptionsMenu(IMenu menu)
|
||||
{
|
||||
MenuInflater.Inflate(Resource.Menu.MainContextMenu, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Clear(bool keepPrevious = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
Toast.MakeText(ApplicationContext, Resources.GetText(Resource.String.clearingStatus), ToastLength.Short).Show();
|
||||
Calendar.Clear(keepPrevious);
|
||||
Toast.MakeText(ApplicationContext, Resources.GetText(Resource.String.doneStatus), ToastLength.Short).Show();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Android.Support.V7.App.AlertDialog.Builder builder = new Android.Support.V7.App.AlertDialog.Builder(this);
|
||||
builder.SetMessage(e.Message)
|
||||
.SetTitle(e.GetType().ToString())
|
||||
.SetPositiveButton("ОК", (IDialogInterfaceOnClickListener)null);
|
||||
|
||||
Android.Support.V7.App.AlertDialog dialog = builder.Create();
|
||||
dialog.Show();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnOptionsItemSelected(IMenuItem item)
|
||||
{
|
||||
Android.Support.V7.App.AlertDialog.Builder builder;
|
||||
Android.Support.V7.App.AlertDialog dialog;
|
||||
switch (item.ItemId)
|
||||
{
|
||||
case Resource.Id.about:
|
||||
StartActivity(new Intent(this, typeof(AboutActivity)));
|
||||
return true;
|
||||
case Resource.Id.email:
|
||||
StartActivity(new Intent(Intent.ActionView, Android.Net.Uri.Parse("mailto:feedback@xfox111.net")));
|
||||
return true;
|
||||
|
||||
case Resource.Id.clear:
|
||||
builder = new Android.Support.V7.App.AlertDialog.Builder(this);
|
||||
builder.SetMessage(Resources.GetText(Resource.String.clearScheduleMessage))
|
||||
.SetTitle(Resources.GetText(Resource.String.clearScheduleTitle))
|
||||
.SetPositiveButton(Resources.GetText(Resource.String.clearUpcomingOption), (s, e) => Clear())
|
||||
.SetNegativeButton(Resources.GetText(Resource.String.clearAllOption), (s, e) => Clear(false))
|
||||
.SetNeutralButton(Resources.GetText(Resource.String.cancelOption), (IDialogInterfaceOnClickListener)null);
|
||||
|
||||
dialog = builder.Create();
|
||||
dialog.Show();
|
||||
|
||||
// Making links clickable
|
||||
dialog.FindViewById<TextView>(Android.Resource.Id.Message).MovementMethod = LinkMovementMethod.Instance;
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnOptionsItemSelected(item);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override void OnBackPressed() =>
|
||||
FinishAffinity(); // Close application
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Android.Support.V4.App;
|
||||
using Android.Support.V4.Content;
|
||||
using Android.Support.V7.App;
|
||||
using Android.Widget;
|
||||
using GUTSchedule;
|
||||
using GUTSchedule.Droid;
|
||||
using GUTSchedule.Droid.Activities;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace GUT.Schedule.Droid.Activities
|
||||
{
|
||||
/// <summary>
|
||||
/// Splash screen activity. Loads init data
|
||||
/// </summary>
|
||||
[Activity(MainLauncher = true, Theme = "@style/AppTheme.Light.SplashScreen")]
|
||||
public class StartActivity : AppCompatActivity
|
||||
{
|
||||
TextView status;
|
||||
|
||||
protected override void OnCreate(Bundle savedInstanceState)
|
||||
{
|
||||
SetContentView(Resource.Layout.SplashScreen);
|
||||
base.OnCreate(savedInstanceState);
|
||||
|
||||
status = FindViewById<TextView>(Resource.Id.status);
|
||||
PackageInfo version = PackageManager.GetPackageInfo(PackageName, PackageInfoFlags.MatchAll);
|
||||
FindViewById<TextView>(Resource.Id.version).Text = $"v{version.VersionName} (ci-id #{version.VersionCode})";
|
||||
|
||||
status.Text = Resources.GetText(Resource.String.permissionsCheckStatus);
|
||||
|
||||
if (ContextCompat.CheckSelfPermission(this, Android.Manifest.Permission.WriteCalendar) != Permission.Granted)
|
||||
{
|
||||
if (ActivityCompat.ShouldShowRequestPermissionRationale(this, Android.Manifest.Permission.WriteCalendar))
|
||||
ShowDialog(Resources.GetText(Resource.String.calendarAccessTitle), Resources.GetText(Resource.String.calendarAccessRationale), RequestPermissions);
|
||||
else
|
||||
RequestPermissions();
|
||||
}
|
||||
else
|
||||
Proceed();
|
||||
}
|
||||
|
||||
private async void Proceed()
|
||||
{
|
||||
try
|
||||
{
|
||||
status.Text = Resources.GetText(Resource.String.calendarLoadingStatus);
|
||||
Calendar.LoadCalendars();
|
||||
if (Calendar.Calendars.Count == 0)
|
||||
{
|
||||
ShowDialog(Resources.GetText(Resource.String.createCalendarTitle), Resources.GetText(Resource.String.createCalendarMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
status.Text = Resources.GetText(Resource.String.facultiesLoadingStatus);
|
||||
await Parser.LoadFaculties();
|
||||
|
||||
status.Text = Resources.GetText(Resource.String.offsetDatesLoadingStatus);
|
||||
using HttpClient client = new HttpClient();
|
||||
Data.FirstWeekDay = int.Parse(await client.GetStringAsync("https://xfox111.net/schedule_offset.txt"));
|
||||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
ShowDialog(e.Message, Resources.GetText(Resource.String.connectionFailMessage), Proceed, FinishAndRemoveTask, Resources.GetText(Resource.String.repeat), Resources.GetText(Resource.String.quit));
|
||||
return;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ShowDialog(e.GetType().ToString(), e.Message, FinishAndRemoveTask);
|
||||
return;
|
||||
}
|
||||
StartActivity(new Intent(this, typeof(MainActivity)));
|
||||
}
|
||||
|
||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
|
||||
{
|
||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
if (grantResults.All(i => i == Permission.Granted))
|
||||
Proceed();
|
||||
else
|
||||
ShowDialog(Resources.GetText(Resource.String.calendarAccessTitle), Resources.GetText(Resource.String.calendarAccessRationale), RequestPermissions);
|
||||
}
|
||||
|
||||
private void RequestPermissions() =>
|
||||
ActivityCompat.RequestPermissions(this, new[]
|
||||
{
|
||||
Android.Manifest.Permission.ReadCalendar,
|
||||
Android.Manifest.Permission.WriteCalendar,
|
||||
Android.Manifest.Permission.Internet
|
||||
}, 0);
|
||||
|
||||
private void ShowDialog(string title, string content, Action posAction = null, Action negAction = null, string posActionLabel = null, string negActionLabel = null)
|
||||
{
|
||||
Android.Support.V7.App.AlertDialog.Builder builder = new Android.Support.V7.App.AlertDialog.Builder(this);
|
||||
builder.SetMessage(content)
|
||||
.SetTitle(title).SetPositiveButton(posActionLabel ?? "OK", (s, e) => posAction?.Invoke());
|
||||
|
||||
if (negAction != null)
|
||||
builder.SetNegativeButton(negActionLabel ?? Resources.GetText(Resource.String.close), (s, e) => negAction.Invoke());
|
||||
|
||||
Android.Support.V7.App.AlertDialog dialog = builder.Create();
|
||||
dialog.Show();
|
||||
}
|
||||
|
||||
public override void OnBackPressed() { } // Disables back button
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System.Collections.Generic;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Database;
|
||||
using Android.Net;
|
||||
using Android.Provider;
|
||||
using GUTSchedule.Models;
|
||||
using Java.Util;
|
||||
|
||||
namespace GUTSchedule.Droid
|
||||
{
|
||||
public static class Calendar
|
||||
{
|
||||
/// <summary>
|
||||
/// List of all existing Google calendars on the device
|
||||
/// </summary>
|
||||
public static List<(string Id, string Name)> Calendars { get; private set; } = new List<(string Id, string Name)>();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves all Google Accounts' calendars existing on the device and puts them to <see cref="Calendars"/>
|
||||
/// </summary>
|
||||
public static void LoadCalendars()
|
||||
{
|
||||
Calendars = new List<(string, string)>(); // Resetting current calendars list
|
||||
|
||||
// Building calendar data retrieval projections
|
||||
Uri calendarsUri = CalendarContract.Calendars.ContentUri;
|
||||
string[] calendarsProjection = {
|
||||
CalendarContract.Calendars.InterfaceConsts.Id,
|
||||
CalendarContract.Calendars.InterfaceConsts.CalendarDisplayName,
|
||||
CalendarContract.Calendars.InterfaceConsts.AccountName
|
||||
};
|
||||
|
||||
// Retrieving calendars data
|
||||
ICursor cursor = Application.Context.ContentResolver.Query(calendarsUri, calendarsProjection, null, null, null);
|
||||
|
||||
while (cursor.MoveToNext())
|
||||
Calendars.Add((cursor.GetString(0), $"{cursor.GetString(1)} ({cursor.GetString(2)})"));
|
||||
|
||||
cursor.Close();
|
||||
}
|
||||
|
||||
public static void Export(IEnumerable<Subject> schedule)
|
||||
{
|
||||
DataSet data = Data.DataSet;
|
||||
|
||||
foreach (Subject item in schedule)
|
||||
{
|
||||
ContentValues eventValues = new ContentValues();
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.CalendarId, data.Calendar);
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Title, string.Format("{0}.{1} {2} ({3})",
|
||||
item.Order,
|
||||
data.AddGroupToTitle ? $" [{item.Group}]" : "",
|
||||
item.Name,
|
||||
item.Type));
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Description, item.Professor);
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventLocation, string.Join(';', item.Cabinets));
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Availability, 0);
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.HasAlarm, data.Reminder != -5);
|
||||
// For some reason Google calendars ignore HasAlarm = false and set reminder for 30 minutes. Local calendars don't seem to have this issue
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtstart, item.StartTime.ToUnixTime());
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtend, item.EndTime.ToUnixTime());
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventTimezone, TimeZone.Default.ID);
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventEndTimezone, TimeZone.Default.ID);
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.CustomAppPackage, Application.Context.PackageName);
|
||||
|
||||
Uri response = Application.Context.ContentResolver.Insert(CalendarContract.Events.ContentUri, eventValues);
|
||||
|
||||
// Settings reminder
|
||||
if (data.Reminder != -5)
|
||||
{
|
||||
ContentValues reminderValues = new ContentValues();
|
||||
reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.EventId, long.Parse(response.LastPathSegment));
|
||||
reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.Minutes, data.Reminder);
|
||||
|
||||
Application.Context.ContentResolver.Insert(CalendarContract.Reminders.ContentUri, reminderValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Export(IEnumerable<CabinetSubject> schedule)
|
||||
{
|
||||
DataSet data = Data.DataSet;
|
||||
|
||||
foreach (CabinetSubject item in schedule)
|
||||
{
|
||||
ContentValues eventValues = new ContentValues();
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.CalendarId, data.Calendar);
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Title, string.Format("{0}{1}. {2} ({3})",
|
||||
item.ProfessorSchedule ? "📚 " : (data.AddGroupToTitle ? $"[{data.Group}] " : ""),
|
||||
item.Order,
|
||||
item.Name,
|
||||
item.Type));
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Description, item.Opponent);
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventLocation, item.Cabinet);
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Availability, 0);
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.HasAlarm, data.Reminder != -5);
|
||||
// For some reason Google calendars ignore HasAlarm = false and set reminder for 30 minutes. Local calendars don't seem to have this issue
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtstart, item.StartTime.ToUnixTime());
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.Dtend, item.EndTime.ToUnixTime());
|
||||
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventTimezone, TimeZone.Default.ID);
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.EventEndTimezone, TimeZone.Default.ID);
|
||||
eventValues.Put(CalendarContract.Events.InterfaceConsts.CustomAppPackage, Application.Context.PackageName);
|
||||
|
||||
Uri response = Application.Context.ContentResolver.Insert(CalendarContract.Events.ContentUri, eventValues);
|
||||
|
||||
// Settings reminder
|
||||
if (data.Reminder != -5)
|
||||
{
|
||||
ContentValues reminderValues = new ContentValues();
|
||||
reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.EventId, long.Parse(response.LastPathSegment));
|
||||
reminderValues.Put(CalendarContract.Reminders.InterfaceConsts.Minutes, data.Reminder);
|
||||
|
||||
Application.Context.ContentResolver.Insert(CalendarContract.Reminders.ContentUri, reminderValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Clear(bool keepPrevious = true)
|
||||
{
|
||||
Uri contentUri = CalendarContract.Events.ContentUri;
|
||||
string selector = $"({CalendarContract.Events.InterfaceConsts.CustomAppPackage} == \"{Application.Context.PackageName}\") AND (deleted != 1)";
|
||||
if (keepPrevious)
|
||||
selector += $" AND (dtstart > {System.DateTime.Now.ToUnixTime()})";
|
||||
|
||||
string[] calendarsProjection = {
|
||||
CalendarContract.Events.InterfaceConsts.Id,
|
||||
CalendarContract.Events.InterfaceConsts.Dtstart,
|
||||
CalendarContract.Events.InterfaceConsts.CustomAppPackage,
|
||||
};
|
||||
|
||||
// Retrieving calendars data
|
||||
ICursor cursor = Application.Context.ContentResolver.Query(contentUri, calendarsProjection, selector, null, null);
|
||||
while (cursor.MoveToNext())
|
||||
Application.Context.ContentResolver.Delete(ContentUris.WithAppendedId(CalendarContract.Events.ContentUri, cursor.GetLong(0)), null, null);
|
||||
|
||||
cursor.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Android.Content;
|
||||
using Android.Widget;
|
||||
|
||||
namespace GUTSchedule.Droid
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets array as Spinner dropdown list content
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Array items type</typeparam>
|
||||
/// <param name="spinner">Spinner on which array will be assigned to</param>
|
||||
/// <param name="context">Current activity context. In most common cases <c>this</c> will do</param>
|
||||
/// <param name="array">Array of items to be displayed</param>
|
||||
public static void SetList<T>(this Spinner spinner, Context context, IEnumerable<T> array)
|
||||
{
|
||||
ArrayAdapter adapter = new ArrayAdapter(context, Resource.Layout.support_simple_spinner_dropdown_item, array.ToList());
|
||||
spinner.Adapter = adapter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="DateTime"/> to milliseconds count
|
||||
/// </summary>
|
||||
/// <remarks>In the nearest future we will be fucked because of that shit</remarks>
|
||||
/// <param name="dt"><see cref="DateTime"/> which is to be converted to UNIX time</param>
|
||||
/// <returns><see cref="long"/> which is represented by total milliseconds count passed since 1970</returns>
|
||||
public static long ToUnixTime(this DateTime dt) =>
|
||||
(long)dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Android.OS;
|
||||
using Android.Widget;
|
||||
using Android.App;
|
||||
using System.Threading.Tasks;
|
||||
using Android.Content;
|
||||
|
||||
namespace GUTSchedule.Droid.Fragments
|
||||
{
|
||||
/// <summary>
|
||||
/// Date picker
|
||||
/// </summary>
|
||||
public class DatePickerFragment : Android.Support.V4.App.DialogFragment, DatePickerDialog.IOnDateSetListener
|
||||
{
|
||||
DateTime _date;
|
||||
bool dismissed = false;
|
||||
public override Dialog OnCreateDialog(Bundle savedInstanceState) =>
|
||||
new DatePickerDialog(Activity, this, _date.Year, _date.Month - 1, _date.Day);
|
||||
|
||||
// Occures when user selected a date
|
||||
public void OnDateSet(DatePicker view, int year, int month, int dayOfMonth)
|
||||
{
|
||||
_date = view.DateTime;
|
||||
dismissed = true;
|
||||
}
|
||||
|
||||
public override void OnCancel(IDialogInterface dialog)
|
||||
{
|
||||
base.OnCancel(dialog);
|
||||
dismissed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows date picker and waits for user input
|
||||
/// </summary>
|
||||
/// <param name="manager">Fragment manager of the current activity (In most common cases it is <c>this.FragmentManager</c>)</param>
|
||||
/// <param name="date">Date which is to be selected by default</param>
|
||||
/// <returns><see cref="DateTime"/> picked by user</returns>
|
||||
public async Task<DateTime> GetDate(Android.Support.V4.App.FragmentManager manager, DateTime date)
|
||||
{
|
||||
_date = date;
|
||||
Show(manager, "datePicker");
|
||||
|
||||
while (!dismissed)
|
||||
await Task.Delay(100);
|
||||
|
||||
return _date;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{A0471165-37F5-4309-8A92-42F1A6589EEE}</ProjectGuid>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<TemplateGuid>{84dd83c5-0fe3-4294-9419-09e7c8ba324f}</TemplateGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GUTSchedule.Droid</RootNamespace>
|
||||
<AssemblyName>GUTSchedule.Droid</AssemblyName>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AndroidApplication>True</AndroidApplication>
|
||||
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
|
||||
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
|
||||
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>False</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
|
||||
<AotAssemblies>false</AotAssemblies>
|
||||
<EnableLLVM>false</EnableLLVM>
|
||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||
<BundleAssemblies>false</BundleAssemblies>
|
||||
<MandroidI18n>CJK;Mideast;Rare;West;Other</MandroidI18n>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AotAssemblies>false</AotAssemblies>
|
||||
<EnableLLVM>false</EnableLLVM>
|
||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||
<BundleAssemblies>false</BundleAssemblies>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||
<AndroidPackageFormat>aab</AndroidPackageFormat>
|
||||
<MandroidI18n>CJK;Mideast;Rare;West;Other</MandroidI18n>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release %28APK%29|AnyCPU'">
|
||||
<OutputPath>bin\Release %28APK%29\</OutputPath>
|
||||
<DebugSymbols>false</DebugSymbols>
|
||||
<DebugType>portable</DebugType>
|
||||
<Optimize>True</Optimize>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
|
||||
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<AotAssemblies>false</AotAssemblies>
|
||||
<EnableLLVM>false</EnableLLVM>
|
||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||
<BundleAssemblies>false</BundleAssemblies>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||
<AndroidPackageFormat>apk</AndroidPackageFormat>
|
||||
<MandroidI18n>CJK;Mideast;Rare;West;Other</MandroidI18n>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Activities\AboutActivity.cs" />
|
||||
<Compile Include="Calendar.cs" />
|
||||
<Compile Include="Fragments\DatePickerFragment.cs" />
|
||||
<Compile Include="Activities\ExportActivity.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="Activities\MainActivity.cs" />
|
||||
<Compile Include="Resources\Resource.designer.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Activities\StartActivity.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\Main.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
<AndroidResource Include="Resources\values\colors.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-anydpi-v26\ic_launcher_round.xml" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-hdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-mdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxhdpi\ic_launcher_round.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_foreground.png" />
|
||||
<AndroidResource Include="Resources\mipmap-xxxhdpi\ic_launcher_round.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CSharp">
|
||||
<Version>4.7.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json">
|
||||
<Version>12.0.3</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Net.Http">
|
||||
<Version>4.3.4</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Core.Utils" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Android.Support.CustomTabs" Version="28.0.0.3" />
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\SplashScreen.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\menu\MainContextMenu.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\Export.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\logo.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\values\styles.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\layout\About.xml">
|
||||
<SubType>Designer</SubType>
|
||||
</AndroidResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\values\strings.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\values-ru\strings.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GUTSchedule\GUTSchedule.csproj">
|
||||
<Project>{a6f6de35-0eb4-4d11-9ff9-f4601595b639}</Project>
|
||||
<Name>GUTSchedule</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="0-development-test" package="com.xfox111.gut.schedule" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28" />
|
||||
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/appName" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme.Light"></application>
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Android.App;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
#if DEBUG
|
||||
[assembly: Application(Debuggable = true)]
|
||||
#else
|
||||
[assembly: Application(Debuggable=false)]
|
||||
#endif
|
||||
[assembly: AssemblyTitle("GUT.Schedule")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GUT.Schedule")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
|
After Width: | Height: | Size: 129 KiB |
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:theme="@style/AppTheme.Light">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.AppCompat.Headline"
|
||||
android:text="@string/appName"/>
|
||||
<TextView
|
||||
android:id="@+id/version"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textStyle="italic"
|
||||
android:layout_marginTop="-5dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:text="v$(Build.BuildNumber) (ci-id #$(Build.BuildId))"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/appDescription"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:text="@string/developedBy"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contributorsTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
style="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:visibility="gone"
|
||||
android:text="@string/contributorsTitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contributors"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
style="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:text="@string/specialThanksTitle"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/specialThanksPeople"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
style="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:text="@string/contactsTitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contacts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
style="@style/TextAppearance.AppCompat.Subhead"
|
||||
android:text="@string/linksTitle"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/links"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="©2020 Michael "XFox" Gordeev"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/feedback"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/feedbackButton"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:theme="@style/AppTheme.Light.SplashScreen">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:indeterminateTint="#fff"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_margin="20dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/loadingStatus"
|
||||
android:textColor="#fff"
|
||||
android:layout_gravity="center_horizontal"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,231 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:theme="@style/AppTheme.Light">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/scheduleParametersTitle"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16dp"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/authorization"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/authorizeCheckbox"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/studentParams"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/facultySpinner"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/faculty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/courseSpinner"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/course"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/groupSpinner"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/professorParams"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="E-mail"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/email"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textWebEmailAddress"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/passwordField"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textWebPassword"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/exportParametersTitle"
|
||||
android:textStyle="bold"
|
||||
android:textSize="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dateRange"/>
|
||||
|
||||
<TableLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stretchColumns="*"
|
||||
android:shrinkColumns="*">
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/start"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="20-Dec-2019"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/end"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="31-Dec-2019"/>
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/forDay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/forDayButton"/>
|
||||
<Button
|
||||
android:id="@+id/forWeek"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/forWeekButton"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<Button
|
||||
android:id="@+id/forMonth"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/forMonthButton"/>
|
||||
<Button
|
||||
android:id="@+id/forSemester"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/forSemesterButton"/>
|
||||
</LinearLayout>
|
||||
</TableRow>
|
||||
</TableLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/reminderSpinner"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/reminder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/reminderNote"/>
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/groupTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/addGroupToTitleCheckbox"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/titleNote"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/destinationCalendarSpinner"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/calendar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#FF0000"
|
||||
android:text="Error"
|
||||
android:layout_marginVertical="5dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/export"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/addScheduleButton"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/copyrights"/>
|
||||
<TextView
|
||||
android:id="@+id/version"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="v$(Build.BuildNumber) (ci-id #$(Build.BuildId))"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:theme="@style/AppTheme.Light.SplashScreen">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_centerInParent="true">
|
||||
|
||||
<!--<ImageView
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:src="@drawable/logo"/>-->
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/appName"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:textSize="36dp"/>
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginVertical="50dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/initializationStatus"
|
||||
android:textSize="20dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:text="v$(Build.BuildNumber) (ci-id #$(Build.BuildId))"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,17 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<item
|
||||
android:id="@+id/clear"
|
||||
android:title="@string/clearCalendarOption"
|
||||
android:showAsAction="never"/>
|
||||
<item
|
||||
android:id="@+id/email"
|
||||
android:title="@string/reportErrorOption"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/about"
|
||||
android:title="@string/aboutTitle"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/colorPrimary"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/colorPrimary"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
<string name="appName">ГУТ.Расписание</string>
|
||||
|
||||
<!-- StartActivity -->
|
||||
<string name="initializationStatus">Инициализация</string>
|
||||
<string name="permissionsCheckStatus">Проверка наличия разрешений</string>
|
||||
<string name="calendarLoadingStatus">Загрузка списка календарей</string>
|
||||
<string name="facultiesLoadingStatus">Загрузка списка факультативов</string>
|
||||
<string name="offsetDatesLoadingStatus">Загрузка дат смещения</string>
|
||||
|
||||
<string name="calendarAccessTitle">Доступ к календарю</string>
|
||||
<string name="calendarAccessRationale">Разрешите приложению получать доступ к календарю. Без этого разрешения приложение не сможет добавлять расписание в ваш календарь</string>
|
||||
|
||||
<string name="createCalendarTitle">Создайте новый календарь</string>
|
||||
<string name="createCalendarMessage">На вашем устройстве нет календарей пригодных для записи расписания</string>
|
||||
|
||||
<string name="connectionFailMessage">Невозможно загрузить расписание. Проверьте интернет-соединение или попробуйте позже</string>
|
||||
<string name="repeat">Повторить</string>
|
||||
<string name="quit">Выйти</string>
|
||||
<string name="close">Закрыть</string>
|
||||
|
||||
<!-- MainActivity -->
|
||||
<string name="scheduleParametersTitle">Параметры расписания</string>
|
||||
<string name="authorizeCheckbox">Авторизоваться через Личный кабинет</string>
|
||||
<string name="facultySpinner">Факультет</string>
|
||||
<string name="courseSpinner">Курс</string>
|
||||
<string name="groupSpinner">Группа</string>
|
||||
|
||||
<string name="passwordField">Пароль</string>
|
||||
|
||||
<string name="exportParametersTitle">Параметры экспорта</string>
|
||||
<string name="dateRange">Диапазон экспорта</string>
|
||||
|
||||
<string name="forDayButton">На день</string>
|
||||
<string name="forWeekButton">На неделю</string>
|
||||
<string name="forMonthButton">На месяц</string>
|
||||
<string name="forSemesterButton">На семестр</string>
|
||||
|
||||
<string name="reminderSpinner">Напоминать за</string>
|
||||
<string name="reminderNote">(i) Внимание, при экспорте в облачный Google-календарь, Google автоматически ставит уведомление за пол часа, если его не поставил пользователь</string>
|
||||
<string name="noReminderOption">Нет</string>
|
||||
<string name="inTimeReminderOption">Во время начала</string>
|
||||
<string name="fiveMinuteReminderOption">За 5 минут</string>
|
||||
<string name="tenMinuteReminderOption">За 10 минут</string>
|
||||
|
||||
<string name="addGroupToTitleCheckbox">Добавить номер группы в заголовок</string>
|
||||
<string name="titleNote">(i) Не касается преподавательского расписания</string>
|
||||
|
||||
<string name="destinationCalendarSpinner">Конечый календарь</string>
|
||||
|
||||
<string name="addScheduleButton">Добавить расписание</string>
|
||||
|
||||
<string name="copyrights">©2020 Михаил Гордеев, ИКСС, ИКТ-907</string>
|
||||
|
||||
<string name="clearCalendarOption">Очистить расписание</string>
|
||||
<string name="reportErrorOption">Сообщить об ошибке</string>
|
||||
|
||||
<string name="invalidDateRangeError">Ошибка: Неправильный диапазон дат</string>
|
||||
<string name="authorizationState">Авторизация...</string>
|
||||
<string name="invalidAuthorizationError">Ошибка: Введите корректные учетные данные</string>
|
||||
<string name="authorizationError">Ошибка авторизации</string>
|
||||
<string name="invalidCredentialError">Ошибка авторизации: Неверный e-mail и/или пароль</string>
|
||||
<string name="groupSelectionError">Ошибка: Не выбрана группа</string>
|
||||
|
||||
<string name="clearScheduleTitle">Очистка расписания</string>
|
||||
<string name="clearScheduleMessage">Это действие удалит экспортированное расписание из всех доступных календарей. \nДанное действие затронет только расписание, экспортированное этим приложением \n\'Все\' - удалит все события расписания, включая прошедшие \n\'Только новые\' - удалит будущие события расписания
|
||||
</string>
|
||||
<string name="clearAllOption">Все</string>
|
||||
<string name="clearUpcomingOption">Только новые</string>
|
||||
<string name="cancelOption">Отмена</string>
|
||||
<string name="clearingStatus">Очистка...</string>
|
||||
|
||||
<!-- ExportActivity -->
|
||||
<string name="loadingStatus">Загрузка</string>
|
||||
<string name="potatoLoadingStatus">Загрузка расписания с картофельных серверов Бонча</string>
|
||||
<string name="scheduleLoadingStatus">Загрузка расписания</string>
|
||||
<string name="calendarExportStatus">Экспортирование в календарь</string>
|
||||
<string name="doneStatus">Готово</string>
|
||||
|
||||
<!-- AboutActivity -->
|
||||
<string name="aboutTitle">О приложении</string>
|
||||
<string name="appDescription">Приложение для экспорта перподавательского и учебного расписаний Санкт-Петербургского Государственного Университета Телекоммуникаций им. проф. М.А. Бонч-Бруевича</string>
|
||||
<string name="developedBy">Разработано Михаилом Гордеевым, ИКТ-907, ИКСС в Научно-образовательном центре \"Технологии информационных образовательных систем\"</string>
|
||||
<string name="contributorsTitle">Свой вклад в разработку внесли</string>
|
||||
|
||||
<string name="specialThanksTitle">Особые благодарности</string>
|
||||
<string name="specialThanksPeople">Виталий Мошков, Анастасия Годунова</string>
|
||||
|
||||
<string name="contactsTitle">Контакты</string>
|
||||
<string name="websiteContact">Веб-сайт</string>
|
||||
<string name="twitterContact">Твиттер</string>
|
||||
<string name="vkontakteContact">ВКонтакте</string>
|
||||
|
||||
<string name="linksTitle">Полезные ссылки</string>
|
||||
<string name="privacyPolicyLink">Политика конфиденциальности</string>
|
||||
<string name="repositoryLink">Репозиторий GitHub</string>
|
||||
<string name="notsLink">НОЦ \"ТИОС\"</string>
|
||||
<string name="sutLink">СПбГУТ</string>
|
||||
|
||||
<string name="feedbackButton">Оставить отзыв</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#ff8000</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,100 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
<string name="appName">GUT.Schedule</string>
|
||||
|
||||
<string name="initializationStatus">Initialization</string>
|
||||
<string name="permissionsCheckStatus">Checking permissions</string>
|
||||
<string name="calendarLoadingStatus">Loading calendars list</string>
|
||||
<string name="facultiesLoadingStatus">Loading faculties list</string>
|
||||
<string name="offsetDatesLoadingStatus">Loading offset dates</string>
|
||||
|
||||
<string name="calendarAccessTitle">Calendar access</string>
|
||||
<string name="calendarAccessRationale">Grant permission to the app to access calendar. Without it the app will not be able to add schedule to your calendar</string>
|
||||
|
||||
<string name="createCalendarTitle">Create new calendar</string>
|
||||
<string name="createCalendarMessage">There\'s no calendars on your device we can write schedule to</string>
|
||||
|
||||
<string name="connectionFailMessage">Unable to load schedule. Check your internet connection or try again later</string>
|
||||
<string name="repeat">Try again</string>
|
||||
<string name="quit">Quit</string>
|
||||
<string name="close">Close</string>
|
||||
|
||||
<!-- MainActivity -->
|
||||
<string name="scheduleParametersTitle">Schedule parameters</string>
|
||||
<string name="authorizeCheckbox">Authorize via Personal cabinet</string>
|
||||
<string name="facultySpinner">Faculty</string>
|
||||
<string name="courseSpinner">Course</string>
|
||||
<string name="groupSpinner">Group</string>
|
||||
|
||||
<string name="passwordField">Password</string>
|
||||
|
||||
<string name="exportParametersTitle">Export parameters</string>
|
||||
<string name="dateRange">Export range</string>
|
||||
|
||||
<string name="forDayButton">For day</string>
|
||||
<string name="forWeekButton">For week</string>
|
||||
<string name="forMonthButton">For month</string>
|
||||
<string name="forSemesterButton">For semester</string>
|
||||
|
||||
<string name="reminderSpinner">Set reminders for</string>
|
||||
<string name="reminderNote">(i) Attention, for cloud-based Google calendars Google automatically sets reminders for 30 minutes if there\'s no reminder set by user</string>
|
||||
<string name="noReminderOption">None</string>
|
||||
<string name="inTimeReminderOption">At the start of event</string>
|
||||
<string name="fiveMinuteReminderOption">5 minutes</string>
|
||||
<string name="tenMinuteReminderOption">10 minutes</string>
|
||||
|
||||
<string name="addGroupToTitleCheckbox">Add group number to event title</string>
|
||||
<string name="titleNote">(i) This doesn\'t affect professors\' schedule</string>
|
||||
|
||||
<string name="destinationCalendarSpinner">Destination calendar</string>
|
||||
|
||||
<string name="addScheduleButton">Add schedule</string>
|
||||
|
||||
<string name="copyrights">©2020 Michael Gordeev, INS, IS-907</string>
|
||||
|
||||
<string name="clearCalendarOption">Clear schedule</string>
|
||||
<string name="reportErrorOption">Report error</string>
|
||||
|
||||
<string name="invalidDateRangeError">Error: Invalid date range</string>
|
||||
<string name="authorizationState">Authorization...</string>
|
||||
<string name="invalidAuthorizationError">Error: Invalid credential</string>
|
||||
<string name="authorizationError">Authorization error</string>
|
||||
<string name="invalidCredentialError">Authorization error: Invalid e-mail and/or password</string>
|
||||
<string name="groupSelectionError">Error: no group was selected</string>
|
||||
|
||||
<string name="clearScheduleTitle">Clear schedule</string>
|
||||
<string name="clearScheduleMessage">This action will purge exported schedule from all available calendars. \nIt will affect only events created by the app. \n\'All\' - will purge all timetable events including the past ones \n\'Upcoming\' - will affect only upcoming timetable events</string>
|
||||
<string name="clearAllOption">All</string>
|
||||
<string name="clearUpcomingOption">Upcoming</string>
|
||||
<string name="cancelOption">Cancel</string>
|
||||
<string name="clearingStatus">Clearing...</string>
|
||||
|
||||
<!-- ExportActivity -->
|
||||
<string name="loadingStatus">Loading</string>
|
||||
<string name="potatoLoadingStatus">Loading schedule from SPbSUT potato servers</string>
|
||||
<string name="scheduleLoadingStatus">Loading schedule</string>
|
||||
<string name="calendarExportStatus">Exporting to calendar</string>
|
||||
<string name="doneStatus">Done</string>
|
||||
|
||||
<!-- 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="contributorsTitle">Contributors</string>
|
||||
|
||||
<string name="specialThanksTitle">Special thanks</string>
|
||||
<string name="specialThanksPeople">Vitaliy Moshkov, Anastasiya Godunova</string>
|
||||
|
||||
<string name="contactsTitle">Contacts</string>
|
||||
<string name="websiteContact">Website</string>
|
||||
<string name="twitterContact">Twitter</string>
|
||||
<string name="vkontakteContact">Vkontakte</string>
|
||||
|
||||
<string name="linksTitle">Useful links</string>
|
||||
<string name="privacyPolicyLink">Privacy policy</string>
|
||||
<string name="repositoryLink">GitHub Repository</string>
|
||||
<string name="notsLink">\"TIES\" RF</string>
|
||||
<string name="sutLink">SPbSUT</string>
|
||||
|
||||
<string name="feedbackButton">Leave feedback</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,21 @@
|
||||
<resources>
|
||||
<style name="AppTheme.Light" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimary</item>
|
||||
<item name="colorAccent">@color/colorPrimary</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
|
||||
<item name="android:fitsSystemWindows">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Light.SplashScreen" parent="AppTheme.Light">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="android:navigationBarColor">@color/colorPrimary</item>
|
||||
<item name="android:windowLightNavigationBar">false</item>
|
||||
<item name="android:windowBackground">@color/colorPrimary</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
<item name="android:indeterminateTint">@android:color/white</item>
|
||||
</style>
|
||||
</resources>
|
||||