diff --git a/.github/workflows/Release.yaml b/.github/workflows/Release.yaml
index 5f05cc6..c90f6d3 100644
--- a/.github/workflows/Release.yaml
+++ b/.github/workflows/Release.yaml
@@ -16,7 +16,7 @@ jobs:
# Константы, используемые далее по тексту
env:
PROJ: ${{ github.event.repository.name }}
- TAG: '1.0'
+ TAG: '1.0.5'
steps:
# Проверка состава репозитория (без анализа, как может показаться)
diff --git a/.release/GrammarMustJoy.apk b/.release/GrammarMustJoy.apk
index bb4a890..ef89a12 100644
Binary files a/.release/GrammarMustJoy.apk and b/.release/GrammarMustJoy.apk differ
diff --git a/.release/GrammarMustJoy.exe b/.release/GrammarMustJoy.exe
new file mode 100644
index 0000000..9d0ceea
Binary files /dev/null and b/.release/GrammarMustJoy.exe differ
diff --git a/.release/GrammarMustJoy_ru_ru.html b/.release/GrammarMustJoy_ru_ru.html
new file mode 100644
index 0000000..f42a4c5
--- /dev/null
+++ b/.release/GrammarMustJoy_ru_ru.html
@@ -0,0 +1,43 @@
+
+
+Проект Grammar must joy: общие сведения | GrammarMustJoy
+
+
+
+
+
Проект Grammar must joy: общие сведения
+
ƒ RD AAOW FDL; 6.07.2024; 0:02
+
+
Существует огромное количество групп и сообществ, коллекционирующих юмор во всех его формах. Но вряд ли многие
+из них могут похвастаться грамотностью текстов и подписей. И речь не о случаях, когда именно её отсутствие есть
+основа для шутки. Это может быть даже не их вина – при огромном количестве скриншотов вместо обычных постов
+этого в принципе сложно добиться.
+
Что ж, попробуем это исправить. Долгое время мы собирали понравившиеся записи, преобразуя их в приятную глазу
+форму и выкладывая на своей стене. Просто так, чтобы отвлечься от текучки. Теперь огромный, образовавшийся почти
+за семь лет запас доступен на этом канале вместе с новыми отечественными и зарубежными текстами.
+
Казалось бы, зачем это нужно? И всё-таки результат того сто́ит. Контент от этих манипуляций становится только
+лучше. Но при этом возвращаются возможность текстового поиска по знакомым словам и скорость загрузки на особо
+медленных девайсах. К тому же, благодаря долгому отбору в коллекции остались лишь самые «стойкие» экспонаты.
+
Вещание сообщества уже давно ведётся в Telegram, используя менее удобную платформу ВК в качестве
+зеркала. Контент тот же, но, благодаря возможностям мессенджера во многих записях исправлены старые ошибки
+и огрехи форматирования.
+
Кроме того, мы создали приложение-клиент для этого сообщества. Его главная особенность – способность извлекать
+записи в случайном порядке из всего архива сообщества, исключая повторения, а также быстро и просто ими делиться.
+Оно исключает необходимость ручного пролистывания новостной ленты.
+
Итак, добро пожаловать в Grammar must joy!
+
+
Пара моментов:
+
+
Да, контент не всегда будет (читайте: почти никогда не будет) оригинальным. И если он вдруг окажется на пути
+чьего-нибудь копирайта, мы просим извещать нас об этом. Такой контент нам... будет изыматься из ленты.
+
Записи могут быть разными. Конечно, без шока, браззерс и прочей неразрешёнки. Но всё-таки 18+!
+
Если мы всё же где-то допускаем ошибки (что вероятно), просим также нам об этом сообщать. При таком названии группы
+не хочется ударять в грязь лицом.
+
Мы следуем Политике социальных сообществ. Поэтому рекламы в обозримом
+будущем здесь не будет. Да и комментарии тут ни к чему. Но советы, пожелания и конструктивная критика приветствуются
+(в комментариях головного сообщества).
+
+
Очень надеемся, что Вам понравится юмор, который почти не подчёркивается Word’ом!
+
+
+
diff --git a/.release/Release.md b/.release/Release.md
index 8abf292..7c9ae36 100644
--- a/.release/Release.md
+++ b/.release/Release.md
@@ -1,2 +1,4 @@
-_Changes for v 1.0_:
-- Initial release as a stand-alone application
+_Changes for v 1.0.5_:
+- Добавлено отключение подписей текстов при копировании из приложения;
+- Исправлены некорректные вызовы некоторых разделов справки и поддержки;
+- Начальный релиз в качестве самостоятельного приложения
diff --git a/Changes.log b/Changes.log
index 99087e5..3be9872 100644
--- a/Changes.log
+++ b/Changes.log
@@ -1,5 +1,9 @@
Grammar must joy: changes log
+Version 1.0.5:
+• Добавлено отключение подписей текстов при копировании из приложения;
+• Исправлены некорректные вызовы некоторых разделов справки и поддержки
+
Version 1.0:
-• A stand-alone app (from uNot) has been made;
-• Publication on GitHub
+• Реализовано самостоятельное приложение на основе функционала uNot;
+• Приложение опубликовано на GitHub
diff --git a/src/GrammarMustJoyForm.cs b/src/GrammarMustJoyForm.cs
new file mode 100644
index 0000000..b10a0ea
--- /dev/null
+++ b/src/GrammarMustJoyForm.cs
@@ -0,0 +1,231 @@
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает главную форму приложения
+ ///
+ public partial class GrammarMustJoyForm: Form
+ {
+ // Переменные
+ private NotifyIcon ni = new NotifyIcon ();
+ private bool allowExit = false;
+ private bool hideWindow;
+
+ ///
+ /// Конструктор. Настраивает главную форму приложения
+ ///
+ public GrammarMustJoyForm (bool HideWindow)
+ {
+ // Инициализация
+ InitializeComponent ();
+
+ this.Text = ProgramDescription.AssemblyVisibleName;
+ this.CancelButton = BClose;
+ MainText.Font = new Font ("Calibri", 13);
+ if (!RDGenerics.AppHasAccessRights (false, false))
+ this.Text += RDLocale.GetDefaultText (RDLDefaultTexts.Message_LimitedFunctionality);
+ hideWindow = HideWindow;
+
+ // Принудительные параметры
+ if (!RDLocale.IsCurrentLanguageRuRu)
+ RDLocale.CurrentLanguage = RDLanguages.ru_ru;
+
+ if (GMJ.EnablePostSubscription)
+ GMJ.EnablePostSubscription = false;
+
+ /*ReloadNotificationsList ();
+ if TGT
+ GetGMJ.Visible = false;
+ else
+ GetGMJ.Visible = RDLocale.IsCurrentLanguageRuRu;
+ endif*/
+
+ // Получение настроек
+ RDGenerics.LoadWindowDimensions (this);
+ ReadMode.Checked = RDGenerics.GetSettings (readPar, false);
+ /*callWindowOnUrgents = RDGenerics.GetSettings (callWindowOnUrgentsPar, false);
+ */
+ try
+ {
+ FontSizeField.Value = RDGenerics.GetSettings (fontSizePar, 130) / 10.0m;
+ }
+ catch { }
+
+ // Настройка иконки в трее
+ ni.Icon = Properties.GrammarMustJoy.GMJNotifier16;
+ ni.Text = ProgramDescription.AssemblyVisibleName;
+ ni.Visible = true;
+
+ ni.ContextMenu = new ContextMenu ();
+
+ /*ni.ContextMenu.MenuItems.Add (new MenuItem (RDLocale.GetText ("MainMenuOption02"), ShowSettings));
+ ni.ContextMenu.MenuItems[0].Enabled = RDGenerics.AppHasAccessRights (false, true);*/
+
+ ni.ContextMenu.MenuItems.Add (new MenuItem (
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_AppAbout), AboutService));
+ ni.ContextMenu.MenuItems.Add (new MenuItem (
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_Exit), CloseService));
+
+ ni.MouseDown += ShowHideFullText;
+ ni.ContextMenu.MenuItems[1].DefaultItem = true;
+ }
+
+ private void GrammarMustJoyForm_Shown (object sender, EventArgs e)
+ {
+ // Скрытие окна настроек
+ GrammarMustJoyForm_Resize (null, null);
+ if (hideWindow)
+ this.Hide ();
+
+ /*// Запуск
+ MainTimer.Interval = (int)ProgramDescription.MasterFrameLength * 4;
+ MainTimer.Enabled = true;*/
+ }
+
+ // Завершение работы службы
+ private void CloseService (object sender, EventArgs e)
+ {
+ allowExit = true;
+ this.Close ();
+ }
+
+ private void GrammarMustJoyForm_FormClosing (object sender, FormClosingEventArgs e)
+ {
+ // Остановка службы
+ if (allowExit)
+ {
+ // Остановка
+ ni.Visible = false;
+ /*MainTimer.Enabled = false;
+
+ // Освобождение ресурсов
+ ns.Dispose ();*/
+ }
+
+ // Скрытие окна просмотра
+ else
+ {
+ this.Hide ();
+ e.Cancel = true;
+ }
+ }
+
+ // О приложении
+ private void AboutService (object sender, EventArgs e)
+ {
+ RDGenerics.ShowAbout (false);
+ }
+
+ // Отображение / скрытие полного списка оповещений
+ private void ShowHideFullText (object sender, MouseEventArgs e)
+ {
+ // Работа только с левой кнопкой мыши
+ if (e.Button != MouseButtons.Left)
+ return;
+
+ /*// Отмена состояния сообщений
+ ns.HasUrgentNotifications = false;*/
+
+ // Обработка состояния
+ if (this.Visible)
+ {
+ this.Close ();
+ }
+ else
+ {
+ this.Show ();
+ this.TopMost = true;
+ this.TopMost = false;
+ MainText.ScrollToCaret ();
+ }
+ }
+
+ // Закрытие окна просмотра
+ private void BClose_Click (object sender, EventArgs e)
+ {
+ /*// Отмена состояния сообщений
+ ns.HasUrgentNotifications = false;*/
+
+ this.Close ();
+ }
+
+ // Переход в режим чтения и обратно
+ private void ReadMode_CheckedChanged (object sender, EventArgs e)
+ {
+ // Изменение состояния
+ if (ReadMode.Checked)
+ {
+ MainText.ForeColor = RDGenerics.GetInterfaceColor (RDInterfaceColors.LightGrey);
+ MainText.BackColor = RDGenerics.GetInterfaceColor (RDInterfaceColors.DefaultText);
+ }
+ else
+ {
+ MainText.ForeColor = RDGenerics.GetInterfaceColor (RDInterfaceColors.DefaultText);
+ MainText.BackColor = RDGenerics.GetInterfaceColor (RDInterfaceColors.LightGrey);
+ }
+
+ // Запоминание
+ RDGenerics.SetSettings (readPar, ReadMode.Checked);
+ }
+ private const string readPar = "Read";
+
+ // Изменение размера формы
+ private void GrammarMustJoyForm_Resize (object sender, EventArgs e)
+ {
+ MainText.Width = this.Width - 38;
+ MainText.Height = this.Height - 87;
+
+ ButtonsPanel.Top = MainText.Top + MainText.Height - 1;
+ }
+
+ // Сохранение размера формы
+ private void GrammarMustJoyForm_ResizeEnd (object sender, EventArgs e)
+ {
+ RDGenerics.SaveWindowDimensions (this);
+ }
+
+ // Запрос сообщения от GMJ
+ private void GetGMJExecutor (object sender, DoWorkEventArgs e)
+ {
+ e.Result = GMJ.GetRandomGMJ ();
+ }
+
+ private void GetGMJ_Click (object sender, EventArgs e)
+ {
+ // Запрос записи
+ RDGenerics.RunWork (GetGMJExecutor, null, "Запрос случайной записи...",
+ RDRunWorkFlags.CaptionInTheMiddle);
+ string s = RDGenerics.WorkResultAsString;
+ string item;
+
+ if (s != "")
+ item = s;
+ else
+ item = "GMJ не отвечает на запрос. Проверьте интернет-соединение";
+
+ // Отображение
+ // Добавление в главное окно
+ if ((MainText.Text.Length + item.Length > ProgramDescription.MasterLogMaxLength) &&
+ (MainText.Text.Length > item.Length)) // Бывает и так
+ MainText.Text = MainText.Text.Substring (item.Length, MainText.Text.Length - item.Length);
+ if (MainText.Text.Length > 0)
+ MainText.AppendText (RDLocale.RNRN + RDLocale.RN);
+
+ // Добавление и форматирование
+ MainText.AppendText (item.Replace (NotificationsSet.MainLogItemSplitter.ToString (), RDLocale.RN));
+ MainText.AppendText (RDLocale.RN);
+ }
+
+ // Изменение размера шрифта
+ private void FontSizeField_ValueChanged (object sender, EventArgs e)
+ {
+ MainText.Font = new Font (MainText.Font.FontFamily, (float)FontSizeField.Value);
+ RDGenerics.SetSettings (fontSizePar, (uint)(FontSizeField.Value * 10.0m));
+ }
+ private const string fontSizePar = "FontSize";
+ }
+ }
diff --git a/src/GrammarMustJoyProgram.cs b/src/GrammarMustJoyProgram.cs
new file mode 100644
index 0000000..45994b8
--- /dev/null
+++ b/src/GrammarMustJoyProgram.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Windows.Forms;
+
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает точку входа приложения
+ ///
+ public static class UniNotifierProgram
+ {
+ ///
+ /// Главная точка входа для приложения
+ ///
+ [STAThread]
+ public static void Main (string[] args)
+ {
+ // Инициализация
+ Application.EnableVisualStyles ();
+ Application.SetCompatibleTextRenderingDefault (false);
+
+ // Язык интерфейса и контроль XPUN
+ if (!RDLocale.IsXPUNClassAcceptable)
+ return;
+
+ // Проверка запуска единственной копии
+ if (!RDGenerics.IsAppInstanceUnique (true))
+ return;
+
+ // Контроль прав
+ if (!RDGenerics.AppHasAccessRights (true, false))
+ return;
+
+ // Отображение справки и запроса на принятие Политики
+ if (!RDGenerics.AcceptEULA ())
+ return;
+ RDGenerics.ShowAbout (true);
+
+ // Запуск
+ Application.Run (new GrammarMustJoyForm ((args.Length > 0) && (args[0] == "-h")));
+ }
+ }
+ }
diff --git a/src/android/AboutPage.xaml b/src/android/AboutPage.xaml
new file mode 100644
index 0000000..73df49e
--- /dev/null
+++ b/src/android/AboutPage.xaml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/AboutPage.xaml.cs b/src/android/AboutPage.xaml.cs
new file mode 100644
index 0000000..4dd32a6
--- /dev/null
+++ b/src/android/AboutPage.xaml.cs
@@ -0,0 +1,17 @@
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает страницу сведений о программе
+ ///
+ [XamlCompilation (XamlCompilationOptions.Compile)]
+ public partial class AboutPage: ContentPage
+ {
+ ///
+ /// Конструктор. Запускает страницу
+ ///
+ public AboutPage ()
+ {
+ InitializeComponent ();
+ }
+ }
+ }
diff --git a/src/android/AndroidManifest.xml b/src/android/AndroidManifest.xml
new file mode 100644
index 0000000..9ddab42
--- /dev/null
+++ b/src/android/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/App.xaml b/src/android/App.xaml
new file mode 100644
index 0000000..bf2970b
--- /dev/null
+++ b/src/android/App.xaml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/App.xaml.cs b/src/android/App.xaml.cs
new file mode 100644
index 0000000..785f079
--- /dev/null
+++ b/src/android/App.xaml.cs
@@ -0,0 +1,804 @@
+using Microsoft.Maui.Controls;
+
+[assembly: XamlCompilation (XamlCompilationOptions.Compile)]
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает функционал приложения
+ ///
+ public partial class App: Application
+ {
+ #region Общие переменные и константы
+
+ // Флаги прав доступа
+ private RDAppStartupFlags flags;
+
+ // Главный журнал приложения
+ private List masterLog;
+
+ // Управление центральной кнопкой журнала
+ private bool centerButtonEnabled = true;
+ private const string semaphoreOn = "●";
+ private const string semaphoreOff = "○";
+
+ /*// Индекс текущей опрашиваемой новости
+ private int getNotificationIndex = -1;*/
+
+ // Флаг завершения прокрутки журнала
+ private bool needsScroll = true;
+
+ // Сформированные контекстные меню
+ private List> tapMenuItems = new List> ();
+ private List specialOptions = new List ();
+
+ // Цветовая схема
+ private readonly Color
+ logMasterBackColor = Color.FromArgb ("#F0F0F0"),
+ logFieldBackColor = Color.FromArgb ("#80808080"),
+ logReadModeColor = Color.FromArgb ("#202020"),
+
+ settingsMasterBackColor = Color.FromArgb ("#FFF8F0"),
+ settingsFieldBackColor = Color.FromArgb ("#FFE8D0"),
+
+ solutionLockedBackColor = Color.FromArgb ("#F0F0F0"),
+
+ aboutMasterBackColor = Color.FromArgb ("#F0FFF0"),
+ aboutFieldBackColor = Color.FromArgb ("#D0FFD0");
+
+ #endregion
+
+ #region Переменные страниц
+
+ private ContentPage settingsPage, aboutPage, logPage;
+
+ private Label aboutLabel, fontSizeFieldLabel, aboutFontSizeField;
+
+ private Switch nightModeSwitch, newsAtTheEndSwitch, keepScreenOnSwitch,
+ enablePostSubscriptionSwitch;
+
+ private Button centerButton, scrollUpButton, scrollDownButton, menuButton;
+
+ private ListView mainLog;
+
+ private List pageVariants = new List ();
+
+ #endregion
+
+ #region Запуск и настройка
+
+ ///
+ /// Конструктор. Точка входа приложения
+ ///
+ public App ()
+ {
+ // Инициализация
+ InitializeComponent ();
+ flags = AndroidSupport.GetAppStartupFlags (RDAppStartupFlags.Huawei | RDAppStartupFlags.CanReadFiles |
+ RDAppStartupFlags.CanWriteFiles | RDAppStartupFlags.CanShowNotifications);
+
+ if (!RDLocale.IsCurrentLanguageRuRu)
+ RDLocale.CurrentLanguage = RDLanguages.ru_ru;
+
+ // Общая конструкция страниц приложения
+ MainPage = new MasterPage ();
+ MasterPage.AppEx = this;
+
+ settingsPage = AndroidSupport.ApplyPageSettings (new SettingsPage (), "SettingsPage",
+ "Настройки приложения", settingsMasterBackColor);
+ aboutPage = AndroidSupport.ApplyPageSettings (new AboutPage (), "AboutPage",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_AppAbout), aboutMasterBackColor);
+ logPage = AndroidSupport.ApplyPageSettings (new LogPage (), "LogPage",
+ "Журнал", logMasterBackColor);
+
+ AndroidSupport.SetMasterPage (MainPage, logPage, logMasterBackColor);
+
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.PolicyTip))
+ AndroidSupport.SetCurrentPage (settingsPage, settingsMasterBackColor);
+
+ // Настройки просмотра
+ AndroidSupport.ApplyLabelSettings (settingsPage, "AppSettingsLabel",
+ "Просмотр", RDLabelTypes.HeaderLeft);
+
+ AndroidSupport.ApplyLabelSettings (settingsPage, "KeepScreenOnLabel",
+ "Запретить переход в спящий режим,\nпока приложение открыто", RDLabelTypes.DefaultLeft);
+ keepScreenOnSwitch = AndroidSupport.ApplySwitchSettings (settingsPage, "KeepScreenOnSwitch",
+ false, settingsFieldBackColor, KeepScreenOnSwitch_Toggled, NotificationsSupport.KeepScreenOn);
+
+ AndroidSupport.ApplyLabelSettings (settingsPage, "EnablePostSubscriptionLabel",
+ "Добавлять ссылку на оригинал записи к тексту\nпри действиях «Скопировать» и «Поделиться»",
+ RDLabelTypes.DefaultLeft);
+ enablePostSubscriptionSwitch = AndroidSupport.ApplySwitchSettings (settingsPage,
+ "EnablePostSubscriptionSwitch", false, settingsFieldBackColor,
+ EnablePostSubscription_Toggled, NotificationsSupport.EnablePostSubscription);
+
+ #region Страница "О программе"
+
+ aboutLabel = AndroidSupport.ApplyLabelSettings (aboutPage, "AboutLabel",
+ RDGenerics.AppAboutLabelText, RDLabelTypes.AppAbout);
+
+ AndroidSupport.ApplyButtonSettings (aboutPage, "ManualsButton",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_ReferenceMaterials),
+ aboutFieldBackColor, ReferenceButton_Click, false);
+ AndroidSupport.ApplyButtonSettings (aboutPage, "HelpButton",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_HelpSupport),
+ aboutFieldBackColor, HelpButton_Click, false);
+ AndroidSupport.ApplyLabelSettings (aboutPage, "GenericSettingsLabel",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_GenericSettings),
+ RDLabelTypes.HeaderLeft);
+
+ AndroidSupport.ApplyLabelSettings (aboutPage, "RestartTipLabel",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Message_RestartRequired),
+ RDLabelTypes.Tip);
+
+ AndroidSupport.ApplyLabelSettings (aboutPage, "FontSizeLabel",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_InterfaceFontSize),
+ RDLabelTypes.DefaultLeft);
+ AndroidSupport.ApplyButtonSettings (aboutPage, "FontSizeInc",
+ RDDefaultButtons.Increase, aboutFieldBackColor, FontSizeButton_Clicked);
+ AndroidSupport.ApplyButtonSettings (aboutPage, "FontSizeDec",
+ RDDefaultButtons.Decrease, aboutFieldBackColor, FontSizeButton_Clicked);
+ aboutFontSizeField = AndroidSupport.ApplyLabelSettings (aboutPage, "FontSizeField",
+ " ", RDLabelTypes.DefaultCenter);
+
+ AndroidSupport.ApplyLabelSettings (aboutPage, "HelpHeaderLabel",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_AppAbout),
+ RDLabelTypes.HeaderLeft);
+ AndroidSupport.ApplyLabelSettings (aboutPage, "HelpTextLabel",
+ RDGenerics.GetEncoding (RDEncodings.UTF8).
+ GetString ((byte[])RD_AAOW.Properties.Resources.ResourceManager.
+ GetObject (RDLocale.GetHelpFilePath ())), RDLabelTypes.SmallLeft);
+
+ FontSizeButton_Clicked (null, null);
+
+ #endregion
+
+ // Страница журнала приложения
+ mainLog = (ListView)logPage.FindByName ("MainLog");
+ mainLog.BackgroundColor = logFieldBackColor;
+ mainLog.HasUnevenRows = true;
+ mainLog.ItemTapped += MainLog_ItemTapped;
+ mainLog.ItemTemplate = new DataTemplate (typeof (NotificationView));
+ mainLog.SelectionMode = ListViewSelectionMode.None;
+ mainLog.SeparatorVisibility = SeparatorVisibility.None;
+ mainLog.ItemAppearing += MainLog_ItemAppearing;
+ AndroidSupport.MasterPage.Popped += CurrentPageChanged;
+
+ centerButton = AndroidSupport.ApplyButtonSettings (logPage, "CenterButton", " ",
+ logFieldBackColor, CenterButton_Click, false);
+ centerButton.FontSize += 6;
+
+ scrollUpButton = AndroidSupport.ApplyButtonSettings (logPage, "ScrollUp",
+ RDDefaultButtons.Up, logFieldBackColor, ScrollUpButton_Click);
+ scrollDownButton = AndroidSupport.ApplyButtonSettings (logPage, "ScrollDown",
+ RDDefaultButtons.Down, logFieldBackColor, ScrollDownButton_Click);
+
+ // Режим чтения
+ AndroidSupport.ApplyLabelSettings (settingsPage, "ReadModeLabel",
+ "Тёмная тема для основного журнала", RDLabelTypes.DefaultLeft);
+ nightModeSwitch = AndroidSupport.ApplySwitchSettings (settingsPage, "ReadModeSwitch",
+ false, settingsFieldBackColor, NightModeSwitch_Toggled, NotificationsSupport.LogReadingMode);
+
+ // Расположение новых записей в конце журнала
+ AndroidSupport.ApplyLabelSettings (settingsPage, "NewsAtTheEndLabel",
+ "Добавлять новые записи в конец журнала", RDLabelTypes.DefaultLeft);
+ newsAtTheEndSwitch = AndroidSupport.ApplySwitchSettings (settingsPage, "NewsAtTheEndSwitch",
+ false, settingsFieldBackColor, NewsAtTheEndSwitch_Toggled, NotificationsSupport.LogNewsItemsAtTheEnd);
+
+ menuButton = AndroidSupport.ApplyButtonSettings (logPage, "MenuButton",
+ RDDefaultButtons.Menu, logFieldBackColor, SelectPage);
+
+ AndroidSupport.ApplyLabelSettings (settingsPage, "LogSettingsLabel",
+ "Главный журнал", RDLabelTypes.HeaderLeft);
+
+ // Режим чтения
+ NightModeSwitch_Toggled (null, null);
+
+ // Размер шрифта
+ fontSizeFieldLabel = AndroidSupport.ApplyLabelSettings (settingsPage, "FontSizeFieldLabel",
+ "", RDLabelTypes.DefaultLeft);
+ fontSizeFieldLabel.TextType = TextType.Html;
+
+ AndroidSupport.ApplyButtonSettings (settingsPage, "FontSizeIncButton",
+ RDDefaultButtons.Increase, settingsFieldBackColor, FontSizeChanged);
+ AndroidSupport.ApplyButtonSettings (settingsPage, "FontSizeDecButton",
+ RDDefaultButtons.Decrease, settingsFieldBackColor, FontSizeChanged);
+
+ FontSizeChanged (null, null);
+
+ // Запуск цикла обратной связи (без ожидания)
+ FinishBackgroundRequest ();
+
+ // Принятие соглашений
+ ShowStartupTips ();
+ }
+
+ // Исправление для сброса текущей позиции журнала
+ private async void CurrentPageChanged (object sender, EventArgs e)
+ {
+ if (AndroidSupport.MasterPage.CurrentPage != logPage)
+ return;
+
+ needsScroll = true;
+ await ScrollMainLog (newsAtTheEndSwitch.IsToggled, -1);
+ }
+
+ // Цикл обратной связи для загрузки текущего журнала, если фоновая служба не успела завершить работу
+ private bool FinishBackgroundRequest ()
+ {
+ // Ожидание завершения операции
+ SetLogState (false);
+
+ UpdateLogButton (true, true);
+
+ // Перезапрос журнала
+ if (masterLog != null)
+ masterLog.Clear ();
+ masterLog = new List (NotificationsSupport.GetMasterLog (true));
+
+ needsScroll = true;
+ UpdateLog ();
+
+ SetLogState (true);
+ return true;
+ }
+
+ // Метод отображает подсказки при первом запуске
+ private async void ShowStartupTips ()
+ {
+ // Контроль XPUN
+ if (!flags.HasFlag (RDAppStartupFlags.Huawei))
+ await AndroidSupport.XPUNLoop ();
+
+ // Требование принятия Политики
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.PolicyTip))
+ {
+ await AndroidSupport.PolicyLoop ();
+ NotificationsSet.TipsState |= NSTipTypes.PolicyTip;
+ }
+
+ // Подсказки
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.StartupTips))
+ {
+ await AndroidSupport.ShowMessage ("Добро пожаловать в клиент Grammar must joy!" + RDLocale.RNRN +
+ "• На этой странице Вы можете настроить поведение приложения." + RDLocale.RNRN +
+ "• Используйте системную кнопку «Назад», чтобы вернуться к журналу записей " +
+ "из любого раздела." + RDLocale.RNRN +
+ "• Используйте кнопку с семафором для получения случайных записей из сообщества GMJ",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_Next));
+
+ await AndroidSupport.ShowMessage ("Внимание!" + RDLocale.RNRN +
+ "• Некоторые устройства требуют ручного разрешения на доступ в интернет " +
+ "(например, если активен режим экономии интернет-трафика). Проверьте его, " +
+ "если запросы не будут работать правильно",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_OK));
+
+ NotificationsSet.TipsState |= NSTipTypes.StartupTips;
+ }
+ }
+
+ // Метод отображает остальные подсказки
+ private async Task ShowTips (NSTipTypes Type)
+ {
+ // Подсказки
+ string msg = "";
+ switch (Type)
+ {
+ case NSTipTypes.GoToButton:
+ msg = "Эта опция позволяет открыть источник выбранного уведомления в браузере";
+ break;
+
+ case NSTipTypes.ShareButton:
+ msg = "Эти опция позволяет поделиться текстом уведомления";
+ if (NotificationsSupport.EnablePostSubscription)
+ msg += ("." + RDLocale.RNRN +
+ "Обратите внимание, что приложение добавляет к текстам, которыми Вы делитесь, " +
+ "ссылки на источники информации (в целях соблюдения прав авторов). Рекомендуется " +
+ "не удалять их при распространении текстов с помощью этой функции");
+ break;
+
+ case NSTipTypes.MainLogClickMenuTip:
+ msg = "Все операции с текстами уведомлений доступны по клику на них в главном журнале";
+ break;
+
+ case NSTipTypes.KeepScreenOnTip:
+ msg = "Этот переключатель позволяет экрану оставаться активным, пока Вы читаете " +
+ "тексты уведомлений (т. е. пока приложение открыто)";
+ break;
+ }
+
+ await AndroidSupport.ShowMessage (msg, RDLocale.GetDefaultText (RDLDefaultTexts.Button_OK));
+ NotificationsSet.TipsState |= Type;
+ return true;
+ }
+
+ ///
+ /// Сохранение настроек программы
+ ///
+ protected override void OnSleep ()
+ {
+ AndroidSupport.StopRequested = true;
+ NotificationsSupport.SetMasterLog (masterLog);
+ AndroidSupport.AppIsRunning = false;
+ }
+
+ ///
+ /// Возврат в интерфейс при сворачивании
+ ///
+ protected override void OnResume ()
+ {
+ AndroidSupport.MasterPage.PopToRootAsync (true);
+
+ // Запуск цикла обратной связи (без ожидания, на случай, если приложение было свёрнуто, но не закрыто,
+ // а во время ожидания имели место обновления журнала)
+ AndroidSupport.AppIsRunning = true;
+ FinishBackgroundRequest ();
+ }
+
+ ///
+ /// Возврат в интерфейс из статичного оповещения (использует перенаправление в MasterPage)
+ ///
+ public void ResumeApp ()
+ {
+ OnResume ();
+ }
+
+ #endregion
+
+ #region Журнал
+
+ // Принудительное обновление лога
+ private void UpdateLog ()
+ {
+ mainLog.ItemsSource = null;
+ mainLog.ItemsSource = masterLog;
+ }
+
+ // Промотка журнала к нужной позиции
+ private async void MainLog_ItemAppearing (object sender, ItemVisibilityEventArgs e)
+ {
+ await ScrollMainLog (newsAtTheEndSwitch.IsToggled, e.ItemIndex);
+ }
+
+ private async Task ScrollMainLog (bool ToTheEnd, int VisibleItem)
+ {
+ // Контроль
+ if (masterLog == null)
+ return false;
+
+ if ((masterLog.Count < 1) || !needsScroll)
+ return false;
+
+ // Искусственная задержка
+ await Task.Delay (100);
+
+ // Промотка с повторением до достижения нужного участка
+ if (ToTheEnd)
+ {
+ if ((VisibleItem < 0) || (VisibleItem > masterLog.Count - 3))
+ needsScroll = false;
+
+ mainLog.ScrollTo (masterLog[masterLog.Count - 1], ScrollToPosition.MakeVisible, false);
+ }
+ else
+ {
+ if ((VisibleItem < 0) || (VisibleItem < 2))
+ needsScroll = false;
+
+ mainLog.ScrollTo (masterLog[0], ScrollToPosition.MakeVisible, false);
+ }
+
+ return true;
+ }
+
+ // Обновление формы кнопки журнала
+ private void UpdateLogButton (bool Requesting, bool FinishingBackgroundRequest)
+ {
+ bool red = Requesting && FinishingBackgroundRequest;
+ bool yellow = Requesting && !FinishingBackgroundRequest;
+ bool green = !Requesting && !FinishingBackgroundRequest;
+ bool dark = nightModeSwitch.IsToggled;
+
+ if (red || yellow || green)
+ {
+ centerButton.Text = (red ? semaphoreOn : semaphoreOff) + (yellow ? semaphoreOn : semaphoreOff) +
+ (green ? semaphoreOn : semaphoreOff);
+
+ if (red)
+ centerButton.TextColor = Color.FromArgb (dark ? "#FF4040" : "#D00000");
+ else if (yellow)
+ centerButton.TextColor = Color.FromArgb (dark ? "#FFFF40" : "#D0D000");
+ else
+ centerButton.TextColor = Color.FromArgb (dark ? "#40FF40" : "#00D000");
+ }
+ else
+ {
+ centerButton.Text = " ";
+ }
+ }
+
+ // Выбор оповещения для перехода или share
+ private async void MainLog_ItemTapped (object sender, ItemTappedEventArgs e)
+ {
+ // Контроль
+ MainLogItem notItem = (MainLogItem)e.Item;
+ if (!centerButtonEnabled || (notItem.StringForSaving == "")) // Признак разделителя
+ return;
+
+ // Сброс состояния
+ UpdateLogButton (false, false);
+
+ // Извлечение ссылки и номера оповещения
+ string notLink = "";
+ int l, r;
+ if (((l = notItem.Header.IndexOf (GMJ.NumberStringBeginning)) >= 0) &&
+ ((r = notItem.Header.IndexOf (GMJ.NumberStringEnding, l)) >= 0))
+ {
+ l += GMJ.NumberStringBeginning.Length;
+ notLink = GMJ.SourceRedirectLink + "/" + notItem.Header.Substring (l, r - l);
+ }
+
+ // Формирование меню
+ int variant = 0, menuItem;
+ if (tapMenuItems.Count < 1)
+ {
+ tapMenuItems.Add (new List {
+ "☍\tПоделиться текстом",
+ "❏\tСкопировать текст",
+ "Ещё...",
+ });
+ tapMenuItems.Add (new List {
+ "▷\tПерейти к источнику",
+ "☍\tПоделиться текстом",
+ "❏\tСкопировать текст",
+ "Ещё...",
+ });
+ tapMenuItems.Add (new List {
+ "✕\tУдалить из журнала",
+ });
+ }
+
+ // Запрос варианта использования
+ menuItem = (string.IsNullOrWhiteSpace (notLink) ? 0 : 1);
+ menuItem = await AndroidSupport.ShowList ("Выберите действие:",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_Cancel),
+ tapMenuItems[menuItem]);
+
+ if (menuItem < 0)
+ return;
+
+ variant = menuItem + 10;
+ if (string.IsNullOrWhiteSpace (notLink))
+ variant++;
+
+ // Контроль второго набора
+ if (variant > 12)
+ {
+ menuItem = await AndroidSupport.ShowList ("Выберите действие:",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_Cancel), tapMenuItems[2]);
+ if (menuItem < 0)
+ return;
+
+ variant += menuItem;
+ }
+
+ // Обработка (неподходящие варианты будут отброшены)
+ switch (variant)
+ {
+ // Переход по ссылке
+ case 0:
+ case 10:
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.GoToButton))
+ await ShowTips (NSTipTypes.GoToButton);
+
+ try
+ {
+ await Launcher.OpenAsync (notLink);
+ }
+ catch
+ {
+ AndroidSupport.ShowBalloon
+ (RDLocale.GetDefaultText (RDLDefaultTexts.Message_BrowserNotAvailable), true);
+ }
+ break;
+
+ // Поделиться
+ case 1:
+ case 11:
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.ShareButton))
+ await ShowTips (NSTipTypes.ShareButton);
+
+ await Share.RequestAsync ((notItem.Header + RDLocale.RNRN + notItem.Text +
+ (NotificationsSupport.EnablePostSubscription ? (RDLocale.RNRN + notLink) : "")).Replace ("\r", ""),
+ ProgramDescription.AssemblyVisibleName);
+ break;
+
+ // Скопировать в буфер обмена
+ case 2:
+ case 12:
+ RDGenerics.SendToClipboard ((notItem.Header + RDLocale.RNRN + notItem.Text +
+ (NotificationsSupport.EnablePostSubscription ? (RDLocale.RNRN + notLink) : "")).Replace ("\r", ""),
+ true);
+ break;
+
+ // Удаление из журнала
+ case 5:
+ case 13:
+ masterLog.RemoveAt (e.ItemIndex);
+ UpdateLog ();
+ break;
+ }
+
+ // Завершено
+ }
+
+ // Блокировка / разблокировка кнопок
+ private void SetLogState (bool State)
+ {
+ // Переключение состояния кнопок и свичей
+ centerButtonEnabled = State;
+
+ settingsPage.IsEnabled = aboutPage.IsEnabled = State;
+ if (!State)
+ {
+ settingsPage.BackgroundColor = aboutPage.BackgroundColor = solutionLockedBackColor;
+ }
+ else
+ {
+ settingsPage.BackgroundColor = settingsMasterBackColor;
+ aboutPage.BackgroundColor = aboutMasterBackColor;
+ }
+
+ // Обновление статуса
+ UpdateLogButton (!State, false);
+ }
+
+ // Добавление текста в журнал
+ private void AddTextToLog (string Text)
+ {
+ if (newsAtTheEndSwitch.IsToggled)
+ {
+ masterLog.Add (new MainLogItem (Text));
+
+ // Удаление верхних строк
+ while (masterLog.Count >= ProgramDescription.MasterLogMaxItems)
+ masterLog.RemoveAt (0);
+ }
+ else
+ {
+ masterLog.Insert (0, new MainLogItem (Text));
+
+ // Удаление нижних строк (здесь требуется, т.к. не выполняется обрезка свойством .MainLog)
+ while (masterLog.Count >= ProgramDescription.MasterLogMaxItems)
+ masterLog.RemoveAt (masterLog.Count - 1);
+ }
+ }
+
+ // Действия средней кнопки журнала
+ private void CenterButton_Click (object sender, EventArgs e)
+ {
+ if (!centerButtonEnabled)
+ {
+ AndroidSupport.ShowBalloon ("Пожалуйста, дождитесь ответа на запрос...", true);
+ return;
+ }
+
+ GetGMJ ();
+ }
+
+ private async void GetGMJ ()
+ {
+ // Блокировка на время опроса
+ SetLogState (false);
+ AndroidSupport.ShowBalloon ("Запрос случайной записи...", false);
+
+ // Запуск и разбор
+ AndroidSupport.StopRequested = false; // Разблокировка метода GetHTML
+ string newText = "";
+
+ for (int i = 0; i < 3; i++) // Минимизация возможных попаданий в пропуски
+ {
+ newText = await Task.Run (GMJ.GetRandomGMJ);
+
+ if (!newText.Contains (GMJ.SourceNoReturnPattern))
+ break;
+ }
+
+ if (newText == "")
+ {
+ AndroidSupport.ShowBalloon ("Grammar must joy не ответила на запрос. Проверьте интернет-соединение", true);
+ }
+ else if (newText.Contains (GMJ.SourceNoReturnPattern))
+ {
+ AndroidSupport.ShowBalloon (newText, true);
+ }
+ else
+ {
+ AddTextToLog (newText);
+ needsScroll = true;
+ UpdateLog ();
+ }
+
+ // Разблокировка
+ SetLogState (true);
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.MainLogClickMenuTip))
+ await ShowTips (NSTipTypes.MainLogClickMenuTip);
+ }
+
+ // Ручная прокрутка
+ private async void ScrollUpButton_Click (object sender, EventArgs e)
+ {
+ needsScroll = true;
+ await ScrollMainLog (false, -1);
+ }
+
+ private async void ScrollDownButton_Click (object sender, EventArgs e)
+ {
+ needsScroll = true;
+ await ScrollMainLog (true, -1);
+ }
+
+ // Выбор текущей страницы
+ private async void SelectPage (object sender, EventArgs e)
+ {
+ // Запрос варианта
+ if (pageVariants.Count < 1)
+ {
+ pageVariants = new List ()
+ {
+ "Настройки приложения",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Control_AppAbout),
+ };
+ }
+
+ int res = await AndroidSupport.ShowList (RDLocale.GetDefaultText (RDLDefaultTexts.Button_GoTo),
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_Cancel), pageVariants);
+ if (res < 0)
+ return;
+
+ // Вызов
+ switch (res)
+ {
+ case 0:
+ AndroidSupport.SetCurrentPage (settingsPage, settingsMasterBackColor);
+ break;
+
+ case 1:
+ AndroidSupport.SetCurrentPage (aboutPage, aboutMasterBackColor);
+ break;
+ }
+ }
+
+ #endregion
+
+ #region Основные настройки
+
+ // Включение / выключение фиксации экрана
+ private async void KeepScreenOnSwitch_Toggled (object sender, ToggledEventArgs e)
+ {
+ // Подсказки
+ if (!NotificationsSet.TipsState.HasFlag (NSTipTypes.KeepScreenOnTip))
+ await ShowTips (NSTipTypes.KeepScreenOnTip);
+
+ NotificationsSupport.KeepScreenOn = keepScreenOnSwitch.IsToggled;
+ }
+
+ // Включение / выключение добавления новостей с конца журнала
+ private void NewsAtTheEndSwitch_Toggled (object sender, ToggledEventArgs e)
+ {
+ // Обновление журнала
+ if (e != null)
+ NotificationsSupport.LogNewsItemsAtTheEnd = newsAtTheEndSwitch.IsToggled;
+
+ UpdateLogButton (false, false);
+ }
+
+ // Включение / выключение режима чтения для лога
+ private void NightModeSwitch_Toggled (object sender, ToggledEventArgs e)
+ {
+ if (e != null)
+ NotificationsSupport.LogReadingMode = nightModeSwitch.IsToggled;
+
+ if (nightModeSwitch.IsToggled)
+ {
+ logPage.BackgroundColor = mainLog.BackgroundColor = centerButton.BackgroundColor =
+ scrollUpButton.BackgroundColor = scrollDownButton.BackgroundColor =
+ menuButton.BackgroundColor = logReadModeColor;
+ NotificationsSupport.LogFontColor = logMasterBackColor;
+ }
+ else
+ {
+ logPage.BackgroundColor = mainLog.BackgroundColor = centerButton.BackgroundColor =
+ scrollUpButton.BackgroundColor = scrollDownButton.BackgroundColor =
+ menuButton.BackgroundColor = logMasterBackColor;
+ NotificationsSupport.LogFontColor = logReadModeColor;
+ }
+ scrollUpButton.TextColor = scrollDownButton.TextColor = menuButton.TextColor =
+ NotificationView.CurrentAntiBackColor;
+
+ // Принудительное обновление (только не при старте)
+ if (e != null)
+ {
+ needsScroll = true;
+ UpdateLog ();
+ }
+
+ // Цепляет кнопку журнала
+ UpdateLogButton (false, false);
+ }
+
+ // Изменение размера шрифта лога
+ private void FontSizeChanged (object sender, EventArgs e)
+ {
+ uint fontSize = NotificationsSupport.LogFontSize;
+
+ if (e != null)
+ {
+ Button b = (Button)sender;
+ if (AndroidSupport.IsNameDefault (b.Text, RDDefaultButtons.Increase) &&
+ (fontSize < AndroidSupport.MaxFontSize))
+ fontSize++;
+ else if (AndroidSupport.IsNameDefault (b.Text, RDDefaultButtons.Decrease) &&
+ (fontSize > AndroidSupport.MinFontSize))
+ fontSize--;
+
+ NotificationsSupport.LogFontSize = fontSize;
+ }
+
+ // Принудительное обновление
+ fontSizeFieldLabel.Text = string.Format ("Размер шрифта в журнале: {0:D}", fontSize.ToString ());
+
+ if (e != null)
+ {
+ needsScroll = true;
+ UpdateLog ();
+ }
+ }
+
+ // Включение / выключение подписи
+ private async void EnablePostSubscription_Toggled (object sender, ToggledEventArgs e)
+ {
+ // Подсказки
+ if (!enablePostSubscriptionSwitch.IsToggled)
+ await AndroidSupport.ShowMessage ("Мы не имеем ничего против отключения подписей у текстов. " +
+ "Честно. Всё-таки юмор – это общественное достояние, не допускающее каких-либо ограничений." +
+ RDLocale.RNRN + "Однако мы будем Вам весьма признательны, если Вы упомянете нас в качестве " +
+ "источника. Спасибо!",
+ RDLocale.GetDefaultText (RDLDefaultTexts.Button_OK));
+
+ NotificationsSupport.EnablePostSubscription = enablePostSubscriptionSwitch.IsToggled;
+ }
+
+ #endregion
+
+ #region О приложении
+
+ // Вызов справочных материалов
+ private async void ReferenceButton_Click (object sender, EventArgs e)
+ {
+ await AndroidSupport.CallHelpMaterials (RDHelpMaterials.ReferenceMaterials);
+ }
+
+ private async void HelpButton_Click (object sender, EventArgs e)
+ {
+ await AndroidSupport.CallHelpMaterials (RDHelpMaterials.HelpAndSupport);
+ }
+
+ // Изменение размера шрифта интерфейса
+ private void FontSizeButton_Clicked (object sender, EventArgs e)
+ {
+ if (sender != null)
+ {
+ Button b = (Button)sender;
+ if (AndroidSupport.IsNameDefault (b.Text, RDDefaultButtons.Increase))
+ AndroidSupport.MasterFontSize += 0.5;
+ else if (AndroidSupport.IsNameDefault (b.Text, RDDefaultButtons.Decrease))
+ AndroidSupport.MasterFontSize -= 0.5;
+ }
+
+ aboutFontSizeField.Text = AndroidSupport.MasterFontSize.ToString ("F1");
+ aboutFontSizeField.FontSize = AndroidSupport.MasterFontSize;
+ }
+
+ #endregion
+ }
+ }
diff --git a/src/android/LogPage.xaml b/src/android/LogPage.xaml
new file mode 100644
index 0000000..8c950dc
--- /dev/null
+++ b/src/android/LogPage.xaml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/android/LogPage.xaml.cs b/src/android/LogPage.xaml.cs
new file mode 100644
index 0000000..941161f
--- /dev/null
+++ b/src/android/LogPage.xaml.cs
@@ -0,0 +1,17 @@
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает страницу журнала программы
+ ///
+ [XamlCompilation (XamlCompilationOptions.Compile)]
+ public partial class LogPage: ContentPage
+ {
+ ///
+ /// Конструктор. Запускает страницу
+ ///
+ public LogPage ()
+ {
+ InitializeComponent ();
+ }
+ }
+ }
diff --git a/src/android/MainActivity.cs b/src/android/MainActivity.cs
new file mode 100644
index 0000000..72bde4a
--- /dev/null
+++ b/src/android/MainActivity.cs
@@ -0,0 +1,58 @@
+using Android.App;
+using Android.Content;
+using Android.Content.PM;
+using Android.OS;
+using Android.Views;
+
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает загрузчик приложения
+ ///
+ [Activity (Label = "Grammar must joy",
+ Icon = "@drawable/launcher_foreground",
+ Theme = "@style/SplashTheme",
+ MainLauncher = true,
+ ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
+ public class MainActivity: MauiAppCompatActivity
+ {
+ ///
+ /// Принудительная установка масштаба шрифта
+ ///
+ /// Существующий набор параметров
+ protected override void AttachBaseContext (Context @base)
+ {
+ if (baseContextOverriden)
+ {
+ base.AttachBaseContext (@base);
+ return;
+ }
+
+ Android.Content.Res.Configuration overrideConfiguration = new Android.Content.Res.Configuration ();
+ overrideConfiguration = @base.Resources.Configuration;
+ overrideConfiguration.FontScale = 0.9f;
+
+ Context context = @base.CreateConfigurationContext (overrideConfiguration);
+ baseContextOverriden = true;
+
+ base.AttachBaseContext (context);
+ }
+ private bool baseContextOverriden = false;
+
+ ///
+ /// Обработчик события создания экземпляра
+ ///
+ protected override void OnCreate (Bundle savedInstanceState)
+ {
+ // Отмена темы для splash screen
+ base.SetTheme (Microsoft.Maui.Controls.Resource.Style.MainTheme);
+
+ // Запуск
+ if (NotificationsSupport.KeepScreenOn)
+ this.Window.AddFlags (WindowManagerFlags.KeepScreenOn);
+
+ // Инициализация и запуск
+ base.OnCreate (savedInstanceState);
+ }
+ }
+ }
diff --git a/src/android/MainApplication.cs b/src/android/MainApplication.cs
new file mode 100644
index 0000000..b3a8351
--- /dev/null
+++ b/src/android/MainApplication.cs
@@ -0,0 +1,26 @@
+using Android.App;
+using Android.Runtime;
+
+namespace RD_AAOW
+ {
+#if DEBUG
+ [Application (Debuggable = true)]
+#else
+ [Application (Debuggable = false)]
+#endif
+ public class MainApplication: MauiApplication
+ {
+ ///
+ /// Конструктор экземпляра приложения
+ ///
+ public MainApplication (IntPtr handle, JniHandleOwnership ownership) : base (handle, ownership)
+ {
+ }
+
+ // Переопределение события создания экземпляра программы
+ protected override MauiApp CreateMauiApp ()
+ {
+ return MauiProgram.CreateMauiApp ();
+ }
+ }
+ }
diff --git a/src/android/MasterPage.xaml b/src/android/MasterPage.xaml
new file mode 100644
index 0000000..e283dc0
--- /dev/null
+++ b/src/android/MasterPage.xaml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/src/android/MasterPage.xaml.cs b/src/android/MasterPage.xaml.cs
new file mode 100644
index 0000000..49aff12
--- /dev/null
+++ b/src/android/MasterPage.xaml.cs
@@ -0,0 +1,42 @@
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает главный макет приложения
+ ///
+ [XamlCompilation (XamlCompilationOptions.Compile)]
+ public partial class MasterPage: NavigationPage
+ {
+ ///
+ /// Конструктор. Создаёт макет приложения
+ ///
+ public MasterPage ()
+ {
+ InitializeComponent ();
+ }
+
+ // Направление повторного вызова на метод Resume в приложении (не работает в MAUI)
+ protected override void OnAppearing ()
+ {
+ if (NotificationsSupport.AllowServiceToStart)
+ appEx.ResumeApp ();
+
+ base.OnAppearing ();
+ }
+
+ ///
+ /// Возвращает или задаёт действующий экземпляр текущего приложения
+ ///
+ public static App AppEx
+ {
+ get
+ {
+ return appEx;
+ }
+ set
+ {
+ appEx = value;
+ }
+ }
+ private static App appEx;
+ }
+ }
diff --git a/src/android/SettingsPage.xaml b/src/android/SettingsPage.xaml
new file mode 100644
index 0000000..0003e4e
--- /dev/null
+++ b/src/android/SettingsPage.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/android/SettingsPage.xaml.cs b/src/android/SettingsPage.xaml.cs
new file mode 100644
index 0000000..529177d
--- /dev/null
+++ b/src/android/SettingsPage.xaml.cs
@@ -0,0 +1,17 @@
+namespace RD_AAOW
+ {
+ ///
+ /// Класс описывает страницу решения
+ ///
+ [XamlCompilation (XamlCompilationOptions.Compile)]
+ public partial class SettingsPage: ContentPage
+ {
+ ///
+ /// Конструктор. Запускает страницу
+ ///
+ public SettingsPage ()
+ {
+ InitializeComponent ();
+ }
+ }
+ }