Releases: thecattest/lyceum-reports-android
Синхронизация, Диаграммы, Многоязыковость, Редизайн, Иконка
Сервис
Создал сервис, который
- автоматически получает новые данные с сервера
- автоматически отправляет неотправленные из-за ошибки или отсутствия соединения изменения
Диаграммы
Сделал активность статистики с bottom navigation и navigation architecture component.
Табличку за день переписал на фрагмент и переместил туда, а также добавил фрагмент с диаграммой за класс.
Многоязыковость
Добавил поддержку следующих языков:
- английский
- украинский
- белорусский
- казахский
Переключается автоматически взависимости от системных настроек. Дефолтный - русский
Редизайн
Перенастроил цвета и шрифты, вынес все размеры в dimensions, переписал на Constraint layout все разметки
У приложения наконец-то появилась иконка!!!
Room - Новая Архитектура
Room
Почти полностью переписал архитектуру приложения, теперь всё работает по схемке на картинке.
Новые data классы, новое api, переписано взаимодействие с данными и перекидывание их в разные места системы.
Всё по красоте, а количество багов я старательно пытался сместить поближе к нулю. Дополнительно есть кучка разных элементов для improving user expirience, snackbar'ы всякие, диалоги например.
Аккаунты для тестов
- админ
thecattest
/superpass
- редактор
editor
/editor
- наблюдатель
viewer
/viewer
Новый внешний вид страницы авторизации
Обновил внешний вид страницы авторизации
Всё из прошлого релиза, естественно, осталось на месте. Я не буду дублировать описания оттуда, так что для начала советую ознакомиться с возможностями предыдущей версии приложения, а потом возвращаться сюда.
Помимо обновления layout, я организовал передачу куков авторизации в запросах не через методы data services, а через interceptor, причем для инициализации retrofit сделал отдельный класс.
Новая авторизация
Старая авторизация
Таблица за день
В этом релизе я добавил таблицу отстутствующих за день по всем классам, а также вагон и маленькую тележку оптимизаций кода
Всё из прошлого релиза, естественно, осталось на месте. Я не буду дублировать описания оттуда, так что для начала советую ознакомиться с возможностями предыдущей версии приложения, а потом возвращаться сюда.
Итак, теперь в верхней панели на главной странице появилась новая кнопка - таблица отстутствующих за день. Она отображается только если у пользователя есть разрешение просматривать таблицу, разрешение приходит вместе с данными с сервера.
Карточки
Обновил внешний вид карточек на главной странице: скругления, border. Для этого пришлось создать в drawable фигуры и ставить их как background карточке и заголовку карточки отдельно, тк ему также нужен и фон.
DefaultCallback
Создал ранее и уже несколько раз переработал этот абстрактный класс. По сути, это базовый класс для создания callback для call.enque, который я использую вместо retrofit2.Callback.
- В нем реализуются методы его родителя - onResponse и onFailure, причём в onResponse используется switch по кодам ответа. 200, 401, 403, 500 и default.
- В 403 вызывается loginmanager.handleNotAuthorized и пользователя сразу кидает на авторизацию.
- В default показывается snackbar с соответствующим сообщением.
- В остальных для обработки вызываются методы типа onResponse200, onResponse401 и тд.
- Также в нём есть метод onPostExecute, который вызовется в конце выполнения запроса при любом исходе, тоже иногда нужен, например, чтобы разблокировать кнопки, заблокированные на время выполнения запроса.
- Кроме того, обработчики обернуты в try/catch.
Класс спасает от дублирования кода и в некоторой степени упрощает и делает более прозрачным процесс обработки запроса.
StatusManager
В приложении я создал layout'ы загрузки и ошибки на сервере для улучшения UX. Так как везде они одинаковые, я задумался о том, чтобы создать класс-хелпер, благодаря которому мне будет проще менять статусы, а также не придется дублировать код. Так появился StatusManager.
- Я инициализирую его в onCreate активности, передавая на вход параметры
- Activity, через который потом будут искаться loading и server error layout'ы,
- View главного layout'а приложения, так как его нужно будет скрывать, меняя статусы,
- и, если я хочу использовать serverErrorLayout, передаю onClickListener для кнопки "попробовать снова".
- В коренной layout активности я добавляю две строчки
<include layout="@layout/fragment_loading"/>
<include layout="@layout/fragment_server_error"/>
И всё, теперь я могу использовать методы setMainLayout, setLoadingLayout, setServerErrorLayout в любом месте и радоваться жизни.
Более того, класс реализует методы saveToBundle и loadFromBundle, которые я могу вызывать в соответствующих обработчиках ивентов активности, и тогда статус будет ещё и сохраняться и загружаться.
DatePickerManager
Когда я создавал активность таблицы отсутствующих за день, я понял, что для создания и использования datePicker пишу практически один и тот же код, причём довольно громоздкий. Я решил создать класс-хелпер и здесь тоже.
- Я инициализирую его в onCreate активности, передавая на вход параметры
- Context, который нужен для доступа к String Resources, так как там находится дефолтный текст кнопки-триггера и формат даты для сервера
- View кнопки-триггера, которая будет запускать date picker
- fragment manager
- callback, который будет вызван при нажатии ок во время выбора даты
Теперь я могу использовать getDate для получения выбранной даты уже в формате для отправки на сервер
При помощи isEmpty проверять, выбрана ли какая-то дата, тк это нужно, например, для включения/выключения SwipeRefreshLayout
Сохранять и грузить date picker в/из bundle, используя методы, реализованные в классе
Bundles
Благодаря реализации в классах менеджеров и data models методов для работы с bundle, код обработчиков соответствующих ивентов в активностях стал очень чистым
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if(statusManager.loadFromBundle(savedInstanceState)) {
summaryWithPermissions.loadFromBundle(savedInstanceState);
updateSummaryView();
}
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
summaryWithPermissions.saveToBundle(outState);
statusManager.saveToBundle(outState);
}
Отмена отправки запросов на сервер
При отправке запроса я кладу call в глобальную переменную класса, так что при нажатии кнопки назад, например, могу отменить запрос. Такая ситуация отдельно обрабатывается в DefaultCallback и всё ок
String Resources
Почистил strings.xml: всерьёз взялся за нейминг строк и вынес туда вообще все строковые ресурсы, использующиеся в программе, так что теперь его даже читать приятно и всё по правилам.
Фрагмент
<string name="app_name">Lyceum Reports</string>
<!-- Page titles -->
<string name="title_main">Отсутствующие</string>
<string name="title_summary_day">Отсутствующие: день</string>
<string name="title_authorization">Авторизация</string>
<string name="title_day">День</string>
<!-- Snackbars titles -->
<string name="snackbar_no_info">За этот день данных нет</string>
<string name="snackbar_no_absent">Все в классе</string>
<string name="snackbar_request_cancelled">Отмена</string>
<string name="snackbar_server_error">Ошибка, попробуйте ещё раз позднее</string>
<string name="snackbar_server_error_code">Ошибка при выполнении запроса :( код </string>
<string name="snackbar_server_error_code_500">Сервер выдал ошибку 500, попробуйте позднее</string>
<string name="snackbar_server_ok">Сработало :)</string>
<!-- Buttons -->
<string name="button_edit">редактировать</string>
<string name="button_submit_day_default">отправить</string>
<string name="button_date_picker_trigger">выбрать дату</string>
<string name="button_logout">Выйти</string>
<!-- Summary -->
<string name="summary_card_yesterday">Вчера</string>
<string name="summary_card_today">Сегодня</string>
<string name="summary_status_no_info">Нет данных</string>
Рефакторинг кода в DataModels, Activities, Managers
Структурировал всё ещё больше, повысил читаемость, а в DataModels добавил методы saveToBundle и loadFromBundle
Главная с кнопкой и без. Карточки новые
Активность с табличкой, пустая при открытии и уже прогруженная. Иконка загрузки и окно ошибки тут тоже показывается.
Пользователи, авторизация, права, а также тема!
В этом релизе я добавил авторизацию и права пользователей.
Всё из прошлого релиза, естественно, осталось на месте. Я не буду дублировать описания оттуда, так что для начала советую ознакомиться с возможностями предыдущей версии приложения, а потом возвращаться сюда.
Итак, я перешёл на новую версию API, которая требует авторизации и регулирует права пользователей. Теперь без входа в аккаунт делать будет нельзя ничего. Я реализовал активность формы авторизации и LoginManager, предоставляющий функции для работы с авторизацией.
LoginManager
По сути, нужно просто получить куки после отправки логина и пароля и дальше добавлять их во все запросы, а в случае ошибки 403 - запускать LoginActivity. LoginManager используется для сохранения и загрузки куков в/из SharedPreferences, а также может перенаправить пользователя на авторизацию или выйти из аккаунта, то бишь стереть куки.
Роли
Описаны вот тут.
В приложении пользователям соответственно ролям скрываются или показываются отдельные элементы интерфейса: кнопки редактирования или карточки классов. Таблички будут реализованы в будущих версиях приложения, так что о них речи пока не идёт.
Тема
Подобрал цвета для светлой и темной темы приложения, так что теперь всё выглядит приятнее и больше соответствует веб-версии сервиса
Можно потрогать!
вот тестовые аккаунты
- Админ
thecattest
/superpass
- Редактор
editor
/editor
- Руководитель
viewer
/viewer
Итак, а теперь десяток скринов для наглядного представления
(14 actually)
Авторизация
Главная и кнопочка logout вверху
Страница отсутствующих и пара информационных сообщений
Окна ошибок в случае, если сервер недоступен. Кстати, фрагменты
Темная тема тоже выглядит хорошо
Руководитель
может только смотреть, кнопок редактирования нет
Редактор
Главная страница и редактирование отсутствующих
Реализованы две активности: главная страница с карточками классов и страница класса, на которой можно просматривать и редактировать информацию об отсутствующих в классе за тот или иной день.
Короткая демка приложения:
https://user-images.githubusercontent.com/57992909/117150358-bfe95c80-adc0-11eb-858f-fa9b6eca4b3f.mp4
Длинная демка, в которой показан почти весь функционал:
https://photos.app.goo.gl/VSWNoqotKFqkXnbu5
Данные сохраняются в бандлы, так что, например, при перевороте экрана ничего не потеряются.
Появляются всплывающие уведомления с поясняющими сообщениями Ошибка при загрузке выглядит так