รองรับความต่อเนื่องข้ามอุปกรณ์ใน Googlebook

ผู้ใช้มักจะเริ่มงานบนโทรศัพท์ Android เช่น การตรวจสอบเอกสาร การจัดเรียงข้อความ หรือการเรียกดูรายการต่างๆ แล้วต้องการย้ายไปใช้ Googlebook ที่มีจอแสดงผลขนาดใหญ่กว่า คีย์บอร์ดจริง และพื้นที่ทำงานแบบหลายหน้าต่าง

ฟีเจอร์ดำเนินการต่อใน Android 17 (ระดับ API 37) ขึ้นไปช่วยให้แถบงานของ Googlebook แสดงไอคอนแอป (เรียกว่าคำแนะนำ) สำหรับกิจกรรมที่ผู้ใช้กำลังทำในโทรศัพท์ที่อยู่ใกล้เคียง ผู้ใช้สามารถทำกิจกรรมต่อใน Googlebook ได้ด้วยการคลิกเพียงครั้งเดียว

การติดตั้งใช้งาน Handoff ข้ามอุปกรณ์ช่วยให้แอปของคุณโอนบริบทกิจกรรมได้ เพื่อให้ผู้ใช้กลับมาทำงานต่อได้ ไม่ว่าจะในแอป Android แบบปรับได้หรือใน เบราว์เซอร์บนเดสก์ท็อป ณ จุดที่ค้างไว้ในโทรศัพท์

เลือกโฟลว์การส่งต่อ

ก่อนที่จะเปิดใช้การส่งต่อข้ามอุปกรณ์ ให้ตัดสินใจว่ากิจกรรมแต่ละอย่างในแอปโทรศัพท์ ควรเปิดอย่างไรเมื่อผู้ใช้เลือกคำแนะนำ "ทำต่อ" ใน Googlebook

โฟลว์การส่งต่อ กำหนดเป้าหมายใน Googlebook เหมาะสำหรับ Key API
การส่งต่อจากแอปหนึ่งไปยังอีกแอปหนึ่ง แอป Android ดั้งเดิมที่ทำงานในหน้าต่างเดสก์ท็อป แอปที่เพิ่มประสิทธิภาพสำหรับหน้าจอขนาดใหญ่และหน้าต่างเดสก์ท็อปใน Google Play HandoffActivityData.Builder
การส่งต่อจากแอปหนึ่งไปยังอีกแอปหนึ่งพร้อมการเปลี่ยนเส้นทางไปยังเว็บ แอป Android เนทีฟหากติดตั้งไว้ หรือ URL ในเบราว์เซอร์เริ่มต้นบนเดสก์ท็อป แอปที่ได้รับการเพิ่มประสิทธิภาพเพื่อประสบการณ์การใช้งานหน้าต่างเดสก์ท็อปแบบเนทีฟ แต่ต้องมีเบราว์เซอร์สำรองที่เชื่อถือได้เมื่อไม่ได้ติดตั้งแอปใน Googlebook setFallbackUri
การส่งต่อโดยตรงไปยังเว็บ URL ในเบราว์เซอร์เริ่มต้นบนเดสก์ท็อป (หรือที่แอปที่มาพร้อมเครื่องสกัดกั้นไว้หากกำหนดค่าสำหรับ Android App Link) เวิร์กโฟลว์ที่เว็บแอปพลิเคชันเป็นประสบการณ์การใช้งานเดสก์ท็อปหลัก 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 ใน 2 กรณีต่อไปนี้

  • การส่งต่อที่ทำงานอยู่เบื้องหน้า: กิจกรรมทำงานอยู่เบื้องหน้าในโทรศัพท์ และ ผู้ใช้เริ่มการส่งต่อจากแถบงานของ Googlebook
  • การบันทึกสถานะเบื้องหลัง: กิจกรรมจะย้ายไปทำงานที่เบื้องหลังในโทรศัพท์ และระบบจะแคชเพย์โหลดการส่งต่อในกรณีที่ผู้ใช้เลือกคำแนะนำใน Googlebook ในภายหลัง

ตรวจสอบว่าคำขอส่งต่อใช้งานอยู่หรือไม่

ตรวจสอบ HandoffActivityDataRequestInfo โดยใช้ isActiveRequest เพื่อ แยกความแตกต่างระหว่างการส่งต่อที่ผู้ใช้เริ่มต้นซึ่งใช้งานอยู่กับการบันทึกในเบื้องหลัง

  • หาก isActiveRequest แสดงผลเป็น true แสดงว่าผู้ใช้ได้ขอให้ ดำเนินการต่อในอุปกรณ์อื่นขณะที่กิจกรรมยังคงปรากฏอยู่
  • หาก isActiveRequest แสดงผลเป็น false แสดงว่าระบบกำลังแคชสถานะเมื่อ กิจกรรมหยุดลง

หากคุณแสดงตัวบ่งชี้ภาพในโทรศัพท์เพื่อยืนยันว่างานกำลังย้ายไปยัง Googlebook ให้กำหนดเวลาการอัปเดต UI แบบไม่พร้อมกันเพื่อให้ onHandoffActivityDataRequested แสดงผลได้โดยไม่ชักช้า

แนบส่วนเสริมของสถานะและฟอลแบ็กของเว็บ

เพย์โหลด HandoffActivityData มีข้อมูลต่อไปนี้

  • คอมโพเนนต์เป้าหมาย (ต้องระบุสำหรับการส่งต่อจากแอปหนึ่งไปยังอีกแอปหนึ่ง): ส่ง this.getComponentName() (ComponentName ของกิจกรรม) หรือ ComponentName(this, TargetActivity::class.java) ที่ชัดเจนไปยัง HandoffActivityData.Builder เพื่อระบุกิจกรรมที่จะเปิดใน Googlebook
  • Extras (ไม่บังคับ): ส่ง PersistableBundle ไปยัง setExtras() ที่มีตัวระบุและพิกัด UI (เช่น รหัสเอกสารที่เปิดอยู่ คีย์แท็บที่ใช้งานอยู่ หรือตำแหน่งการเลื่อน) โดยชุดต้องมีขนาดเล็กกว่า 50 KB
  • 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 ของกิจกรรมที่รับ ให้ดึงข้อมูลพิเศษจาก Intent ที่เข้ามา การจัดการ onNewIntent ช่วยให้มั่นใจได้ว่าหากกิจกรรมใช้ singleTop launch mode หรือกำหนดเส้นทางไปยังหน้าต่างเดสก์ท็อปที่มีอยู่ หน้าต่างจะอัปเดตเป็นรายการที่ส่งต่อ
  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. เตรียมอุปกรณ์ทั้ง 2 เครื่อง
    • ลงชื่อเข้าใช้บัญชี Google เดียวกันทั้งในโทรศัพท์ Android (ที่ใช้ Android 17 ขึ้นไป) และ Googlebook (หรืออุปกรณ์ทดสอบที่ทำงานในโหมดการแสดงหน้าต่างบนเดสก์ท็อป)
    • เปิดบลูทูธและเชื่อมต่ออุปกรณ์ทั้ง 2 เครื่องกับเครือข่าย Wi-Fi เดียวกัน
    • ในอุปกรณ์ทั้ง 2 เครื่อง ให้เปิดการตั้งค่า > อุปกรณ์ที่เชื่อมต่อ > ค่ากำหนดการเชื่อมต่อ > บริการข้ามอุปกรณ์ > ทำกิจกรรมต่อ ทำตามข้อความแจ้งการตั้งค่า และเปิดใช้งาน ดูข้อกำหนดเบื้องต้นของสภาพแวดล้อมทั้งหมดได้ที่ตั้งค่าและทดสอบ
  2. ติดตั้งแอป
    • สำหรับการส่งต่อจากแอปหนึ่งไปยังอีกแอปหนึ่ง ให้ติดตั้งบิลด์ทั้งในโทรศัพท์และ Googlebook
    • สำหรับการเปลี่ยนเส้นทางไปยังเว็บหรือการส่งต่อโดยตรงไปยังเว็บ ให้ติดตั้งบิลด์ในโทรศัพท์และปล่อยให้แอปไม่ได้ติดตั้งใน Googlebook เพื่อให้คุณยืนยันได้ว่าเบราว์เซอร์เปิด URL ที่คาดไว้
  3. ทริกเกอร์การโอน
    • ในโทรศัพท์ ให้เปิดแอปและไปที่กิจกรรมที่เปิดใช้ การส่งต่อ (การเรียก setHandoffEnabled(true, null) สำหรับการส่งต่อจากแอปหนึ่งไปยังอีกแอปหนึ่ง หรือการส่ง HandoffActivityParams พร้อม setAllowHandoffWithoutPackageInstalled(true) เมื่อทดสอบโดยที่ไม่ได้ติดตั้งแอป ใน Googlebook)
    • ใน Googlebook ให้ดูที่ด้านขวาของแถบงานเพื่อหาไอคอนแอป ที่มีป้าย "ดำเนินการต่อ" แล้วเลือก
  4. ยืนยันการกู้คืนเดสก์ท็อป
    • ยืนยันว่าแอปเปิดในหน้าต่างเดสก์ท็อป (หรือแท็บเบราว์เซอร์) ที่ รายการและตำแหน่งการเลื่อนที่แน่นอนจากโทรศัพท์
    • ปรับขนาดหน้าต่างแอปใน Googlebook และตรวจสอบว่าเนื้อหาที่กู้คืน และการเลือกยังคงอยู่เมื่อเปลี่ยนขนาดหน้าต่าง

แหล่งข้อมูลเพิ่มเติม