GitHub – anajetli/android_login_registration_via_api: Lets create a simple and beautiful Login and Registration Form, Authenticate via API

Progressdialog

Как ты помнишь из многих наших статей про Android, обмен данными по сети — всегда долгий процесс, выполняющийся в отдельном потоке. Если никак не показывать пользователю, что приложение работает, он может подумать, будто что-то пошло не так. Для таких случаев есть класс ProgressDialog — элемент с анимированным крутящимся индикатором, демонстрирующий пользователю, что приложение чем-то занято.

mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage(getString(R.string.loading));
mProgressDialog.setIndeterminate(true);
mProgressDialog.show();

Его будет логично использовать при старте Activity, если приложению придется запрашивать у AS токен доступа. Когда вычисления закончатся, убрать этот элемент можно, вызвав метод hide.

if (opr.isDone()) {
  ...
} else {
  showProgressDialog();
  ...
}
mProgressDialog.hide();

Signinbutton

Чтобы дизайнеры всего мира не мучились, копируя логотип Google, в Google Play Services есть готовая реализация кнопки со знакомым каждому пользователю Android интерфейсом. Без лишних сомнений добавляем ее в верстку UI.

Silientsignin

Чтобы пользователю не приходилось снова и снова вводить свои учетные данные, в GSI доступна возможность тихого входа. Для этого нам понадобится метод onStart. При тихом входе проверяется состояние токена доступа к PR. Если пользователь недавно из нашего приложения входил в свой аккаунт, то все хорошо и приложение сразу же получит все нужные данные. В противном случае начнется обмен данными с AS и приложение с небольшой задержкой получит новый токен.

OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
if (opr.isDone()) {
  GoogleSignInResult result = opr.get();
  handleSignInResult(result);
} else {
  opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
    @Override
    public void onResult(GoogleSignInResult googleSignInResult) {
      handleSignInResult(googleSignInResult);
      ...
    }
  }
}
Рис. 3. Пример работы GSI
Рис. 3. Пример работы GSI

Spinner (выпадающий список)

Для реализации выпадающего списка (например, отфильтруем продукцию по типу) нужно в разметку добавить элемент Spinner

Создать класс ProductType (не data, а обычный) и переопределить в нём метод toString, чтобы в выпадающем списке показывало то что нам нужно

Похожее:  Передать показания счетчика воды Бийск (система Город - )

В классе основного окна (в том, где вы будете использовать выпадающий список) создайте список для хранения типов продукции, получите указатель на элемент и назначте ему адаптер:

Вторым параметром передаётся layout файл, в котором должен быть элемент TextView с id=”@android:id/text1″

Tutorial

You can watch the tutorial here

Библиотека gsi

OAuth работает практически из коробки. К примеру, Google реализовала этот механизм в подключаемой библиотеке. В данном случае этот механизм называется Google Sign-In (GSI), и реализован он в библиотеке Google Play Services. Для ее подключения необходимо изменить оба Gradle-файла, но мы уже не раз пользовались этой библиотекой, поэтому трудностей у тебя возникнуть не должно.

classpath 'com.google.gms:google-services:2.0.0-alpha6'
apply plugin: 'com.google.gms.google-services'
compile 'com.google.android.gms:play-services-auth:9.0.2'
Рис. 2. Google Play Services (с) Google
Рис. 2. Google Play Services (с) Google

Генерация ключа

Как и при работе с любым другим API из библиотеки Google Play Services, необходимо добавить в приложение конфигурационный файл, созданный на сайте Google. Обрати внимание, что в этот раз он будет жестко привязан к цифровой подписи устройства, на котором разрабатывается приложение. Поэтому если потом проект с созданным конфигом собрать на другом компьютере, то GSI работать не станет.

Добавление альбомной ориентации

В окне разметки (acticity_main.xml) перейдите в режим “design” и кликните кнопку “Orientation…” выбираем “Create Landscape Variation”

Система автоматически создаст Layout с альбомной ориентацией.

Учитывайте, что конструктор общий для всех ориентаций – при обращении к несуществующему объекту произойдет исключение.

Чтобы для разных ориентаций не рисовать одинаковую разметку (допустим список валют выводится в обеих ориентациях) можно вынести повторяющиеся куски разметки в отдельные файлы разметки (layout), а в нужные места вставить ссылку на них с помощью тега include

В паре с include используется тег merge. Если выделяемый кусок разметки содержит несколько отдельных элементов, то по стандартам XML мы должны завернуть их в один родительский. Как раз тег merge и можно использовать в таком случае. Он игнорируется при разборе разметки и ни как на неё не влияет.

Такие выделенные куски разметки можно использовать и в том случае, если один и тот же участок разметки используется в нескольких окнах.

Интегрируемся

Поскольку GSI будет сам отрисовывать интерфейс аутентификации пользователя, для его реализации целесообразно выделить класс — наследник компонента Activity. Начнем с класса GoogleApiСlient, объект которого должен быть создан раньше всех. Это базовый класс для работы с любыми функциями из Google Play Services. Когда все действия совершаются в Activity, то это подключение удобнее реализовать в методе onCreate.

protected void onCreate(Bundle savedInstanceState) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
  .enableAutoManage(this, this)
  .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
  .build();

Объект собирается с помощью сборщика GoogleApiClient.Builder, затем он самостоятельно подключается к серверу Google (enableAutoManage) и реализует API, в данном случае это GSI (константа GOOGLE_SIGN_IN_API).

Когда пользователь будет вводить пароль к своему аккаунту, Google еще раз спросит, точно ли приложению они нужны. Данные, которые запрашивает приложение, задаются заранее объектом gso — класс GoogleSignInOptions. Если приложению будут нужны email и данные из профиля пользователя, то объект собирается билдером вот с такими параметрами.

GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
  .requestEmail().requestProfile().build();

Ложка дегтя

Прибегнуть к сторонней базе данных пользователей легко и удобно, но разработчики все равно оставляют пользователю возможность создать новый аккаунт в приложении. И правильно делают, ведь у OAuth есть несколько слабых мест, которые могут быть не так очевидны.

  • 100%-я интернет-зависимость. При создании Activity объект GoogleApiClient сразу же начинает обмен данными с AS — очевидно, если у пользователя не будет доступа к сети, он не сможет зайти в свою учетную запись. Не забываем и про возможную сегментацию: в мире много мест, где есть интернет, но нет Google.
  • Правила игры могут поменяться. Сколько раз уже случалось, что популярный сервис или API внезапно исчезал или для него менялись правила доступа. К примеру, сервис GCM, о котором мы недавно писали, похоже, может не дожить до конца года: Google просит разработчиков переходить на технологию Firebase Cloud Messaging.
  • У приложения нет своих пользователей. С OAuth в чистом виде разработчик теряет представление о том, кто же вообще интересуется его программой. Конечно, можно после успешной авторизации дублировать данные на свой сервер, но они будут неполными и не всегда актуальными.

Первичная настройка приложения

  1. Создаем новый проект и сразу пытаемся его запустить.

    Если при сборке проекта выходит подобная ошибка, то нужно “понизить” версию зависимости, на которую ругается сборщик. Вообще эта ошибка означает, что какой-то пакет (в нашем случае androidx.appcompat:appcompat:1.4.0) требует более новой SDK, чем установлена. Но в AVD пока нет версий новее 30.

    Открываем build.graddle (Module...), находим нужный пакет в зависимостях (секция dependencies) и уменьшаем минорную версию пакета. Например, если была версия 1.4.0, то правим на 1.3.0.

  2. Устанавливаем иконку и название проекта.

Раньше как-то не пришло в голову, но часто графические ресурсы таскают сразу в приложении. По крайней мере в том задании про банк иконки валют и стран прилагались к заданию.

Это значит, что при получении, например, информации о валюте мы должны иконку тащить не из интернета, а из ресурсов.

Простая загрузка ресурса с ИЗВЕСТНЫМ id не сложная:

Но при получении данных из интернета мы имеем НАЗВАНИЕ ресурса (файла), а не его id в приложении. Для поиска id по имени есть отдельный метод:

Проект “база”. авторизация на сервере (basic auth, token). post-запросы. api.

Содержание

Реализация

Теоретическая часть закончена, запускаем Android Studio. OAuth сейчас очень популярна, на сайте проекта указано больше десяти крупных проектов, поддерживающих эту технологию. Как ты уже мог догадаться, общий принцип работы в каждом случае одинаков, различаются только названия классов и адреса удостоверяющих центров.

Поскольку мы пишем под Android, логично будет включить в наше приложение авторизацию через аккаунты Google — почти у каждого пользователя этой ОС есть такая учетная запись. Я знаю только одного человека, который не пользуется Google Play, но он параноик, а OAuth вообще не для них :).

Регулярные выражения

Возможно будет задание сделать форму регистрации. В этом случае обычно нужно сделать проверку введенных данных. Например, электронной почты

Сохранение данных при работе приложения

Специфика жизненного цикла activity такова, что при каждом чихе (смена ориентации, языка и т.п.) окно пересоздаётся То есть все наши данные теряются.

Гугл рекомендует нам пользоваться событиями onSaveInstanceState и onRestoreInstanceState которые вызываются, соответственно, при удалении activity и при восстановлении.

В параметрах этих функций передаётся объект, в который (и из которого) мы можем сохранить нужные нам данные и потом восстановить.

Но хранить в таком объекте можно только скалярные данные (т.е. базовые типы типа “целое”, “строка” и т.п.). Чтобы сохранить более-менее сложный объект, например, массив продукции, нужно использовать сериализацию/десериализацию.

Есть ещё более модная концепция LiveData – но там даже у меня происходит вывих мозга (это самая правильная концепция и если вы не дай бог станете таки андроид-разработчиками, то разобраться в ней надо).

Но ещё есть простой рабоче-крестьянский вариант с глобальными переменными. Само понятие глобальные переменные противоречит одному из основных постулатов ООП – инкапсуляция, поэтому его не рекомендуется использовать в сколько-нибудь серъезных проектах.

  1. Создайте класс MyApp, который наследуется от класа Application и внутри опишите публичные переменные, которые можно будет использовать во всех activity нашего приложения

  2. В манифесте в тег application добавьте атрибут android:name=”.MyApp”, где .MyApp это имя созданного нами ранее класса

  3. В классах, где нам нужны глобальные переменные создаем переменную, которая будет хранить указатель на MyApp

    В конструкторе класса проинициализируйте её

    И дальше в коде можете её использовать

Устройство oauth

С появлением технологии OAuth алгоритм регистрации на новом ресурсе для пользователя резко изменился. Теперь не надо заучивать очередные учетные данные, а можно несколькими нажатиями войти на сайт с помощью своей учетной записи одной из социальных сетей.

Если кратко, то «регистрация» на новом ресурсе с помощью OAuth выглядит так: между пользователем и ресурсом появляется посредник — сервер (чаще всего социальной сети), который получает уведомление пользователя о его намерении, а затем подтверждает ресурсу, что он уже знает этого пользователя и готов поделиться с ресурсом его учетными данными.

Рис. 1. Схема OAuth
Рис. 1. Схема OAuth

Фильтрация данных

В нормальных АПИ должна быть предусмотрена фильтрация набора данных на сервере, то есть вам придут только те данные, которые нужны. Но на моём сервере АПИ простенькое (зато универсальное), оно просто отдаёт ту таблицу в базе, которую вы запросите (нам это не страшно, так как таблицы у нас коротенькие).

Из-за этого возникает необходимость отфильтровать отображаемые данные.

Делается это элементарно – создаётся ещё один список соотвествующего типа. Причём основной список имеет смысл вынести в класс приложения (MyApp) и заполнять только при первом обращении к нему (size==0).

У нас задача немного осложняется тем, что связь между продуктами и материалами многие-ко-многим и для выковыривания материалов продукта надо считать ещё таблицу связей ProductMaterial.

Имеет смысл и таблицу связей сразу считать в класс приложения (глобальный), а при переходе в продукт составлять локальный список идентификаторов материалов и затем искать по таблице материалов те, которые входят в этот список (<List>.contains)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *