Как поддерживать непрерывность работы на нескольких устройствах на Googlebook

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

Функция Продолжить на другом устройстве в Android 17 (уровень API 37) и более поздних версиях позволяет панели задач Googlebook показывать значок приложения (подсказку) для действия, которое пользователь выполняет на своем телефоне, находящемся рядом. Пользователи могут продолжить работу на Googlebook одним нажатием.

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

Как выбрать поток передачи

Прежде чем включить передачу данных между устройствами, решите, как будет открываться каждое действие в приложении на телефоне, когда пользователь выберет предложение "Продолжить на устройстве" на Googlebook:

Процесс передачи запроса Целевой уровень в Googlebook Поддерживаемые устройства Key API
Передача данных между приложениями Нативное приложение Android, запущенное в окне на компьютере Приложения, оптимизированные для больших экранов и многооконного режима на компьютере, в Google Play HandoffActivityData.Builder
Передача данных между приложениями с переходом на сайт Нативное приложение Android, если оно установлено, или URL в браузере по умолчанию на компьютере. Приложения, оптимизированные для работы в собственном окне на компьютере, но требующие надежного браузера в качестве резервного варианта, если приложение не установлено на устройстве Chromebook. setFallbackUri
Переход на сайт URL в браузере для компьютера по умолчанию (или перехваченный нативным приложением, если для него настроены ссылки на приложения для Android) Рабочие процессы, в которых веб-приложение является основным интерфейсом для компьютеров HandoffActivityData.createWebHandoff

Как включить передачу для действия

Функция "Продолжить на другом устройстве" отключена по умолчанию и настраивается для каждого действия отдельно. Выполните следующие действия при каждой отправке на телефоне:

  1. Вызовите setHandoffEnabled и передайте true, когда операция загрузит состояние, необходимое для передачи. После включения этой функции система может запрашивать данные для передачи, когда действие выполняется в активном режиме или переходит в фоновый.
  2. Переопределите onHandoffActivityDataRequested, чтобы возвращать ненулевой объект HandoffActivityData. Вы можете в любое время запросить isHandoffEnabled, чтобы проверить, активна ли передача для действия.
import android.app.Activity
import android.app.HandoffActivityData
import android.app.HandoffActivityDataRequestInfo
import android.os.Bundle
import android.os.PersistableBundle

class DocumentEditorActivity : Activity() {

    private var documentId: String = "doc_42"
    private var scrollOffsetPx: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Enable handoff once the activity is ready to transfer state.
        setHandoffEnabled(true, null)
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        val extras = PersistableBundle().apply {
            putString(EXTRA_DOCUMENT_ID, documentId)
            putInt(EXTRA_SCROLL_OFFSET, scrollOffsetPx)
        }

        return HandoffActivityData.Builder(getComponentName())
            .setExtras(extras)
            .build()
    }

    companion object {
        const val EXTRA_DOCUMENT_ID = "extra_document_id"
        const val EXTRA_SCROLL_OFFSET = "extra_scroll_offset"
    }
}

Статус передачи посылки

Система вызывает onHandoffActivityDataRequested в двух случаях:

  • Передача активного приложения. Приложение работает на телефоне в активном режиме, и пользователь запускает передачу с панели задач Googlebook.
  • Переход в фоновый режим. Активность переходит в фоновый режим на телефоне, и система кеширует полезную нагрузку передачи на случай, если пользователь позже выберет подсказку в Google Книгах.

Как проверить, активен ли запрос на передачу

Проверьте HandoffActivityDataRequestInfo с помощью isActiveRequest, чтобы отличить активную передачу, инициированную пользователем, от сохранения в фоновом режиме:

  • Если isActiveRequest возвращает значение true, пользователь активно запросил продолжение на другом устройстве, пока действие видно.
  • Если isActiveRequest возвращает false, система кэширует состояние, когда операция останавливается.

Если на телефоне показывается визуальный индикатор, подтверждающий, что задача переносится на Googlebook, запланируйте обновление интерфейса асинхронно, чтобы onHandoffActivityDataRequested возвращало значение без задержки.

Как прикрепить дополнительные данные о состоянии и резервную веб-страницу

Полезная нагрузка HandoffActivityData содержит следующие сведения:

  • Целевой компонент (обязательный параметр для передачи данных между приложениями): передайте this.getComponentName() (ComponentName действия) или явный ComponentName(this, TargetActivity::class.java) в HandoffActivityData.Builder, чтобы указать, какое действие будет запущено в Google Книгах.
  • Дополнительные параметры (необязательно). Передайте PersistableBundle в setExtras(), содержащий идентификаторы и координаты интерфейса (например, идентификаторы открытых документов, ключи активных вкладок или позиции прокрутки). Размер пакета должен быть меньше 50 КБ.
  • Резервный URI (необязательно). Передайте веб-Uri в setFallbackUri, чтобы Googlebook мог открыть задачу в браузере по умолчанию, если ваше приложение Android не установлено.
import android.app.Activity
import android.app.HandoffActivityData
import android.app.HandoffActivityDataRequestInfo
import android.app.HandoffActivityParams
import android.net.Uri
import android.os.Bundle
import android.os.PersistableBundle

class ArticleReaderActivity : Activity() {

    private var articleId: String = "android-desktop-windowing"
    private var readingProgressPercent: Int = 45

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setHandoffEnabled(
            true,
            HandoffActivityParams.Builder()
                .setAllowHandoffWithoutPackageInstalled(true)
                .build()
        )
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        if (handoffRequestInfo.isActiveRequest()) {
            window.decorView.post {
                onHandoffStartedToSecondaryDevice()
            }
        }

        val extras = PersistableBundle().apply {
            putString(EXTRA_ARTICLE_ID, articleId)
            putInt(EXTRA_READING_PROGRESS, readingProgressPercent)
        }

        val fallbackWebUri = Uri.Builder()
            .scheme("https")
            .authority("example.com")
            .appendPath("articles")
            .appendPath(articleId)
            .appendQueryParameter("progress", readingProgressPercent.toString())
            .build()

        return HandoffActivityData.Builder(getComponentName())
            .setExtras(extras)
            .setFallbackUri(fallbackWebUri)
            .build()
    }

    private fun onHandoffStartedToSecondaryDevice() {
        // Save local drafts and update phone UI without blocking handoff.
    }

    companion object {
        const val EXTRA_ARTICLE_ID = "extra_article_id"
        const val EXTRA_READING_PROGRESS = "extra_reading_progress"
    }
}

Как настроить передачу в веб-интерфейс

Если основное приложение для компьютеров на Googlebook – это веб-приложение, передайте HandoffActivityParams с setAllowHandoffWithoutPackageInstalled(true) в setHandoffEnabled и вызовите HandoffActivityData.createWebHandoff вместо HandoffActivityData.Builder:

import android.app.Activity
import android.app.HandoffActivityData
import android.app.HandoffActivityDataRequestInfo
import android.app.HandoffActivityParams
import android.net.Uri
import android.os.Bundle

class WebDashboardActivity : Activity() {

    private var projectId: String = "proj_901"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setHandoffEnabled(
            true,
            HandoffActivityParams.Builder()
                .setAllowHandoffWithoutPackageInstalled(true)
                .build()
        )
    }

    override fun onHandoffActivityDataRequested(
        handoffRequestInfo: HandoffActivityDataRequestInfo
    ): HandoffActivityData {
        val webUri = Uri.Builder()
            .scheme("https")
            .authority("app.example.com")
            .appendPath("projects")
            .appendPath(projectId)
            .build()

        return HandoffActivityData.createWebHandoff(webUri)
    }
}

Как получать и восстанавливать состояние на устройстве Googlebook

Когда пользователь выбирает вариант "Продолжить на устройстве" на панели задач Googlebook, система запускает целевое приложение ComponentName в окне на рабочем столе и добавляет дополнительные данные PersistableBundle к запуску Intent.

На Googlebook при обработке входящей передачи учитывайте многооконный режим и многопанельные макеты:

  1. Извлеките дополнительные данные, переданные при переходе. В методах onCreate и onNewIntent принимающего класса activity извлеките дополнительные данные из входящего объекта Intent. Обработка onNewIntent гарантирует, что если ваше действие использует режим запуска singleTop или переходит к существующему окну в многооконном режиме, окно обновится до переданного элемента.
  2. Адаптируйте состояние для многопанельных макетов. Состояние, которое занимало полноэкранный режим на телефоне, часто сопоставляется с выбранным элементом в макете "список и подробные сведения" или вспомогательной панели на Googlebook. Заполните основной список и панель подробностей, чтобы в многооконном режиме была вся необходимая информация.
  3. Сохраняйте восстановленное состояние при изменении размера окна. Храните переданное состояние в ViewModel (SavedStateHandle) или rememberSaveable в Jetpack Compose, чтобы при изменении размера окна в свободной форме на устройстве Googlebook восстановленный сеанс не сбрасывался.
import android.app.Activity
import android.content.Intent
import android.os.Bundle

class DocumentEditorReceiverActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Restore from the handoff intent only on initial launch so subsequent
        // desktop window configuration changes use saved instance state.
        if (savedInstanceState == null) {
            handleHandoffIntent(intent)
        }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        handleHandoffIntent(intent)
    }

    private fun handleHandoffIntent(incomingIntent: Intent?) {
        val documentId = incomingIntent?.getStringExtra(EXTRA_DOCUMENT_ID)
            ?: return
        val scrollOffsetPx = incomingIntent.getIntExtra(EXTRA_SCROLL_OFFSET, 0)

        openDocumentInDesktopWorkspace(
            documentId = documentId,
            initialScrollOffsetPx = scrollOffsetPx
        )
    }

    private fun openDocumentInDesktopWorkspace(
        documentId: String,
        initialScrollOffsetPx: Int
    ) {
        // Load the document into the list-detail layout and restore scroll position.
    }

    companion object {
        const val EXTRA_DOCUMENT_ID = "extra_document_id"
        const val EXTRA_SCROLL_OFFSET = "extra_scroll_offset"
    }
}

Как проверить синхронизацию задач между телефоном и Googlebook

Чтобы проверить реализацию:

  1. Подготовьте оба устройства
    • Войдите в один и тот же аккаунт Google на телефоне Android (с Android 17 или более поздней версии) и Googlebook (или тестовом устройстве, работающем в многооконном режиме).
    • Включите Bluetooth и подключите оба устройства к одной сети Wi-Fi.
    • На обоих устройствах откройте Настройки > Подключенные устройства > Настройки подключения > Сервисы для нескольких устройств > Продолжение работы, следуйте инструкциям и включите Задачи. Полный список требований к среде приведен в разделе Настройка и тестирование.
  2. Как установить приложение
    • Чтобы проверить передачу данных между приложениями, установите сборку на телефон и Googlebook.
    • Для перехода на сайт или прямой передачи на сайт установите сборку на телефон и оставьте приложение неустановленным на Googlebook, чтобы убедиться, что браузер открывает нужный URL.
  3. Как запустить передачу
    • На телефоне откройте приложение и перейдите к действию, которое позволяет передавать данные (вызов setHandoffEnabled(true, null) для передачи данных между приложениями или передача HandoffActivityParams с setAllowHandoffWithoutPackageInstalled(true) при тестировании с удаленным на Googlebook приложением).
    • На панели задач Googlebook найдите значок приложения с бейджем "Продолжить на" и нажмите на него.
  4. Как проверить восстановление рабочего стола
    • Убедитесь, что приложение открывается в окне на компьютере (или вкладке браузера) на том же элементе и в том же месте, что и на телефоне.
    • Измените размер окна приложения на Googlebook и убедитесь, что восстановленный контент и выделение сохраняются при переходе между классами размеров окон.

Дополнительные ресурсы