Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Анимация](./animation.md) | [Содержание](../readme.md#практика-разработка-мобильных-приложений) | [Проект "каршеринг" Часть 2. Профиль пользователя.](./android_profile.md) # Проект "каршеринг" Часть 1. Регистрация/Авторизация. **Содержание** * [Техническое задание](#техническое-задание-задание-на-демо-экзамене) * [Первичная настройка приложения](#первичная-настройка-приложения) * [Регистрация и авторизация. Регулярные выражения. POST-запросы. Сохранение данных при работе приложения](#регистрация-и-авторизация-регулярные-выражения-post-запросы-сохранение-данных-при-работе-приложения) ## Техническое задание >Сразу уточню: задание скомпилировано из нескольких демо-экзаменов, могут быть нестыковки. **Модуль 1** Разработка мобильного приложения для бронирования автомобилей (каршеринг) Необходимо разработать мобильное приложение для смартфона, удовлетворяющее следующим требованиям: Приложение должно поддерживать следующие версии ОС: * Android 9.0 и новее * iOS 13.0 и новее В работе необходимо использовать систему контроля версий **Git**. Необходимо загрузить результаты выполнения модуля в отдельный репозиторий с именем `Module_X`, где `Х` – это номер модуля. Необходимо корректно обрабатывать запросы к серверу. В случае получения ошибки от сервера или отсутствия соединения с сетью Интернет необходимо отобразить соответствующий текст ошибки с помощью диалогового окна. Необходимо строго следовать предложенному дизайну. Макеты приложения доступны по ссылке: >Пока нет фигмы Описание протокола API в формате OpenAPI (Swagger) доступно по ссылке: https://swagger.kolei.ru/?url=https://carsharing.kolei.ru/swagger/api.yml Проект приложения должен быть структурирован по экранам, то есть исходные файлы конкретного экрана должны быть в соответствующей папке. Общие для нескольких экранов классы необходимо поместить в папку `common`. Необходимо реализовать следующий функционал: 1. Создайте проект. Настройте иконку приложения согласно макету. Следует учесть разницу в отображении иконок на различных версиях операционной системы. 1. Реализуйте экран *Launch Screen* согласно макету. ![](../img/mas1_01.png) Текст должен быть отдельным элементом (на скрине нет, но добавьте что-нибудь сами). Логотип приложения должен быть расположен по центру экрана. При первом запуске приложения после `Launch Screen` должен отображаться `SignUp Screen` (регистрация). При последующих - `SignIn Screen` (авторизация). >Я создание загрузочного экрана расписывать не буду, вы уже должны справиться с этим самостоятельно. 1. Реализуйте экран `SignUp Screen` согласно макету: ![](../img/mas1_03.png) >**Обратите внимание!** В оригинальном задании был вход по e-mail, но в моем АПИ нужно по телефону * При нажатии на кнопку "Зарегистрироваться" необходимо проверять поля для ввода на пустоту, а также телефон на корректность (требования к телефону описаны в документации к API). При некорректном заполнении необходимо отобразить ошибку с помощью диалогового окна. Так же необходимо проверять равенство пароля и его повтора. * При корректном заполнении формы необходимо отправлять запрос регистрации на сервер. При получении ошибки от сервера ее необходимо отобразить с помощью диалогового окна. При успешной регистрации нужно автоматически осуществить авторизацию и перейти на `Main Screen`. * При нажатии на кнопку "У меня уже есть аккаунт" необходимо осуществлять переход на `SignIn Screen`. На скрине этой кнопки нет - добавьте. * при нажатии на ссылку "Оферта" необходимо открыть web-страничку с текстом оферты. Этой кнопки тоже нет, тоже добавьте сами. >Создание этого и следующего экранов я тоже расписывать не буду, тут всё элементарно. Остановлюсь только на создании POST-запросов и регулярных выражениях. 1. Реализуйте экран `SignIn Screen` согласно макету: ![](../img/mas1_02.png) * При нажатии на кнопку "Войти" необходимо проверять поля для ввода на пустоту, а также телефон на корректность (требования к телефону описаны в документации к API). При некорректном заполнении необходимо отобразить ошибку с помощью диалогового окна. При корректном заполнении формы необходимо отправить на сервер соответствующий запрос. * При нажатии на кнопку "Регистрация" необходимо осуществлять переход на `SignUp Screen`. * При успешной авторизации необходимо осуществлять переход на экран `Main Screen` или `Profile Screen`, в зависимости от состояния пользователя (загружены ли права и другие необходимые документы). При получении ошибки от сервера необходимо отобразить её с помощью диалогового окна. 1. Реализуйте экран `Profile Screen` согласно макету: * На экране необходимо отобразить аватарку пользователя. Рамка вокруг аватарки должна быть разного цвета в зависимости от состояния профиля: - *желтый*: не загружены фотографии водительских прав или паспорта - *красный*: есть штрафы от ГИБДД или претензии от владельца каршеринга - *зелёный*: всё OK (активный профиль) * При нажатии на аватарку открыть приложение "Камера" и полученную миниатюру отправить на сервер и заменить ею существующую аватрку. * При нажатии на кнопки "Загрузить фото водительских прав" или "Загрузить файл паспорта" открыть приложение "Галерея" и выбранный файл отправить на сервер. 1. Реализуйте экран `Main Screen` согласно макету: * На экране необходимо отобразить карту с текущей позицией и маркерами автомобилей (список доступных автомобилей получить с сервера). * при клике на маркер автомобиля показывать (во всплывающем окне) краткую информацию об автомобиле: марка автомобиля, фото, кнопки "забронировать" и "маршрут". Кнопка "забронировать" должна быть только у активного пользователя * при клике на кнопку "Забронировать" открыть окно `Booking Screen` * ~~При клике на кнопку "маршрут" закрыть всплывающее окно и проложить маршрут от текущей позиции пользователя до выбранного автомобиля~~ * При нажатии на иконку профиля необходимо переходить на `Profile Screen`. 1. Реализуйте экран `Booking Screen` согласно макету: ## API Описание протокола API в формате OpenAPI (Swagger) доступно по ссылке: https://swagger.kolei.ru/?url=https://carsharing.kolei.ru/swagger/api.yml ### Примечания Для запросов требующих авторизации необходимо добавлять заголовок `Authorization: Bearer <ваш токен>` В качестве токена используется поле `userId`, получаемое в ответ на запрос `/login` >Маловероятно, но вдруг попадётся задача сделать "базовую авторизацию" (Basic Auth) >При таком методе авторизации в запрос нужно добавить заголовок `Authorization: Basic <логин:пароль в кодировке base64>` > >```kt >Base64.encodeToString( > "$login:$password".toByteArray(), > Base64.NO_WRAP) >``` > >Базовая авторизация позволяет использовать GET-запрос, т.к. в теле запроса ничего не предается ## Первичная настройка приложения 1. Создаем новый проект и сразу пытаемся его запустить. Если при сборке проекта выходит подобная ошибка, то нужно "понизить" версию зависимости, на которую ругается сборщик. Вообще эта ошибка означает, что какой-то пакет (в нашем случае **androidx.appcompat:appcompat:1.4.0**) требует более новой SDK, чем установлена. Но в **AVD** пока нет версий новее 30. ``` The minCompileSdk (31) specified in a dependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties) is greater than this module's compileSdkVersion (android-30). Dependency: androidx.appcompat:appcompat:1.4.0. ``` Открываем `build.graddle (Module...)`, находим нужный пакет в зависимостях (секция **dependencies**) и уменьшаем минорную версию пакета. Например, если была версия **1.4.0**, то правим на **1.3.0**. 2. Устанавливаем иконку и название проекта. * Установка иконки: В контекстном меню папки ресурсов (**res**) выбираем `New -> Image asset` ![](../img/04033.png) В появившемся окне в поле путь (**Path**) выбираем картинку (в нашем случае произвольную, а на демо-экзамене она должна быть в предоставленных ресурсах). Можно заодно задать имя ресурса в поле **Name**. ![](../img/04034.png) Открываем манифест (`manifest/AndroidManifest.xml`) и в теге **application** правим атрибут `android:icon`: ``` android:icon="@mipmap/ico" ``` * установка названия приложения В принципе достаточно поменять в манифесте атрибут ``` android:label="Название вашего приложения" ``` Но на всякий случай можно завернуть его в ресурсы (вдруг на экзамене будет под это отдельный критерий): В файле строковых ресурсов (`res/values/strings.xml`) добавить (исправить если уже есть) значение ```xml Восьмёрка ``` И в манифесте вставить указатель на строковый ресурс ``` android:label="@string/app_name" ``` Раньше как-то не пришло в голову, но часто графические ресурсы хранят сразу в приложении (в ресурсах). По крайней мере в одном из заданий про банк иконки валют и стран прилагались к заданию. Это значит, что при получении, например, информации о валюте мы должны иконку получать не из интернета, а из ресурсов. Простая загрузка ресурса с ИЗВЕСТНЫМ `id` не сложная: ```kt findViewById(R.id.ico) .setImageResource( R.drawable.ic_launcher_background ) ``` Но при получении данных из интернета мы имеем НАЗВАНИЕ ресурса (файла), а не его `id` в приложении. Для поиска `id` по имени есть отдельный метод: ```kt val icoId = resources .getIdentifier( "ic_launcher_background", // название ресурса "drawable", // раздел, в котором находится ресурс this.packageName // пакет ) // дальше как обычно findViewById(R.id.ico).setImageResource(icoId) ``` ## Добавление альбомной ориентации (include/merge) В окне разметки (acticity_main.xml) перейдите в режим "design" и кликните кнопку "Orientation..." выбираем "Create Landscape Variation" ![](/img/as023.png) Система автоматически создаст Layout с альбомной ориентацией. ![](/img/as024.png) >Учитывайте, что конструктор общий для всех ориентаций - при обращении к несуществующему объекту произойдет исключение. Чтобы для разных ориентаций не рисовать одинаковую разметку (допустим список валют выводится в обеих ориентациях) можно вынести повторяющиеся куски разметки в отдельные файлы разметки (layout), а в нужные места вставить ссылку на них с помощью тега **include** ```xml ``` В паре с **include** используется тег **merge**. Если выделяемый кусок разметки содержит несколько отдельных элементов, то по стандартам XML мы должны завернуть их в один родительский. Как раз тег **merge** и можно использовать в таком случае. Он игнорируется при разборе разметки и ни как на неё не влияет. ```xml