Pull to refresh
0
FUNCORP
Разработка развлекательных сервисов

Это птица? Это самолёт? Нет, это токен вашего пользователя летит на новый телефон

Reading time6 min
Views6.6K
Привет, Хабр!

Сегодня я расскажу про API для разработчиков от компании Google. Речь пойдёт о том, как не заставлять пользователя заново логиниться в приложении после переноса данных, или, выражаясь точнее, как использовать Android Account Transfer API.

Скорее всего, каждый из нас покупал новый смартфон и ему приходилось переносить на него всю важную информацию и приложения со старого. Сейчас этот процесс стал достаточно простым благодаря технологии Tap & Go. Но есть одно но. Приходится заново логиниться везде, где только можно. А что если это приложение типа фитнес-трекера, где залогинился один раз и забыл? Восстанавливать пароль? Опять головная боль. Вы можете сказать: «Но есть же Smart Lock!», и будете правы, но мы же должны учесть все кейсы. Что если человек забыл сохранить пароль? Или он просто параноик и не хранит пароли? Или в приложении не реализован Smart Lock? Думаю, что всегда найдутся причины забыть авторизационные данные. Но теперь решение есть, и вы сможете облегчить бремя переноса авторизационных данных ваших пользователей. Только вот оно не для всех. Да и эффективно заработает как минимум через год.

Из этого вытекает три вопроса:

1. Что вообще из себя представляет Account Transfer API?
2. Почему он не для всех?
3. Почему эффективно заработает минимум через год?

Необходимые требования


Как я уже говорил, Account Transfer API позволяет разработчикам передать авторизационные данные, включая токен и информацию об аккаунте, со старого телефона на новый. А что для этого нужно?

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

Что нужно для того, чтобы всё заработало?

1. Старый девайс с версией Android не ниже 4.0.1 (API Level 14).
Не такая уж и проблема, скажете вы, и я с этим соглашусь, но смотрим далее.

2. Новый девайс, на который мы хотим скопировать данные, должен быть под управлением ОС не ниже Android 8.0 (API Level 26).

Это и есть ответ на вопрос «Почему эффективно заработает минимум через год?». Да, в этом году многие девайсы получат обновление Android до Oreo, но вряд ли они будут продаваться с уже встроенной ОС с необходимым API Level. Скорее всего, на прилавках так и останутся стоковые версии, у которых «на борту» будет максимум Android Marshmallow. И после покупки обновлять их придётся вручную. А это ключевой момент для нашей фичи. Очень велика вероятность, что пользователь сначала перенесёт свои данные на устройство, а уже потом обновит его.

Чтобы не быть голословным, покажу вам информацию Google от 5 февраля 2018.



3. На обоих устройствах должен быть Google Play версии 11.2.0 и выше. Это не должно стать большой проблемой, если, конечно, вы не поддерживаете устройства без Google Play. На них трансфер организовать не получится.

4. Вы должны собрать свой APK, используя сервисы Google Play не ниже 11.2.0. Может стать проблемой только в случае, если у вас есть причины не использовать сервисы данной версии или не использовать сервисы вообще.

5. Необходим уже реализованный AbstractAccountAuthenticator, интегрированный с AccountManager. Есть отличный цикл статей на «Хабре» по реализации.

Как это работает?




При условии, что все требования выполнены, между телефонами создаётся безопасное проводное соединение или зашифрованное через Bluetooth. Если во время трансфера вы хотите проверить валидность токена у себя на бэкенде, то одно из устройств должно быть подключено к интернету.

Далее система отсылает Broadcast-сообщение (ACTION_START_ACCOUNT_EXPORT или ACTION_ACCOUNT_EXPORT_DATA_AVAILABLE) о готовности начать экспорт данных, при получении которого мы должны запустить Foreground Service, предназначенный для обмена данными. Т.е. нам нужно сформировать и отправить данные аккаунта пользователя через защищённое соединение на устройство-получатель.





После этого система присылает ещё одно Broadcast-сообщение (ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE) уже на устройство-получатель. Здесь также требуется запустить Foreground Service и обработать полученную информацию.



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



Реализация


Наконец вы решаетесь приступить к реализации.

Для начала необходимо подключить play-services-auth, если это не было сделано ранее.

dependencies {
  // For Account Transfer api, use SDK version 11.2.0 or higher
  compile 'com.google.android.gms:play-services-auth-base:<VERSION_NUMBER>'
}

Далее объявить в манифесте BroadcastReceiver и Service:

<receiver
     android:name=".AccountTransferBroadcastReceiver"
     android:enabled="true"
     android:exported="true" >
  <intent-filter>
     <action android:name="com.google.android.gms.auth.START_ACCOUNT_EXPORT" />
  </intent-filter>
  <intent-filter>
     <action android:name="com.google.android.gms.auth.ACCOUNT_IMPORT_DATA_AVAILABLE" />
  </intent-filter>
  <intent-filter>
     <action android:name="com.google.android.gms.auth.ACCOUNT_EXPORT_DATA_AVAILABLE" />
  </intent-filter>
</receiver>

<service android:name=".AuthenticatorService">
  <intent-filter>
     <action android:name="android.accounts.AccountAuthenticator"/>
  </intent-filter>
  <meta-data
        android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/authenticator"/>
</service>

Как было описано выше, сервис нужно запускать при получении соответствующих Broadcast-сообщений:

public class AccountTransferBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
     // Long running tasks, like calling Account Transfer API, shouldn't happen here. Start a
     // foreground service to perform long running tasks.
     Intent serviceIntent = AccountTransferService.getIntent(context, intent.getAction());
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(serviceIntent);
     } else {
        context.startService(serviceIntent);
     }
  }
}

Ну и соответственно, частичная реализация сервиса:

protected void onHandleIntent(Intent intent) {
  String action = intent.getAction();
  if (action == null) {
     return;
  }
 
  switch (action) {
     case AccountTransfer.ACTION_ACCOUNT_IMPORT_DATA_AVAILABLE:
        importAccount();
        return;
     case ACTION_START_ACCOUNT_EXPORT:
     case AccountTransfer.ACTION_ACCOUNT_EXPORT_DATA_AVAILABLE:
        exportAccount();
        return;
  }
}

private void importAccount() {
  // Handle to client object
  AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
 
  // Make RetrieveData api call to get the transferred over data.
  Task<byte[]> transferTask = client.retrieveData(ACCOUNT_TYPE);
  try {
     byte[] transferBytes = Tasks.await(transferTask, TIMEOUT_API, TIME_UNIT);
     //import logic
  } catch (ExecutionException | InterruptedException | TimeoutException | JSONException e) {
     client.notifyCompletion(
           ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
     return;
  }
  client.notifyCompletion(
        ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS);
 
}

private void exportAccount() {
  byte[] transferBytes = Foo.yourOwnMethodToGetAccountTransferBytes();
  AccountTransferClient client = AccountTransfer.getAccountTransferClient(this);
  if (transferBytes == null) {
     // Notifying is important.
     client.notifyCompletion(
           ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_SUCCESS);
     return;
  }
 
  // Send the data over to the other device.
  Task<Void> exportTask = client.sendData(ACCOUNT_TYPE, transferBytes);
  try {
     Tasks.await(exportTask, TIMEOUT_API, TIME_UNIT);
  } catch (ExecutionException | InterruptedException | TimeoutException e) {
     // Notifying is important.
     client.notifyCompletion(
           ACCOUNT_TYPE, AuthenticatorTransferCompletionStatus.COMPLETED_FAILURE);
     return;
  }
}

Более подробный пример можно найти в репозитории googlesamples.

Для тестирования Google предлагает запустить Setup Wizard командой.

$ adb shell am start -a android.intent.action.MAIN -n com.google.android.gms/.smartdevice.d2d.ui.TargetActivity

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

Подводя итог, отмечу, что Google хочет максимально сократить время, которое пользователь тратит на повторную авторизацию, и делает логичные шаги для этого. И хорошо бы поддержать их в этом направлении. Хоть дело и требует некоторых временных затрат на реализацию, но оно того стоит. Ведь дьявол кроется в деталях.

Интересная информация по этой теме:

Документация
Видеовведение
Пример реализации
Tags:
Hubs:
Total votes 19: ↑19 and ↓0+19
Comments8

Articles

Information

Website
funcorp.dev
Registered
Founded
Employees
101–200 employees
Location
Кипр
Representative
ulanana