پشتیبانی از پیوستگی بین دستگاهی در گوگل‌بوک، پشتیبانی از پیوستگی بین دستگاهی در گوگل‌بوک، پشتیبانی از پیوستگی بین دستگاهی در گوگل‌بوک، پشتیبانی از پیوستگی بین دستگاهی در گوگل‌بوک

کاربران اغلب کارهایی را در تلفن اندرویدی خود شروع می‌کنند - مانند بررسی یک سند، مرتب‌سازی پیام‌ها یا مرور مجموعه‌ای از موارد - و سپس می‌خواهند به یک گوگل‌بوک با صفحه نمایش بزرگتر، صفحه کلید فیزیکی و فضای کاری چند پنجره‌ای آن نقل مکان کنند.

ویژگی «ادامه دادن» در اندروید ۱۷ (سطح API ۳۷) و بالاتر، نوار وظیفه گوگل‌بوک را قادر می‌سازد تا یک آیکون برنامه (به نام پیشنهاد ) برای فعالیتی که کاربر در تلفن همراه نزدیک خود در حال انجام آن بوده است، نمایش دهد. کاربران می‌توانند با یک کلیک، فعالیت خود را در گوگل‌بوک خود ادامه دهند.

پیاده‌سازی قابلیت انتقال بین دستگاهی (cross-device handoff) به برنامه شما اجازه می‌دهد تا زمینه فعالیت (activity context) را منتقل کند، بنابراین کاربر می‌تواند گردش کار خود را، چه در برنامه اندروید تطبیقی ​​شما و چه در مرورگر دسکتاپ، دقیقاً از همان جایی که در تلفن خود متوقف شده بود، از سر بگیرد.

یک جریان تحویل را انتخاب کنید

قبل از فعال کردن انتقال بین دستگاهی، تصمیم بگیرید که هر فعالیت در برنامه تلفن شما چگونه باید باز شود وقتی کاربر پیشنهاد ادامه در Googlebook را انتخاب می‌کند:

جریان تحویل هدف در گوگل بوک بهترین برای API کلید
انتقال از برنامه به برنامه دیگر اجرای برنامه بومی اندروید در پنجره دسکتاپ برنامه‌های بهینه شده برای صفحه نمایش‌های بزرگ و پنجره‌های دسکتاپ در گوگل پلی HandoffActivityData.Builder
انتقال از برنامه به برنامه دیگر با قابلیت اتصال مجدد از طریق وب برنامه بومی اندروید در صورت نصب؛ در غیر این صورت، یک URL در مرورگر پیش‌فرض دسکتاپ برنامه‌هایی که برای تجربه پنجره دسکتاپ بومی بهینه شده‌اند اما در صورت عدم نصب برنامه روی گوگل‌بوک، به یک مرورگر جایگزین قابل اعتماد نیاز دارند setFallbackUri
انتقال مستقیم به وب آدرس اینترنتی (URL) در مرورگر پیش‌فرض دسکتاپ (یا اگر برای پیوندهای برنامه اندروید پیکربندی شده باشد، توسط برنامه اصلی رهگیری می‌شود) گردش‌های کاری که در آن‌ها برنامه وب شما، تجربه اصلی دسکتاپ است HandoffActivityData.createWebHandoff

فعال کردن handoff برای یک فعالیت

گزینه «ادامه روشن» به طور پیش‌فرض غیرفعال است و برای هر فعالیت پیکربندی می‌شود. این مراحل را در هر فعالیت ارسال در تلفن دنبال کنید:

  1. تابع setHandoffEnabled را فراخوانی کنید و پس از اینکه activity حالت مورد نیاز برای handoff را بارگذاری کرد، مقدار true به آن بدهید. پس از فعال‌سازی، سیستم می‌تواند داده‌های handoff را در حالی که activity در پیش‌زمینه است یا به پس‌زمینه منتقل می‌شود، درخواست کند.
  2. تابع onHandoffActivityDataRequested را برای برگرداندن یک شیء HandoffActivityData غیر تهی، نادیده بگیرید. می‌توانید در هر زمانی از isHandoffEnabled برای بررسی فعال بودن handoff برای فعالیت مورد نظر، پرس‌وجو کنید.
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"
    }
}

وضعیت فعالیت بسته برای handoff

سیستم در دو سناریو، متد onHandoffActivityDataRequested را فراخوانی می‌کند:

  • انتقال پیش‌زمینه: فعالیت در پیش‌زمینه گوشی است و کاربر از نوار وظیفه گوگل‌بوک، انتقال را آغاز می‌کند.
  • ضبط وضعیت پس‌زمینه: فعالیت به پس‌زمینه گوشی منتقل می‌شود و سیستم، داده‌های مربوط به انتقال را در حافظه پنهان (cache) ذخیره می‌کند تا در صورتی که کاربر بعداً پیشنهاد را در گوگل‌بوک انتخاب کند، از آنها استفاده کند.

بررسی کنید که آیا درخواست انتقال فعال است یا خیر

با استفاده از isActiveRequest HandoffActivityDataRequestInfo بررسی کنید تا بین یک handoff فعال که توسط کاربر آغاز شده و یک ذخیره پس‌زمینه تمایز قائل شوید:

  • اگر isActiveRequest true را برگرداند، کاربر به طور فعال درخواست ادامه فعالیت در دستگاه دیگری را در حالی که فعالیت قابل مشاهده است، داده است.
  • اگر isActiveRequest false را برگرداند، سیستم در حال ذخیره وضعیت (state) در زمان توقف فعالیت (activity) است.

اگر یک نشانگر بصری روی گوشی نمایش داده شود که تأیید کند وظیفه به Googlebook منتقل می‌شود، به‌روزرسانی رابط کاربری را به صورت ناهمگام برنامه‌ریزی کنید تا onHandoffActivityDataRequested بدون تأخیر بازگردد.

موارد اضافی ایالت و یک پشتیبان وب را ضمیمه کنید

یک داده‌ی HandoffActivityData شامل اطلاعات زیر است:

  • کامپوننت هدف (برای انتقال از برنامه به برنامه مورد نیاز است): this.getComponentName() ( ComponentName اکتیویتی) یا یک ComponentName(this, TargetActivity::class.java) صریح را به HandoffActivityData.Builder ارسال کنید تا مشخص شود کدام اکتیویتی در Googlebook اجرا می‌شود.
  • موارد اضافی (اختیاری): یک PersistableBundle به setExtras() ارسال کنید که شامل شناسه‌ها و مختصات رابط کاربری (برای مثال، شناسه‌های سند باز، کلیدهای تب فعال یا موقعیت‌های اسکرول) باشد. حجم بسته باید کمتر از ۵۰ کیلوبایت باشد.
  • آدرس اینترنتی جایگزین (اختیاری): یک Uri وب به setFallbackUri ارسال کنید تا اگر برنامه اندروید شما نصب نشده باشد، گوگل‌بوک بتواند وظیفه را در مرورگر پیش‌فرض باز کند.
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"
    }
}

پیکربندی انتقال مستقیم به وب

اگر تجربه اصلی دسکتاپ شما در گوگل‌بوک یک برنامه وب است، HandoffActivityParams به همراه setAllowHandoffWithoutPackageInstalled(true) به setHandoffEnabled ارسال کنید و به جای استفاده از HandoffActivityData.Builder ، HandoffActivityData.createWebHandoff را فراخوانی کنید:

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)
    }
}

دریافت و بازیابی وضعیت در گوگل‌بوک

وقتی کاربر پیشنهاد «ادامه به ادامه» را در نوار وظیفه گوگل‌بوک انتخاب می‌کند، سیستم ComponentName هدف را در یک پنجره دسکتاپ اجرا می‌کند و افزونه‌های PersistableBundle را به Intent اجرا متصل می‌کند.

در گوگل‌بوک، با در نظر گرفتن پنجره‌بندی دسکتاپ و طرح‌بندی‌های چندبخشی، تحویل ورودی را مدیریت کنید:

  1. استخراج موارد اضافی تحویل داده شده: در هر دو متد onCreate و onNewIntent از اکتیویتی گیرنده، موارد اضافی را از Intent ورودی بازیابی کنید. مدیریت onNewIntent تضمین می‌کند که اگر اکتیویتی شما از حالت راه‌اندازی singleTop استفاده می‌کند یا به یک پنجره دسکتاپ موجود مسیردهی می‌کند، پنجره به مورد تحویل داده شده به‌روزرسانی شود.
  2. تطبیق وضعیت با طرح‌بندی‌های چندبخشی: وضعیتی که نمای جزئیات تمام‌صفحه را در تلفن اشغال می‌کرد، اغلب در Googlebook به یک مورد انتخاب‌شده در طرح‌بندی لیست-جزئیات یا صفحه پشتیبانی نگاشت می‌شود. هم انتخاب لیست اصلی و هم صفحه جزئیات را پر کنید تا پنجره دسکتاپ زمینه کاملی داشته باشد.
  3. حفظ حالت بازیابی‌شده در طول تغییر اندازه پنجره: حالت تحویل داده‌شده را در یک ViewModel ( SavedStateHandle ) ذخیره کنید یا در Jetpack Compose rememberSaveable کنید تا تغییر اندازه پنجره در حالت آزاد در 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"
    }
}

تست هندآف بین گوشی و گوگل‌بوک

برای تأیید پیاده‌سازی خود از ابتدا تا انتها:

  1. هر دو دستگاه را آماده کنید
    • هم در گوشی اندروید خود (با اندروید ۱۷ یا بالاتر) و هم در گوگل‌بوک خود (یا یک دستگاه آزمایشی که در حالت پنجره‌ای دسکتاپ اجرا می‌شود) به یک حساب گوگل وارد شوید.
    • بلوتوث را روشن کنید و هر دو دستگاه را به یک شبکه وای‌فای متصل کنید.
    • در هر دو دستگاه، تنظیمات > دستگاه‌های متصل > تنظیمات اتصال > سرویس‌های بین دستگاهی > ادامه فعالیت را باز کنید، دستورالعمل‌های راه‌اندازی را دنبال کنید و وظایف را فعال کنید. برای پیش‌نیازهای کامل محیط، به راه‌اندازی و آزمایش مراجعه کنید.
  2. برنامه خود را نصب کنید
    • برای انتقال از یک برنامه به برنامه دیگر ، نسخه ساخته شده خود را هم روی گوشی و هم روی گوگل بوک نصب کنید.
    • برای بازگشت به وب یا انتقال مستقیم به وب ، نسخهٔ ساخته‌شده را روی گوشی نصب کنید و برنامه را روی گوگل‌بوک حذف نصب کنید تا بتوانید تأیید کنید که مرورگر، آدرس اینترنتی مورد انتظار را باز می‌کند.
  3. شروع فرآیند تحویل کالا
    • در گوشی، برنامه خود را باز کنید و به اکتیویتی که handoff را فعال می‌کند بروید (برای handoff از برنامه به برنامه، setHandoffEnabled(true, null) را فراخوانی کنید، یا هنگام تست با برنامه‌ای که در Googlebook حذف نصب شده است، HandoffActivityParams را با setAllowHandoffWithoutPackageInstalled(true) ارسال کنید).
    • در گوگل‌بوک، سمت راست نوار وظیفه را برای یافتن آیکون برنامه خود با نشان «ادامه دهید» بررسی کنید و آن را انتخاب کنید.
  4. تأیید بازیابی دسکتاپ
    • تأیید کنید که برنامه شما در یک پنجره دسکتاپ (یا برگه مرورگر) در موقعیت دقیق آیتم و اسکرول از طریق گوشی باز می‌شود.
    • اندازه پنجره برنامه را در Googlebook تغییر دهید و تأیید کنید که محتوا و انتخاب بازیابی شده در طول انتقال کلاس اندازه پنجره باقی می‌مانند.

منابع اضافی

،

کاربران اغلب کارهایی را در تلفن اندرویدی خود شروع می‌کنند - مانند بررسی یک سند، مرتب‌سازی پیام‌ها یا مرور مجموعه‌ای از موارد - و سپس می‌خواهند به یک گوگل‌بوک با صفحه نمایش بزرگتر، صفحه کلید فیزیکی و فضای کاری چند پنجره‌ای آن نقل مکان کنند.

ویژگی «ادامه دادن» در اندروید ۱۷ (سطح API ۳۷) و بالاتر، نوار وظیفه گوگل‌بوک را قادر می‌سازد تا یک آیکون برنامه (به نام پیشنهاد ) برای فعالیتی که کاربر در تلفن همراه نزدیک خود در حال انجام آن بوده است، نمایش دهد. کاربران می‌توانند با یک کلیک، فعالیت خود را در گوگل‌بوک خود ادامه دهند.

پیاده‌سازی قابلیت انتقال بین دستگاهی (cross-device handoff) به برنامه شما اجازه می‌دهد تا زمینه فعالیت (activity context) را منتقل کند، بنابراین کاربر می‌تواند گردش کار خود را، چه در برنامه اندروید تطبیقی ​​شما و چه در مرورگر دسکتاپ، دقیقاً از همان جایی که در تلفن خود متوقف شده بود، از سر بگیرد.

یک جریان تحویل را انتخاب کنید

قبل از فعال کردن انتقال بین دستگاهی، تصمیم بگیرید که هر فعالیت در برنامه تلفن شما چگونه باید باز شود وقتی کاربر پیشنهاد ادامه در Googlebook را انتخاب می‌کند:

جریان تحویل هدف در گوگل بوک بهترین برای API کلید
انتقال از برنامه به برنامه دیگر اجرای برنامه بومی اندروید در پنجره دسکتاپ برنامه‌های بهینه شده برای صفحه نمایش‌های بزرگ و پنجره‌های دسکتاپ در گوگل پلی HandoffActivityData.Builder
انتقال از برنامه به برنامه دیگر با قابلیت اتصال مجدد از طریق وب برنامه بومی اندروید در صورت نصب؛ در غیر این صورت، یک URL در مرورگر پیش‌فرض دسکتاپ برنامه‌هایی که برای تجربه پنجره دسکتاپ بومی بهینه شده‌اند اما در صورت عدم نصب برنامه روی گوگل‌بوک، به یک مرورگر جایگزین قابل اعتماد نیاز دارند setFallbackUri
انتقال مستقیم به وب آدرس اینترنتی (URL) در مرورگر پیش‌فرض دسکتاپ (یا اگر برای پیوندهای برنامه اندروید پیکربندی شده باشد، توسط برنامه اصلی رهگیری می‌شود) گردش‌های کاری که در آن‌ها برنامه وب شما، تجربه اصلی دسکتاپ است HandoffActivityData.createWebHandoff

فعال کردن handoff برای یک فعالیت

گزینه «ادامه روشن» به طور پیش‌فرض غیرفعال است و برای هر فعالیت پیکربندی می‌شود. این مراحل را در هر فعالیت ارسال در تلفن دنبال کنید:

  1. تابع setHandoffEnabled را فراخوانی کنید و پس از اینکه activity حالت مورد نیاز برای handoff را بارگذاری کرد، مقدار true به آن بدهید. پس از فعال‌سازی، سیستم می‌تواند داده‌های handoff را در حالی که activity در پیش‌زمینه است یا به پس‌زمینه منتقل می‌شود، درخواست کند.
  2. تابع onHandoffActivityDataRequested را برای برگرداندن یک شیء HandoffActivityData غیر تهی، نادیده بگیرید. می‌توانید در هر زمانی از isHandoffEnabled برای بررسی فعال بودن handoff برای فعالیت مورد نظر، پرس‌وجو کنید.
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"
    }
}

وضعیت فعالیت بسته برای handoff

سیستم در دو سناریو، متد onHandoffActivityDataRequested را فراخوانی می‌کند:

  • انتقال پیش‌زمینه: فعالیت در پیش‌زمینه گوشی است و کاربر از نوار وظیفه گوگل‌بوک، انتقال را آغاز می‌کند.
  • ضبط وضعیت پس‌زمینه: فعالیت به پس‌زمینه گوشی منتقل می‌شود و سیستم، داده‌های مربوط به انتقال را در حافظه پنهان (cache) ذخیره می‌کند تا در صورتی که کاربر بعداً پیشنهاد را در گوگل‌بوک انتخاب کند، از آنها استفاده کند.

بررسی کنید که آیا درخواست انتقال فعال است یا خیر

با استفاده از isActiveRequest HandoffActivityDataRequestInfo بررسی کنید تا بین یک handoff فعال که توسط کاربر آغاز شده و یک ذخیره پس‌زمینه تمایز قائل شوید:

  • اگر isActiveRequest true را برگرداند، کاربر به طور فعال درخواست ادامه فعالیت در دستگاه دیگری را در حالی که فعالیت قابل مشاهده است، داده است.
  • اگر isActiveRequest false را برگرداند، سیستم در حال ذخیره وضعیت (state) در زمان توقف فعالیت (activity) است.

اگر یک نشانگر بصری روی گوشی نمایش داده شود که تأیید کند وظیفه به Googlebook منتقل می‌شود، به‌روزرسانی رابط کاربری را به صورت ناهمگام برنامه‌ریزی کنید تا onHandoffActivityDataRequested بدون تأخیر بازگردد.

موارد اضافی ایالت و یک پشتیبان وب را ضمیمه کنید

یک داده‌ی HandoffActivityData شامل اطلاعات زیر است:

  • کامپوننت هدف (برای انتقال از برنامه به برنامه مورد نیاز است): this.getComponentName() ( ComponentName اکتیویتی) یا یک ComponentName(this, TargetActivity::class.java) صریح را به HandoffActivityData.Builder ارسال کنید تا مشخص شود کدام اکتیویتی در Googlebook اجرا می‌شود.
  • موارد اضافی (اختیاری): یک PersistableBundle به setExtras() ارسال کنید که شامل شناسه‌ها و مختصات رابط کاربری (برای مثال، شناسه‌های سند باز، کلیدهای تب فعال یا موقعیت‌های اسکرول) باشد. حجم بسته باید کمتر از ۵۰ کیلوبایت باشد.
  • آدرس اینترنتی جایگزین (اختیاری): یک Uri وب به setFallbackUri ارسال کنید تا اگر برنامه اندروید شما نصب نشده باشد، گوگل‌بوک بتواند وظیفه را در مرورگر پیش‌فرض باز کند.
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"
    }
}

پیکربندی انتقال مستقیم به وب

اگر تجربه اصلی دسکتاپ شما در گوگل‌بوک یک برنامه وب است، HandoffActivityParams به همراه setAllowHandoffWithoutPackageInstalled(true) به setHandoffEnabled ارسال کنید و به جای استفاده از HandoffActivityData.Builder ، HandoffActivityData.createWebHandoff را فراخوانی کنید:

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)
    }
}

دریافت و بازیابی وضعیت در گوگل‌بوک

وقتی کاربر پیشنهاد «ادامه به ادامه» را در نوار وظیفه گوگل‌بوک انتخاب می‌کند، سیستم ComponentName هدف را در یک پنجره دسکتاپ اجرا می‌کند و افزونه‌های PersistableBundle را به Intent اجرا متصل می‌کند.

در گوگل‌بوک، با در نظر گرفتن پنجره‌بندی دسکتاپ و طرح‌بندی‌های چندبخشی، تحویل ورودی را مدیریت کنید:

  1. استخراج موارد اضافی تحویل داده شده: در هر دو متد onCreate و onNewIntent از اکتیویتی گیرنده، موارد اضافی را از Intent ورودی بازیابی کنید. مدیریت onNewIntent تضمین می‌کند که اگر اکتیویتی شما از حالت راه‌اندازی singleTop استفاده می‌کند یا به یک پنجره دسکتاپ موجود مسیردهی می‌کند، پنجره به مورد تحویل داده شده به‌روزرسانی شود.
  2. تطبیق وضعیت با طرح‌بندی‌های چندبخشی: وضعیتی که نمای جزئیات تمام‌صفحه را در تلفن اشغال می‌کرد، اغلب در Googlebook به یک مورد انتخاب‌شده در طرح‌بندی لیست-جزئیات یا صفحه پشتیبانی نگاشت می‌شود. هم انتخاب لیست اصلی و هم صفحه جزئیات را پر کنید تا پنجره دسکتاپ زمینه کاملی داشته باشد.
  3. حفظ حالت بازیابی‌شده در طول تغییر اندازه پنجره: حالت تحویل داده‌شده را در یک ViewModel ( SavedStateHandle ) ذخیره کنید یا در Jetpack Compose rememberSaveable کنید تا تغییر اندازه پنجره در حالت آزاد در 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"
    }
}

تست هندآف بین گوشی و گوگل‌بوک

برای تأیید پیاده‌سازی خود از ابتدا تا انتها:

  1. هر دو دستگاه را آماده کنید
    • هم در گوشی اندروید خود (با اندروید ۱۷ یا بالاتر) و هم در گوگل‌بوک خود (یا یک دستگاه آزمایشی که در حالت پنجره‌ای دسکتاپ اجرا می‌شود) به یک حساب گوگل وارد شوید.
    • بلوتوث را روشن کنید و هر دو دستگاه را به یک شبکه وای‌فای متصل کنید.
    • در هر دو دستگاه، تنظیمات > دستگاه‌های متصل > تنظیمات اتصال > سرویس‌های بین دستگاهی > ادامه فعالیت را باز کنید، دستورالعمل‌های راه‌اندازی را دنبال کنید و وظایف را فعال کنید. برای پیش‌نیازهای کامل محیط، به راه‌اندازی و آزمایش مراجعه کنید.
  2. برنامه خود را نصب کنید
    • برای انتقال از یک برنامه به برنامه دیگر ، نسخه ساخته شده خود را هم روی گوشی و هم روی گوگل بوک نصب کنید.
    • برای بازگشت به وب یا انتقال مستقیم به وب ، نسخهٔ ساخته‌شده را روی گوشی نصب کنید و برنامه را روی گوگل‌بوک حذف نصب کنید تا بتوانید تأیید کنید که مرورگر، آدرس اینترنتی مورد انتظار را باز می‌کند.
  3. شروع فرآیند تحویل کالا
    • در گوشی، برنامه خود را باز کنید و به اکتیویتی که handoff را فعال می‌کند بروید (برای handoff از برنامه به برنامه، setHandoffEnabled(true, null) را فراخوانی کنید، یا هنگام تست با برنامه‌ای که در Googlebook حذف نصب شده است، HandoffActivityParams را با setAllowHandoffWithoutPackageInstalled(true) ارسال کنید).
    • در گوگل‌بوک، سمت راست نوار وظیفه را برای یافتن آیکون برنامه خود با نشان «ادامه دهید» بررسی کنید و آن را انتخاب کنید.
  4. تأیید بازیابی دسکتاپ
    • تأیید کنید که برنامه شما در یک پنجره دسکتاپ (یا برگه مرورگر) در موقعیت دقیق آیتم و اسکرول از طریق گوشی باز می‌شود.
    • اندازه پنجره برنامه را در Googlebook تغییر دهید و تأیید کنید که محتوا و انتخاب بازیابی شده در طول انتقال کلاس اندازه پنجره باقی می‌مانند.

منابع اضافی

،

کاربران اغلب کارهایی را در تلفن اندرویدی خود شروع می‌کنند - مانند بررسی یک سند، مرتب‌سازی پیام‌ها یا مرور مجموعه‌ای از موارد - و سپس می‌خواهند به یک گوگل‌بوک با صفحه نمایش بزرگتر، صفحه کلید فیزیکی و فضای کاری چند پنجره‌ای آن نقل مکان کنند.

ویژگی «ادامه دادن» در اندروید ۱۷ (سطح API ۳۷) و بالاتر، نوار وظیفه گوگل‌بوک را قادر می‌سازد تا یک آیکون برنامه (به نام پیشنهاد ) برای فعالیتی که کاربر در تلفن همراه نزدیک خود در حال انجام آن بوده است، نمایش دهد. کاربران می‌توانند با یک کلیک، فعالیت خود را در گوگل‌بوک خود ادامه دهند.

پیاده‌سازی قابلیت انتقال بین دستگاهی (cross-device handoff) به برنامه شما اجازه می‌دهد تا زمینه فعالیت (activity context) را منتقل کند، بنابراین کاربر می‌تواند گردش کار خود را، چه در برنامه اندروید تطبیقی ​​شما و چه در مرورگر دسکتاپ، دقیقاً از همان جایی که در تلفن خود متوقف شده بود، از سر بگیرد.

یک جریان تحویل را انتخاب کنید

قبل از فعال کردن انتقال بین دستگاهی، تصمیم بگیرید که هر فعالیت در برنامه تلفن شما چگونه باید باز شود وقتی کاربر پیشنهاد ادامه در Googlebook را انتخاب می‌کند:

جریان تحویل هدف در گوگل بوک بهترین برای API کلید
انتقال از برنامه به برنامه دیگر اجرای برنامه بومی اندروید در پنجره دسکتاپ برنامه‌های بهینه شده برای صفحه نمایش‌های بزرگ و پنجره‌های دسکتاپ در گوگل پلی HandoffActivityData.Builder
انتقال از برنامه به برنامه دیگر با قابلیت اتصال مجدد از طریق وب برنامه بومی اندروید در صورت نصب؛ در غیر این صورت، یک URL در مرورگر پیش‌فرض دسکتاپ برنامه‌هایی که برای تجربه پنجره دسکتاپ بومی بهینه شده‌اند اما در صورت عدم نصب برنامه روی گوگل‌بوک، به یک مرورگر جایگزین قابل اعتماد نیاز دارند setFallbackUri
انتقال مستقیم به وب آدرس اینترنتی (URL) در مرورگر پیش‌فرض دسکتاپ (یا اگر برای پیوندهای برنامه اندروید پیکربندی شده باشد، توسط برنامه اصلی رهگیری می‌شود) گردش‌های کاری که در آن‌ها برنامه وب شما، تجربه اصلی دسکتاپ است HandoffActivityData.createWebHandoff

فعال کردن handoff برای یک فعالیت

گزینه «ادامه روشن» به طور پیش‌فرض غیرفعال است و برای هر فعالیت پیکربندی می‌شود. این مراحل را در هر فعالیت ارسال در تلفن دنبال کنید:

  1. تابع setHandoffEnabled را فراخوانی کنید و پس از اینکه activity حالت مورد نیاز برای handoff را بارگذاری کرد، مقدار true به آن بدهید. پس از فعال‌سازی، سیستم می‌تواند داده‌های handoff را در حالی که activity در پیش‌زمینه است یا به پس‌زمینه منتقل می‌شود، درخواست کند.
  2. تابع onHandoffActivityDataRequested را برای برگرداندن یک شیء HandoffActivityData غیر تهی، نادیده بگیرید. می‌توانید در هر زمانی از isHandoffEnabled برای بررسی فعال بودن handoff برای فعالیت مورد نظر، پرس‌وجو کنید.
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"
    }
}

وضعیت فعالیت بسته برای handoff

سیستم در دو سناریو، متد onHandoffActivityDataRequested را فراخوانی می‌کند:

  • انتقال پیش‌زمینه: فعالیت در پیش‌زمینه گوشی است و کاربر از نوار وظیفه گوگل‌بوک، انتقال را آغاز می‌کند.
  • ضبط وضعیت پس‌زمینه: فعالیت به پس‌زمینه گوشی منتقل می‌شود و سیستم، داده‌های مربوط به انتقال را در حافظه پنهان (cache) ذخیره می‌کند تا در صورتی که کاربر بعداً پیشنهاد را در گوگل‌بوک انتخاب کند، از آنها استفاده کند.

بررسی کنید که آیا درخواست انتقال فعال است یا خیر

با استفاده از isActiveRequest HandoffActivityDataRequestInfo بررسی کنید تا بین یک handoff فعال که توسط کاربر آغاز شده و یک ذخیره پس‌زمینه تمایز قائل شوید:

  • اگر isActiveRequest true را برگرداند، کاربر به طور فعال درخواست ادامه فعالیت در دستگاه دیگری را در حالی که فعالیت قابل مشاهده است، داده است.
  • اگر isActiveRequest false را برگرداند، سیستم در حال ذخیره وضعیت (state) در زمان توقف فعالیت (activity) است.

اگر یک نشانگر بصری روی گوشی نمایش داده شود که تأیید کند وظیفه به Googlebook منتقل می‌شود، به‌روزرسانی رابط کاربری را به صورت ناهمگام برنامه‌ریزی کنید تا onHandoffActivityDataRequested بدون تأخیر بازگردد.

موارد اضافی ایالت و یک پشتیبان وب را ضمیمه کنید

یک داده‌ی HandoffActivityData شامل اطلاعات زیر است:

  • کامپوننت هدف (برای انتقال از برنامه به برنامه مورد نیاز است): this.getComponentName() ( ComponentName اکتیویتی) یا یک ComponentName(this, TargetActivity::class.java) صریح را به HandoffActivityData.Builder ارسال کنید تا مشخص شود کدام اکتیویتی در Googlebook اجرا می‌شود.
  • موارد اضافی (اختیاری): یک PersistableBundle به setExtras() ارسال کنید که شامل شناسه‌ها و مختصات رابط کاربری (برای مثال، شناسه‌های سند باز، کلیدهای تب فعال یا موقعیت‌های اسکرول) باشد. حجم بسته باید کمتر از ۵۰ کیلوبایت باشد.
  • آدرس اینترنتی جایگزین (اختیاری): یک Uri وب به setFallbackUri ارسال کنید تا اگر برنامه اندروید شما نصب نشده باشد، گوگل‌بوک بتواند وظیفه را در مرورگر پیش‌فرض باز کند.
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"
    }
}

پیکربندی انتقال مستقیم به وب

اگر تجربه اصلی دسکتاپ شما در گوگل‌بوک یک برنامه وب است، HandoffActivityParams به همراه setAllowHandoffWithoutPackageInstalled(true) به setHandoffEnabled ارسال کنید و به جای استفاده از HandoffActivityData.Builder ، HandoffActivityData.createWebHandoff را فراخوانی کنید:

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)
    }
}

دریافت و بازیابی وضعیت در گوگل‌بوک

وقتی کاربر پیشنهاد «ادامه به ادامه» را در نوار وظیفه گوگل‌بوک انتخاب می‌کند، سیستم ComponentName هدف را در یک پنجره دسکتاپ اجرا می‌کند و افزونه‌های PersistableBundle را به Intent اجرا متصل می‌کند.

در گوگل‌بوک، با در نظر گرفتن پنجره‌بندی دسکتاپ و طرح‌بندی‌های چندبخشی، تحویل ورودی را مدیریت کنید:

  1. استخراج موارد اضافی تحویل داده شده: در هر دو متد onCreate و onNewIntent از اکتیویتی گیرنده، موارد اضافی را از Intent ورودی بازیابی کنید. مدیریت onNewIntent تضمین می‌کند که اگر اکتیویتی شما از حالت راه‌اندازی singleTop استفاده می‌کند یا به یک پنجره دسکتاپ موجود مسیردهی می‌کند، پنجره به مورد تحویل داده شده به‌روزرسانی شود.
  2. تطبیق وضعیت با طرح‌بندی‌های چندبخشی: وضعیتی که نمای جزئیات تمام‌صفحه را در تلفن اشغال می‌کرد، اغلب در Googlebook به یک مورد انتخاب‌شده در طرح‌بندی لیست-جزئیات یا صفحه پشتیبانی نگاشت می‌شود. هم انتخاب لیست اصلی و هم صفحه جزئیات را پر کنید تا پنجره دسکتاپ زمینه کاملی داشته باشد.
  3. حفظ حالت بازیابی‌شده در طول تغییر اندازه پنجره: حالت تحویل داده‌شده را در یک ViewModel ( SavedStateHandle ) ذخیره کنید یا در Jetpack Compose rememberSaveable کنید تا تغییر اندازه پنجره در حالت آزاد در 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"
    }
}

تست هندآف بین گوشی و گوگل‌بوک

برای تأیید پیاده‌سازی خود از ابتدا تا انتها:

  1. هر دو دستگاه را آماده کنید
    • هم در گوشی اندروید خود (با اندروید ۱۷ یا بالاتر) و هم در گوگل‌بوک خود (یا یک دستگاه آزمایشی که در حالت پنجره‌ای دسکتاپ اجرا می‌شود) به یک حساب گوگل وارد شوید.
    • بلوتوث را روشن کنید و هر دو دستگاه را به یک شبکه وای‌فای متصل کنید.
    • در هر دو دستگاه، تنظیمات > دستگاه‌های متصل > تنظیمات اتصال > سرویس‌های بین دستگاهی > ادامه فعالیت را باز کنید، دستورالعمل‌های راه‌اندازی را دنبال کنید و وظایف را فعال کنید. برای پیش‌نیازهای کامل محیط، به راه‌اندازی و آزمایش مراجعه کنید.
  2. برنامه خود را نصب کنید
    • برای انتقال از یک برنامه به برنامه دیگر ، نسخه ساخته شده خود را هم روی گوشی و هم روی گوگل بوک نصب کنید.
    • برای بازگشت به وب یا انتقال مستقیم به وب ، نسخهٔ ساخته‌شده را روی گوشی نصب کنید و برنامه را روی گوگل‌بوک حذف نصب کنید تا بتوانید تأیید کنید که مرورگر، آدرس اینترنتی مورد انتظار را باز می‌کند.
  3. شروع فرآیند تحویل کالا
    • در گوشی، برنامه خود را باز کنید و به اکتیویتی که handoff را فعال می‌کند بروید (برای handoff از برنامه به برنامه، setHandoffEnabled(true, null) را فراخوانی کنید، یا هنگام تست با برنامه‌ای که در Googlebook حذف نصب شده است، HandoffActivityParams را با setAllowHandoffWithoutPackageInstalled(true) ارسال کنید).
    • در گوگل‌بوک، سمت راست نوار وظیفه را برای یافتن آیکون برنامه خود با نشان «ادامه دهید» بررسی کنید و آن را انتخاب کنید.
  4. تأیید بازیابی دسکتاپ
    • تأیید کنید که برنامه شما در یک پنجره دسکتاپ (یا برگه مرورگر) در موقعیت دقیق آیتم و اسکرول از طریق گوشی باز می‌شود.
    • اندازه پنجره برنامه را در Googlebook تغییر دهید و تأیید کنید که محتوا و انتخاب بازیابی شده در طول انتقال کلاس اندازه پنجره باقی می‌مانند.

منابع اضافی

،

کاربران اغلب کارهایی را در تلفن اندرویدی خود شروع می‌کنند - مانند بررسی یک سند، مرتب‌سازی پیام‌ها یا مرور مجموعه‌ای از موارد - و سپس می‌خواهند به یک گوگل‌بوک با صفحه نمایش بزرگتر، صفحه کلید فیزیکی و فضای کاری چند پنجره‌ای آن نقل مکان کنند.

ویژگی «ادامه دادن» در اندروید ۱۷ (سطح API ۳۷) و بالاتر، نوار وظیفه گوگل‌بوک را قادر می‌سازد تا یک آیکون برنامه (به نام پیشنهاد ) برای فعالیتی که کاربر در تلفن همراه نزدیک خود در حال انجام آن بوده است، نمایش دهد. کاربران می‌توانند با یک کلیک، فعالیت خود را در گوگل‌بوک خود ادامه دهند.

پیاده‌سازی قابلیت انتقال بین دستگاهی (cross-device handoff) به برنامه شما اجازه می‌دهد تا زمینه فعالیت (activity context) را منتقل کند، بنابراین کاربر می‌تواند گردش کار خود را، چه در برنامه اندروید تطبیقی ​​شما و چه در مرورگر دسکتاپ، دقیقاً از همان جایی که در تلفن خود متوقف شده بود، از سر بگیرد.

یک جریان تحویل را انتخاب کنید

قبل از فعال کردن انتقال بین دستگاهی، تصمیم بگیرید که هر فعالیت در برنامه تلفن شما چگونه باید باز شود وقتی کاربر پیشنهاد ادامه در Googlebook را انتخاب می‌کند:

جریان تحویل هدف در گوگل بوک بهترین برای API کلید
انتقال از برنامه به برنامه دیگر اجرای برنامه بومی اندروید در پنجره دسکتاپ برنامه‌های بهینه شده برای صفحه نمایش‌های بزرگ و پنجره‌های دسکتاپ در گوگل پلی HandoffActivityData.Builder
انتقال از برنامه به برنامه دیگر با قابلیت اتصال مجدد از طریق وب برنامه بومی اندروید در صورت نصب؛ در غیر این صورت، یک URL در مرورگر پیش‌فرض دسکتاپ برنامه‌هایی که برای تجربه پنجره دسکتاپ بومی بهینه شده‌اند اما در صورت عدم نصب برنامه روی گوگل‌بوک، به یک مرورگر جایگزین قابل اعتماد نیاز دارند setFallbackUri
انتقال مستقیم به وب آدرس اینترنتی (URL) در مرورگر پیش‌فرض دسکتاپ (یا اگر برای پیوندهای برنامه اندروید پیکربندی شده باشد، توسط برنامه اصلی رهگیری می‌شود) گردش‌های کاری که در آن‌ها برنامه وب شما، تجربه اصلی دسکتاپ است HandoffActivityData.createWebHandoff

فعال کردن handoff برای یک فعالیت

گزینه «ادامه روشن» به طور پیش‌فرض غیرفعال است و برای هر فعالیت پیکربندی می‌شود. این مراحل را در هر فعالیت ارسال در تلفن دنبال کنید:

  1. تابع setHandoffEnabled را فراخوانی کنید و پس از اینکه activity حالت مورد نیاز برای handoff را بارگذاری کرد، مقدار true به آن بدهید. پس از فعال‌سازی، سیستم می‌تواند داده‌های handoff را در حالی که activity در پیش‌زمینه است یا به پس‌زمینه منتقل می‌شود، درخواست کند.
  2. تابع onHandoffActivityDataRequested را برای برگرداندن یک شیء HandoffActivityData غیر تهی، نادیده بگیرید. می‌توانید در هر زمانی از isHandoffEnabled برای بررسی فعال بودن handoff برای فعالیت مورد نظر، پرس‌وجو کنید.
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"
    }
}

وضعیت فعالیت بسته برای handoff

سیستم در دو سناریو، متد onHandoffActivityDataRequested را فراخوانی می‌کند:

  • انتقال پیش‌زمینه: فعالیت در پیش‌زمینه گوشی است و کاربر از نوار وظیفه گوگل‌بوک، انتقال را آغاز می‌کند.
  • ضبط وضعیت پس‌زمینه: فعالیت به پس‌زمینه گوشی منتقل می‌شود و سیستم، داده‌های مربوط به انتقال را در حافظه پنهان (cache) ذخیره می‌کند تا در صورتی که کاربر بعداً پیشنهاد را در گوگل‌بوک انتخاب کند، از آنها استفاده کند.

بررسی کنید که آیا درخواست انتقال فعال است یا خیر

با استفاده از isActiveRequest HandoffActivityDataRequestInfo بررسی کنید تا بین یک handoff فعال که توسط کاربر آغاز شده و یک ذخیره پس‌زمینه تمایز قائل شوید:

  • اگر isActiveRequest true را برگرداند، کاربر به طور فعال درخواست ادامه فعالیت در دستگاه دیگری را در حالی که فعالیت قابل مشاهده است، داده است.
  • اگر isActiveRequest false را برگرداند، سیستم در حال ذخیره وضعیت (state) در زمان توقف فعالیت (activity) است.

اگر یک نشانگر بصری روی گوشی نمایش داده شود که تأیید کند وظیفه به Googlebook منتقل می‌شود، به‌روزرسانی رابط کاربری را به صورت ناهمگام برنامه‌ریزی کنید تا onHandoffActivityDataRequested بدون تأخیر بازگردد.

موارد اضافی ایالت و یک پشتیبان وب را ضمیمه کنید

یک داده‌ی HandoffActivityData شامل اطلاعات زیر است:

  • کامپوننت هدف (برای انتقال از برنامه به برنامه مورد نیاز است): this.getComponentName() ( ComponentName اکتیویتی) یا یک ComponentName(this, TargetActivity::class.java) صریح را به HandoffActivityData.Builder ارسال کنید تا مشخص شود کدام اکتیویتی در Googlebook اجرا می‌شود.
  • موارد اضافی (اختیاری): یک PersistableBundle به setExtras() ارسال کنید که شامل شناسه‌ها و مختصات رابط کاربری (برای مثال، شناسه‌های سند باز، کلیدهای تب فعال یا موقعیت‌های اسکرول) باشد. حجم بسته باید کمتر از ۵۰ کیلوبایت باشد.
  • آدرس اینترنتی جایگزین (اختیاری): یک Uri وب به setFallbackUri ارسال کنید تا اگر برنامه اندروید شما نصب نشده باشد، گوگل‌بوک بتواند وظیفه را در مرورگر پیش‌فرض باز کند.
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"
    }
}

پیکربندی انتقال مستقیم به وب

اگر تجربه اصلی دسکتاپ شما در گوگل‌بوک یک برنامه وب است، HandoffActivityParams به همراه setAllowHandoffWithoutPackageInstalled(true) به setHandoffEnabled ارسال کنید و به جای استفاده از HandoffActivityData.Builder ، HandoffActivityData.createWebHandoff را فراخوانی کنید:

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)
    }
}

دریافت و بازیابی وضعیت در گوگل‌بوک

وقتی کاربر پیشنهاد «ادامه به ادامه» را در نوار وظیفه گوگل‌بوک انتخاب می‌کند، سیستم ComponentName هدف را در یک پنجره دسکتاپ اجرا می‌کند و افزونه‌های PersistableBundle را به Intent اجرا متصل می‌کند.

در گوگل‌بوک، با در نظر گرفتن پنجره‌بندی دسکتاپ و طرح‌بندی‌های چندبخشی، تحویل ورودی را مدیریت کنید:

  1. استخراج موارد اضافی تحویل داده شده: در هر دو متد onCreate و onNewIntent از اکتیویتی گیرنده، موارد اضافی را از Intent ورودی بازیابی کنید. مدیریت onNewIntent تضمین می‌کند که اگر اکتیویتی شما از حالت راه‌اندازی singleTop استفاده می‌کند یا به یک پنجره دسکتاپ موجود مسیردهی می‌کند، پنجره به مورد تحویل داده شده به‌روزرسانی شود.
  2. تطبیق وضعیت با طرح‌بندی‌های چندبخشی: وضعیتی که نمای جزئیات تمام‌صفحه را در تلفن اشغال می‌کرد، اغلب در Googlebook به یک مورد انتخاب‌شده در طرح‌بندی لیست-جزئیات یا صفحه پشتیبانی نگاشت می‌شود. هم انتخاب لیست اصلی و هم صفحه جزئیات را پر کنید تا پنجره دسکتاپ زمینه کاملی داشته باشد.
  3. حفظ حالت بازیابی‌شده در طول تغییر اندازه پنجره: حالت تحویل داده‌شده را در یک ViewModel ( SavedStateHandle ) ذخیره کنید یا در Jetpack Compose rememberSaveable کنید تا تغییر اندازه پنجره در حالت آزاد در 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"
    }
}

تست هندآف بین گوشی و گوگل‌بوک

برای تأیید پیاده‌سازی خود از ابتدا تا انتها:

  1. هر دو دستگاه را آماده کنید
    • هم در گوشی اندروید خود (با اندروید ۱۷ یا بالاتر) و هم در گوگل‌بوک خود (یا یک دستگاه آزمایشی که در حالت پنجره‌ای دسکتاپ اجرا می‌شود) به یک حساب گوگل وارد شوید.
    • بلوتوث را روشن کنید و هر دو دستگاه را به یک شبکه وای‌فای متصل کنید.
    • در هر دو دستگاه، تنظیمات > دستگاه‌های متصل > تنظیمات اتصال > سرویس‌های بین دستگاهی > ادامه فعالیت را باز کنید، دستورالعمل‌های راه‌اندازی را دنبال کنید و وظایف را فعال کنید. برای پیش‌نیازهای کامل محیط، به راه‌اندازی و آزمایش مراجعه کنید.
  2. برنامه خود را نصب کنید
    • برای انتقال از یک برنامه به برنامه دیگر ، نسخه ساخته شده خود را هم روی گوشی و هم روی گوگل بوک نصب کنید.
    • برای بازگشت به وب یا انتقال مستقیم به وب ، نسخهٔ ساخته‌شده را روی گوشی نصب کنید و برنامه را روی گوگل‌بوک حذف نصب کنید تا بتوانید تأیید کنید که مرورگر، آدرس اینترنتی مورد انتظار را باز می‌کند.
  3. شروع فرآیند تحویل کالا
    • در گوشی، برنامه خود را باز کنید و به اکتیویتی که handoff را فعال می‌کند بروید (برای handoff از برنامه به برنامه، setHandoffEnabled(true, null) را فراخوانی کنید، یا هنگام تست با برنامه‌ای که در Googlebook حذف نصب شده است، HandoffActivityParams را با setAllowHandoffWithoutPackageInstalled(true) ارسال کنید).
    • در گوگل‌بوک، سمت راست نوار وظیفه را برای یافتن آیکون برنامه خود با نشان «ادامه دهید» بررسی کنید و آن را انتخاب کنید.
  4. تأیید بازیابی دسکتاپ
    • تأیید کنید که برنامه شما در یک پنجره دسکتاپ (یا برگه مرورگر) در موقعیت دقیق آیتم و اسکرول از طریق گوشی باز می‌شود.
    • اندازه پنجره برنامه را در Googlebook تغییر دهید و تأیید کنید که محتوا و انتخاب بازیابی شده در طول انتقال کلاس اندازه پنجره باقی می‌مانند.

منابع اضافی