Евгений Колесников 3 年之前
父节点
当前提交
112394f06b
共有 12 个文件被更改,包括 299 次插入4 次删除
  1. 3 3
      articles/android_auth.md
  2. 293 0
      articles/f6_demo_1.md
  3. 二进制
      img/f6_001.png
  4. 二进制
      img/f6_002.png
  5. 二进制
      img/f6_003.png
  6. 二进制
      img/f6_004.png
  7. 二进制
      img/f6_005.png
  8. 二进制
      img/f6_006.png
  9. 二进制
      img/f6_007.png
  10. 二进制
      img/f6_008.png
  11. 2 0
      readme.md
  12. 1 1
      shpora/startActivity.md

+ 3 - 3
articles/android_auth.md

@@ -11,9 +11,9 @@
 **Содержание**
 
 * [API](#API)
-* [Первичная настройка приложения](#Первичная-настройка-приложения)
-* [Добавление альбомной ориентации](#Добавление-альбомной-ориентации)
-* [~~Модальный диалог авторизации~~](#Модальный-диалог-авторизации)
+* [Первичная настройка приложения](#первичная-настройка-приложения)
+* [Добавление альбомной ориентации](#добавление-альбомной-ориентации)
+* [~~Модальный диалог авторизации~~](#модальный-диалог-авторизации)
 * [Регулярные выражения](#Регулярные-выражения)
 * [HTTP-запросы, методы, форматы, заголовки.](#HTTP-запросы-методы-форматы-заголовки)
 * [Сохранение данных при работе приложения](#сохранение-данных-при-работе-приложения)

+ 293 - 0
articles/f6_demo_1.md

@@ -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-го андроида. Установите по требованиям ТЗ:
+
+![](../img/f6_001.png)
+
+>В работе необходимо использовать систему контроля версий 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
+
+Как пользоваться "фигмой" для получения ресурсов:
+
+![figma](../img/f6_002.png)
+
+1. Убедитесь, что смотрите правильный вариант вёрстки (нам нужен Андроид, Модуль 1)
+2. Экраны подписаны - нам нужен Launch Screen
+3. Выбираем элемент, который хотим экспортировать, например, логотип
+4. В правой панели выберите закладку **Export**
+5. Окройте **Preview** и убедитесь что элемент выбран весь целиком
+6. Убедитесь, что формат для экспорта **PNG** и жмите **кнопку** Export
+
+Браузер сохранить файл с тем же названием, что и у элемента вёрстки (`video-camera 2.png`). Переименуйте его, убрав пробелы, чёрточки и любую другую фигню, которая может помешать воспринимать ресурс по имени (оставьте только латинские буквы и цифры)
+
+Перенесите (drag-n-drop) полученный файл в ресурс drawable проекта
+
+![](../img/f6_004.png)
+
+в шаблон главного окна (`activity_main.xml`) добавьте элемент **ImageView** и отцентрируйте его горизонтально и вертикально (на этом окне можно не извращаться и не добавлять **LinearLayout**)
+
+![](../img/f6_003.png)
+
+Аналогично экспортируйте и внедрите в ресурсы картинку с текстом *WorldCinema*.
+
+Нигде в задании не сказано, как переходить на окно авторизации - добавьте таймер на несколько секунд, чтобы экперты успели рассмотреть вёрстку, и [переходите на следующее окно](../shpora/startActivity.md#таймер-обратного-отсчёта-и-переход-на-другое-activity)
+
+## Экран регистрации
+
+![](../img/f6_008.png)
+
+>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) на описание протокола АПИ и смотрим что там есть:
+
+![](../img/f6_005.png)
+
+В начале идёт общая информация. Нам тут пока интереснен только так называемый базовый URL. Здесь он находится в блоке Servers (`http://cinema.areas.su`), но может быть написан и просто текстом (как базовые урлы для картинок и видео).
+
+Дальше идут описания методов АПИ. Рассмотрим подробно метод Регистрация:
+
+![](../img/f6_006.png)
+
+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** с плагинами
+
+![](../img/f6_007.png)
+
+Пример отправки запроса я не привожу - вы уже должны написать его самостоятельно.
+
+### 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. Реализуйте данный экран в соответствии с макетом.
+>   * При нажатии на кнопку "Сохранить" необходимо сохранить новую коллекцию в памяти устройства и закрыть экран.
+>   * Необходимо проверять на пустоту поле для ввода названия коллекции. При отсутствии значения необходимо отобразить сообщение об ошибке.
+

二进制
img/f6_001.png


二进制
img/f6_002.png


二进制
img/f6_003.png


二进制
img/f6_004.png


二进制
img/f6_005.png


二进制
img/f6_006.png


二进制
img/f6_007.png


二进制
img/f6_008.png


+ 2 - 0
readme.md

@@ -309,6 +309,8 @@ https://office-menu.ru/uroki-sql Уроки SQL
 
 1. [ViewPager, Fragments, Tabs](./articles/pager.md)
 
+1. [Разбор заданий прошлых лет](./articles/f6_demo_1.md)
+
 <!-- 
 
 TODO

+ 1 - 1
shpora/startActivity.md

@@ -45,7 +45,7 @@ setResult(RESULT_OK, newIntent)
 finish()
 ```
 
-Таймер обратного отсчёта и переход на другое activity
+## Таймер обратного отсчёта и переход на другое activity
 
 ```kt
 override fun onCreate(savedInstanceState: Bundle?) {