|
@@ -0,0 +1,293 @@
|
|
|
|
|
+# Разбор заданий прошлых лет
|
|
|
|
|
+
|
|
|
|
|
+Задание состоит из 3-х модулей:
|
|
|
|
|
+
|
|
|
|
|
+* **Модуль 1** - разработка приложения для смартфона. Основное задание.
|
|
|
|
|
+ - [Создание проекта](#создание-проекта)
|
|
|
|
|
+ - [Заставка](#экран-launch-screen)
|
|
|
|
|
+ - [Экран регистрации](#экран-регистрации)
|
|
|
|
|
+ - [Валидация полей](#поля-для-ввода-валидируются-на-пустоту)
|
|
|
|
|
+ - [Swagger](#запрос-регистрации-изучаем-работу-со-swagger)
|
|
|
|
|
+ - [Регулярные авражения](#email-проверяется-на-удовлетворение-шаблону-из-задания-02-балла)
|
|
|
|
|
+ - [Сохранение состояния](#при-первом-запуске-приложения-первым-отображается-signup-screen-при-последующих---signin-01-балла)
|
|
|
|
|
+ - [Экран авторизации](#экран-авторизации)
|
|
|
|
|
+
|
|
|
|
|
+* **Модуль 2** - разработка приложения для часов (может быть и для телевизора). Коротенькое задание (30 минут). Нужно просто создать приложение из двух окон + работа с интернетом.
|
|
|
|
|
+* **Модуль 3** - презентация.
|
|
|
|
|
+
|
|
|
|
|
+>**Модуль 1**: Смартфоны (разработка мобильного приложения для управления коллекцией фильмов)
|
|
|
|
|
+>
|
|
|
|
|
+>Необходимо разработать мобильное приложение для смартфона, удовлетворяющее следующим требованиям:
|
|
|
|
|
+>
|
|
|
|
|
+>Приложение должно поддерживать следующие версии ОС:
|
|
|
|
|
+> * Android 9.0 и новее
|
|
|
|
|
+> * iOS 13.0 и новее
|
|
|
|
|
+
|
|
|
|
|
+Задание универсальное, мы пишем для Android. Версия задаётся при создании проекта. По-умолчанию там стоит вообще для 5-го андроида. Установите по требованиям ТЗ:
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+>В работе необходимо использовать систему контроля версий Git.
|
|
|
|
|
+>
|
|
|
|
|
+>Для входа используйте учетную запись вида wsX, где X – это номер участника. Необходимо загрузить результаты >выполнения модуля в отдельную ветку с именем “Module-X”, где Х – это номер модуля. Для каждого проекта необходим отдельный репозиторий.
|
|
|
|
|
+
|
|
|
|
|
+Критерий | Баллы
|
|
|
|
|
+---------|:----:
|
|
|
|
|
+Создан репозиторий для проекта | 0.25
|
|
|
|
|
+Создана ветка для мобильного приложения | 0.35
|
|
|
|
|
+Проект корректно сохранен в ветку для мобильного приложения и не требует дополнительного разархивирования | 0.45
|
|
|
|
|
+**Итого** | 1.05
|
|
|
|
|
+
|
|
|
|
|
+1. Название учётной записи будет **userXX**, но об этом я ещё упомяну перед экзаменом (у нас используется готовый сервер **Gogs** с предустановленными пользователями)
|
|
|
|
|
+2. По веткам:
|
|
|
|
|
+
|
|
|
|
|
+ **Во-первых**, перед выполнением модуля сразу создайте соответствующий репозиторий (в этом задании названия репозиториев не оговорено): Module-1, Module-2, Module-3
|
|
|
|
|
+
|
|
|
|
|
+ **Во-вторых**, создайте в репозитории ветку: `git checkout -b Module-1`
|
|
|
|
|
+
|
|
|
|
|
+ **В-третьих**, при работе желательно в основной ветке (`Module-Х`) держать только **РАБОЧИЙ** код, чтобы было что оценивать не зависимо от того, на каком месте вы остановились. Для этого создайте ещё одну ветку, например **work** (`git checkout -b work`) и работайте в ней. После отладки какой-то фичи (например, сделали авторизацию) переключаетесь на основную ветку (Module-1), сливаете ветки и заливаете основную ветку в репозиторий. Затем возвращаетесь в основную ветку:
|
|
|
|
|
+
|
|
|
|
|
+ ```
|
|
|
|
|
+ git checkout Module-1
|
|
|
|
|
+ git merge work
|
|
|
|
|
+ git push origin Module-1
|
|
|
|
|
+ git checkout work
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+ итоговая структура репозиториев должна получиться примерно такая:
|
|
|
|
|
+
|
|
|
|
|
+ * Module-1 (репозиторий)
|
|
|
|
|
+ - Module-1 (ветка)
|
|
|
|
|
+ * Module-2 (репозиторий)
|
|
|
|
|
+ - Module-2 (ветка)
|
|
|
|
|
+ * Module-3 (репозиторий)
|
|
|
|
|
+ - Module-3 (ветка)
|
|
|
|
|
+
|
|
|
|
|
+>Необходимо корректно обрабатывать запросы к серверу. В случае получения ошибки от сервера или отсутствия соединения с сетью Интернет необходимо отобразить соответствующий текст ошибки с помощью диалогового окна.
|
|
|
|
|
+
|
|
|
|
|
+Тут вам придумывать ничего не надо - в классе **Http** уже есть пример, как перехватывать исключения
|
|
|
|
|
+
|
|
|
|
|
+>Необходимо строго следовать предложенному дизайну. Макеты приложения доступны по ссылке:
|
|
|
|
|
+https://www.figma.com/file/tD64TlCMQEqlr8OTv6bW2o/KOD1.4-Variant3?node-id=0%3A1
|
|
|
|
|
+>
|
|
|
|
|
+>~~Во время работы не будет доступа в Интернет,~~ кроме документации и API. Описание протокола API доступно по ссылке: https://app.swaggerhub.com/apis-docs/WorldSkills-MAD/WorldCinema/1.0.0
|
|
|
|
|
+
|
|
|
|
|
+>Проект приложения должен быть структурирован по экранам, то есть исходные файлы конкретного экрана должны быть в соответствующей папке. Общие для нескольких экранов классы необходимо поместить в папку common.
|
|
|
|
|
+
|
|
|
|
|
+Критерий | Баллы
|
|
|
|
|
+---------|:----:
|
|
|
|
|
+Проект приложения для смартфона имеет корректную структуру (все файлы распределены по папкам, соответствующим экранам приложения) | 0.5
|
|
|
|
|
+
|
|
|
|
|
+## Создание проекта
|
|
|
|
|
+
|
|
|
|
|
+>Необходимо реализовать следующий функционал:
|
|
|
|
|
+>
|
|
|
|
|
+>1. Создайте проект. Настройте иконку приложения согласно макету. Следует учесть разницу в отображении иконок на различных версиях операционной системы.
|
|
|
|
|
+
|
|
|
|
|
+Критерий | Баллы
|
|
|
|
|
+---------|:----:
|
|
|
|
|
+Создан проект мобильного приложения | 0.1
|
|
|
|
|
+Проект мобильного приложения успешно собирается | 0.1
|
|
|
|
|
+Иконка приложения соответствует Заданию | 0.2
|
|
|
|
|
+
|
|
|
|
|
+Про создание иконок смотри [первичную настройку приложения](./android_auth.md#первичная-настройка-приложения)
|
|
|
|
|
+
|
|
|
|
|
+## Экран Launch Screen
|
|
|
|
|
+
|
|
|
|
|
+>2. Реализуйте экран Launch Screen согласно макету. Текст должен быть отдельным элементом. Логотип приложения должен быть расположен по центру экрана.
|
|
|
|
|
+
|
|
|
|
|
+Критерий | Баллы
|
|
|
|
|
+---------|:----:
|
|
|
|
|
+Текст на Launch Screen является отдельным элементом | 0.1
|
|
|
|
|
+Логотип на Launch Screen отцентрирован | 0.1
|
|
|
|
|
+
|
|
|
|
|
+Как пользоваться "фигмой" для получения ресурсов:
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+1. Убедитесь, что смотрите правильный вариант вёрстки (нам нужен Андроид, Модуль 1)
|
|
|
|
|
+2. Экраны подписаны - нам нужен Launch Screen
|
|
|
|
|
+3. Выбираем элемент, который хотим экспортировать, например, логотип
|
|
|
|
|
+4. В правой панели выберите закладку **Export**
|
|
|
|
|
+5. Окройте **Preview** и убедитесь что элемент выбран весь целиком
|
|
|
|
|
+6. Убедитесь, что формат для экспорта **PNG** и жмите **кнопку** Export
|
|
|
|
|
+
|
|
|
|
|
+Браузер сохранить файл с тем же названием, что и у элемента вёрстки (`video-camera 2.png`). Переименуйте его, убрав пробелы, чёрточки и любую другую фигню, которая может помешать воспринимать ресурс по имени (оставьте только латинские буквы и цифры)
|
|
|
|
|
+
|
|
|
|
|
+Перенесите (drag-n-drop) полученный файл в ресурс drawable проекта
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+в шаблон главного окна (`activity_main.xml`) добавьте элемент **ImageView** и отцентрируйте его горизонтально и вертикально (на этом окне можно не извращаться и не добавлять **LinearLayout**)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+Аналогично экспортируйте и внедрите в ресурсы картинку с текстом *WorldCinema*.
|
|
|
|
|
+
|
|
|
|
|
+Нигде в задании не сказано, как переходить на окно авторизации - добавьте таймер на несколько секунд, чтобы экперты успели рассмотреть вёрстку, и [переходите на следующее окно](../shpora/startActivity.md#таймер-обратного-отсчёта-и-переход-на-другое-activity)
|
|
|
|
|
+
|
|
|
|
|
+## Экран регистрации
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+>3. Реализуйте экран SignUp Screen согласно макету:
|
|
|
|
|
+> * При нажатии на кнопку "Зарегистрироваться" необходимо проверять поля для ввода на пустоту, а также email на корректность (требования к email описаны в документации к API). При некорректном заполнении необходимо отобразить ошибку с помощью диалогового окна. Так же необходимо проверять равенство пароля и его повтора.
|
|
|
|
|
+> * При корректном заполнении формы необходимо отправлять запрос регистрации на сервер. При получении ошибки от сервера ее необходимо отобразить с помощью диалогового окна. При успешной регистрации нужно автоматически осуществить авторизацию и перейти на Main Screen.
|
|
|
|
|
+> * При нажатии на кнопку "У меня уже есть аккаунт" необходимо осуществлять переход на SignIn Screen.
|
|
|
|
|
+> * При первом запуске приложения после Launch Screen должен отображаться SignUp Screen. При последующих - SignIn Screen.
|
|
|
|
|
+
|
|
|
|
|
+Критерий | Баллы
|
|
|
|
|
+---------|:----:
|
|
|
|
|
+Реализован запрос регистрации. Запрос фиксируется сервером | 0.7
|
|
|
|
|
+Экран соответствует макету (оценивается верстка). Корректно реализованы 9 элементов: логотип, название, 5 текстовых полей, 2 кнопки (минус 0,1 за каждый отсутствующий или некорректный элемент) | 0.9
|
|
|
|
|
+Поля для ввода валидируются на пустоту (минус 0,1 за каждое поле без валидации) | 0.5
|
|
|
|
|
+Пароль и его повтор проверяются на равенство | 0.1
|
|
|
|
|
+При получении ошибки от сервера она отображается с помощью диалогового окна | 0.3
|
|
|
|
|
+Email проверяется на удовлетворение шаблону из Задания | 0.2
|
|
|
|
|
+При первом запуске приложения первым отображается SignUp Screen, при последующих - SignIn | 0.1
|
|
|
|
|
+**Итого** | 2.8
|
|
|
|
|
+
|
|
|
|
|
+Тут уже можно завернуть всё в **LinearLayout**
|
|
|
|
|
+
|
|
|
|
|
+### Поля для ввода валидируются на пустоту
|
|
|
|
|
+
|
|
|
|
|
+Можно было бы использовать [специальный компонент](http://developer.alexanderklimov.ru/android/layout/textinputlayout.php) с маской ввода, но в задания сказано что валидация делается вручную при клике на кнопку "Зарегистрироваться". Пример ниже:
|
|
|
|
|
+
|
|
|
|
|
+```kt
|
|
|
|
|
+// обратите внимание на наименование переменных: самоочевидное название + тип элемента
|
|
|
|
|
+lateinit var nameEditText: EditText
|
|
|
|
|
+lateinit var signUpButton: Button
|
|
|
|
|
+
|
|
|
|
|
+override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
|
+ super.onCreate(savedInstanceState)
|
|
|
|
|
+ setContentView(R.layout.activity_main)
|
|
|
|
|
+
|
|
|
|
|
+ // не забываем инициализировать ссылки на визуальные элементы
|
|
|
|
|
+ nameEditText = findViewById(R.id.nameEditText)
|
|
|
|
|
+ signUpButton = findViewById(R.id.signUpButton)
|
|
|
|
|
+
|
|
|
|
|
+ signUpButton.setOnClickListener {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (nameEditText.text.isEmpty())
|
|
|
|
|
+ throw Exception("Не заполнено имя пользователя")
|
|
|
|
|
+
|
|
|
|
|
+ тут остальные проверки
|
|
|
|
|
+
|
|
|
|
|
+ тут отправка запроса регистрации
|
|
|
|
|
+
|
|
|
|
|
+ } catch (e: Exception) {
|
|
|
|
|
+ // любую ошибку показываем на экране
|
|
|
|
|
+ AlertDialog.Builder(this)
|
|
|
|
|
+ .setTitle("Ошибка")
|
|
|
|
|
+ .setMessage(e.message)
|
|
|
|
|
+ .setPositiveButton("OK", null)
|
|
|
|
|
+ .create()
|
|
|
|
|
+ .show()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Запрос регистрации (изучаем работу со Swagger)
|
|
|
|
|
+
|
|
|
|
|
+**Swagger** - это фреймворк для спецификации *RESTful API*. Его прелесть заключается в том, что он дает возможность не только интерактивно просматривать спецификацию, но и отправлять запросы.
|
|
|
|
|
+
|
|
|
|
|
+Открываем [ссылку](https://app.swaggerhub.com/apis-docs/WorldSkills-MAD/WorldCinema/1.0.0) на описание протокола АПИ и смотрим что там есть:
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+В начале идёт общая информация. Нам тут пока интереснен только так называемый базовый URL. Здесь он находится в блоке Servers (`http://cinema.areas.su`), но может быть написан и просто текстом (как базовые урлы для картинок и видео).
|
|
|
|
|
+
|
|
|
|
|
+Дальше идут описания методов АПИ. Рассмотрим подробно метод Регистрация:
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+1. В заголовке указано какой метод и "путь" используются для запроса. К пути надо добавить "базовый урл" и получим полный адрес запроса: `http://cinema.areas.su/auth/register`
|
|
|
|
|
+
|
|
|
|
|
+1. В параметрах (Parameters) указываются параметры GET-запросов, передаваемые в строке запроса. У нас тут пусто.
|
|
|
|
|
+
|
|
|
|
|
+1. Тело запроса (Request body). Тут указано что тело запроса обязательно должно присутсвовать (required) и формат `application/json`
|
|
|
|
|
+
|
|
|
|
|
+ В теле запроса должна быть JSON-строка. Пример её можно посмотреть на вкладке **Example value**, но нам интереснее вкладка **schema** - на ней описаны типы данных (string), описание поля (что это такое вообще) и, возможно, обязательность использования поля. Например, для поля **email** расписан шаблон, которому оно должно соответсвовать.
|
|
|
|
|
+
|
|
|
|
|
+1. Коды ответов (Responses)
|
|
|
|
|
+
|
|
|
|
|
+ Тут надо быть внимательным, коды могут отличаться.
|
|
|
|
|
+
|
|
|
|
|
+Ну и самое приятное в Swagger - можно прямо в нём проверить результат работы. Кликаем кнопку "Try it out", вводим в открывшемся окне тело запроса и нажимаем выполнить (Execute). Таким образом нам не нужны ни **Postman** ни **VSCode** с плагинами
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+Пример отправки запроса я не привожу - вы уже должны написать его самостоятельно.
|
|
|
|
|
+
|
|
|
|
|
+### Email проверяется на удовлетворение шаблону из Задания (0.2 балла)
|
|
|
|
|
+
|
|
|
|
|
+Такая проверка делается с помощью регулярных выражений. Например:
|
|
|
|
|
+
|
|
|
|
|
+`^[a-z0-9]+@[a-z0-9]+\.[a-z0-9]{1,3}$`
|
|
|
|
|
+
|
|
|
|
|
+В задании сказано, что имя и домен второго уровня должны содержать только маленькие буквы и цифры.
|
|
|
|
|
+
|
|
|
|
|
+* знак `^` означает "начало строки"
|
|
|
|
|
+* Диапазон значений указывается в квадратных скобках.
|
|
|
|
|
+* Количество символов указывается после диапазона (`+` означает 1 и более)
|
|
|
|
|
+* знак `@` пишем как есть
|
|
|
|
|
+* точка является зарезервированным символом регулярных выражений (означает "любой символ"), поэтому её экранируем (перед точкой пишем обратный слеш)
|
|
|
|
|
+* домен первого уровня должен содержать не более 3-х символов - задаем количество в фигурных скобках (от 1 до 3)
|
|
|
|
|
+* знак `$` означает "конец строки"
|
|
|
|
|
+
|
|
|
|
|
+Пример проверки электронной почты:
|
|
|
|
|
+
|
|
|
|
|
+```kt
|
|
|
|
|
+val re = Regex("""[a-z0-9]+@[a-z0-9]+\.[a-z0-9]{1,3}""")
|
|
|
|
|
+val res = re.find(emailEditText.text.ToString())
|
|
|
|
|
+// если регулярное выражение ничего не найдет, то вернёт null
|
|
|
|
|
+if(res == null)
|
|
|
|
|
+ throw Exception("Электронная почта не соответствует шаблону")
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### При первом запуске приложения первым отображается SignUp Screen, при последующих - SignIn (0.1 балла)
|
|
|
|
|
+
|
|
|
|
|
+Для того чтобы узнать первый запуск или нет, нужно сохранить этот признак в постоянное хранилище. Пример работы с хранилищем есть в [шпаргалке](../shpora/preferences.md). Попробуйте разобраться самомтоятельно.
|
|
|
|
|
+
|
|
|
|
|
+## Экран авторизации
|
|
|
|
|
+
|
|
|
|
|
+>4. Реализуйте экран SignIn Screen согласно макету:
|
|
|
|
|
+> * При нажатии на кнопку "Войти" необходимо проверять поля для ввода на пустоту, а также email на корректность (требования к email описаны в документации к API). При некорректном заполнении необходимо отобразить ошибку с помощью диалогового окна. При корректном заполнении формы необходимо отправить на сервер соответствующий запрос.
|
|
|
|
|
+> * При нажатии на кнопку "Регистрация" необходимо осуществлять переход на SignUp Screen.
|
|
|
|
|
+> * При успешной авторизации необходимо осуществлять переход на экран Main Screen. При получении ошибки от сервера необходимо отобразить ее с помощью диалогового окна.
|
|
|
|
|
+>5. Реализуйте экран Main Screen согласно макету:
|
|
|
|
|
+> * На экране необходимо отобразить обложки фильмов из подборки «new» (информацию о фильмах необходимо запрашивать с сервера). Обложки должны быть отображены в виде карусели, необходимо реализовать возможность пролистывания с помощью жеста swipe.
|
|
|
|
|
+> * При пролистывании обложек название фильма в верхней части экрана должно меняться.
|
|
|
|
|
+> * При нажатии на обложку необходимо переходить на Chat Screen для соответствующего фильма.
|
|
|
|
|
+>6. Реализуйте экран Profile Screen согласно макету:
|
|
|
|
|
+> * Данные о пользователе необходимо запрашивать с сервера.
|
|
|
|
|
+> * При нажатии на кнопку "Изменить" необходимо реализовать изменение аватара пользователя:
|
|
|
|
|
+> - Пользователь выбирает источник фотографии (камера или Галерея), выбор источника следует реализовать с помощью диалогового окна. При выборе Галереи необходимо открывать Галерею. При выборе камеры осуществлять переход на экран Камеры не нужно.
|
|
|
|
|
+> - Пользователь выбирает фотографию.
|
|
|
|
|
+> - Фотография отправляется на сервер.
|
|
|
|
|
+> - В случае успеха аватар пользователя заменяется на новый, в случае ошибки она отображается с помощью диалогового окна.
|
|
|
|
|
+> * При нажатии на кнопку "Выход" необходимо осуществлять переход на экран авторизации.
|
|
|
|
|
+> * При нажатии на кнопку «Обсуждения» необходимо переходить на соответствующий экран.
|
|
|
|
|
+>7. Реализуйте экран Chat List Screen согласно макету:
|
|
|
|
|
+> * Информацию необходимо запрашивать с сервера (запрос чатов пользователя). Если информация с сервера содержит дубляжи – их необходимо удалить. Если ваш пользователь еще не имеет чатов, сервер присылает пустой список, отправьте сообщение от текущего пользователя в чате с id = 1 с помощью Swagger или Postman.
|
|
|
|
|
+> * В подзаголовке ячейки необходимо отобразить последнее сообщение в соответствующем чате + имя его автора. Текст сообщения необходимо обрезать до двух строк.
|
|
|
|
|
+> * Реализуйте отображение постеров к фильмам. Для получения постеров используйте подходящий запрос из API. Если постер для данного фильма нельзя получить из API - сгенерируйте абревиатуру по следующему правилу: если название фильма состоит и одного слова - необходимо взять первые две буквы слова; иначе - первые буквы первого и второго слова.
|
|
|
|
|
+> * При нажатии на ячейку необходимо осуществлять переход на Chat Screen для выбранного фильма.
|
|
|
|
|
+>8. Реализуйте экран Chat Screen согласно макету:
|
|
|
|
|
+> * Сообщения необходимо упорядочить от старых к новым сверху вниз. Для сегодняшних сообщений необходимо отобразить заголовок "Сегодня".
|
|
|
|
|
+> * "Облако" сообщения должно растягиваться по содержимому.
|
|
|
|
|
+> * Последовательно идущие сообщения одного автора необходимо группировать (расстояния между сообщениями должны быть меньше, как на макете).
|
|
|
|
|
+> * Реализуйте блок отправки сообщения как на макете. При вводе сообщения поле для ввода должно растягиваться по вертикали.
|
|
|
|
|
+> * При нажатии на кнопку "Отправить" необходимо отправить сообщение на сервер. При позитивном ответе от сервера необходимо отобразить сообщение в чате. При возникновении ошибки - отобразить ошибку с помощью диалогового окна.
|
|
|
|
|
+> * Необходимо валидировать поле для ввода на пустоту. При отсутствии текста сообщения необходимо отобразить ошибку с помощью диалогового окна.
|
|
|
|
|
+>9. Реализуйте экран Collections Screen согласно макету:
|
|
|
|
|
+> * При нажатии на иконку в правом верхнем углу необходимо переходить на экран Create Collection Screen.
|
|
|
|
|
+> * На экране необходимо отображать созданные коллекции. Информация о коллекциях должна храниться в памяти устройства. Необходимо хранить название коллекции и иконку.
|
|
|
|
|
+> * Реализуйте Swipe-to-delete для удаления коллекции, в том числе из памяти устройства.
|
|
|
|
|
+>10. Реализуйте экран Create Collection Screen согласно макету:
|
|
|
|
|
+> * При открытии экрана в качестве иконки должно быть выбрано случайное изображение из коллекции иконок.
|
|
|
|
|
+> * При нажатии на кнопку "Выбрать иконку" необходимо осуществлять переход на экран Icon Selection. Реализуйте данный экран в соответствии с макетом.
|
|
|
|
|
+> * При нажатии на кнопку "Сохранить" необходимо сохранить новую коллекцию в памяти устройства и закрыть экран.
|
|
|
|
|
+> * Необходимо проверять на пустоту поле для ввода названия коллекции. При отсутствии значения необходимо отобразить сообщение об ошибке.
|
|
|
|
|
+
|