사용자는 Android 휴대전화에서 문서 검토, 메시지 분류, 다양한 항목 탐색과 같은 작업을 시작한 후 더 큰 디스플레이, 실제 키보드, 멀티 윈도우 작업 공간이 있는 Googlebook으로 이동하고 싶어 합니다.
Android 17 (API 수준 37) 이상의 계속하기 기능을 사용하면 Googlebook 작업 표시줄에 사용자가 근처 휴대전화에서 실행 중인 활동의 앱 아이콘 (추천이라고 함)이 표시됩니다. 사용자는 한 번의 클릭으로 Googlebook에서 활동을 계속할 수 있습니다.
크로스 디바이스 핸드오프를 구현하면 앱이 활동 컨텍스트를 전송하여 사용자가 적응형 Android 앱이나 데스크톱 브라우저에서 휴대전화에서 중단한 정확한 위치에서 워크플로를 재개할 수 있습니다.
핸드오프 흐름 선택
교차 기기 핸드오프를 사용 설정하기 전에 사용자가 Googlebook에서 '계속하기' 제안을 선택할 때 휴대전화 앱의 각 Activity가 어떻게 열려야 하는지 결정하세요.
| 인계 흐름 | Googlebook에서 타겟팅 | 권장 용도 | 주요 API |
|---|---|---|---|
| 앱 간 핸드오프 | 데스크톱 창에서 실행되는 네이티브 Android 앱 | Google Play의 대형 화면 및 데스크톱 창 모드에 최적화된 앱 | HandoffActivityData.Builder |
| 웹 대체가 포함된 앱 간 핸드오프 | 설치된 경우 네이티브 Android 앱, 그렇지 않은 경우 기본 데스크톱 브라우저의 URL | 기본 데스크톱 창 환경에 최적화되었지만 Googlebook에 앱이 설치되지 않은 경우 안정적인 브라우저 대체가 필요한 앱 | setFallbackUri |
| 웹 직접 인계 | 기본 데스크톱 브라우저의 URL (또는 Android 앱 링크로 구성된 경우 네이티브 앱에 의해 가로채기됨) | 웹 애플리케이션이 기본 데스크톱 환경인 워크플로 | HandoffActivityData.createWebHandoff |
활동의 핸드오프 사용 설정
Continue On은 기본적으로 사용 중지되어 있으며 활동별로 구성됩니다. 휴대전화의 각 전송 활동에서 다음 단계를 따르세요.
- 활동이 핸드오프에 필요한 상태를 로드하면
setHandoffEnabled를 호출하고true를 전달합니다. 사용 설정되면 활동이 포그라운드에 있거나 백그라운드로 이동하는 동안 시스템에서 핸드오프 데이터를 요청할 수 있습니다. - null이 아닌
HandoffActivityData객체를 반환하도록onHandoffActivityDataRequested재정의 언제든지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 작업 표시줄에서 핸드오프를 시작합니다.
- 백그라운드 상태 캡처: 활동이 휴대전화에서 백그라운드로 이동하고 사용자가 나중에 Googlebook에서 제안을 선택할 경우를 대비해 시스템에서 핸드오프 페이로드를 캐시합니다.
핸드오프 요청이 활성 상태인지 확인
isActiveRequest를 사용하여 HandoffActivityDataRequestInfo를 검사하여 활성 사용자 시작 핸드오프와 백그라운드 저장 간에 차이를 구분합니다.
isActiveRequest가true를 반환하면 활동이 표시되는 동안 사용자가 다른 기기에서 계속하도록 적극적으로 요청한 것입니다.isActiveRequest이false를 반환하면 시스템은 활동이 중지될 때 상태를 캐시하고 있습니다.
작업이 Googlebook으로 이동하고 있음을 확인하는 시각적 표시기를 휴대전화에 표시하는 경우 onHandoffActivityDataRequested가 지연 없이 반환되도록 UI 업데이트를 비동기적으로 예약합니다.
상태 추가 기능 및 웹 대체 연결
HandoffActivityData 페이로드에는 다음 정보가 포함됩니다.
- 타겟 구성요소 (앱 간 핸드오프에 필요):
this.getComponentName()(활동의ComponentName) 또는 명시적ComponentName(this, TargetActivity::class.java)를HandoffActivityData.Builder에 전달하여 Googlebook에서 실행할 활동을 지정합니다. - Extras (선택사항): 식별자와 UI 좌표 (예: 열린 문서 ID, 활성 탭 키, 스크롤 위치)가 포함된
PersistableBundle를setExtras()에 전달합니다. 번들은 50KB 미만이어야 합니다. - 대체 URI (선택사항): Android 앱이 설치되어 있지 않은 경우 Googlebook이 기본 브라우저에서 작업을 열 수 있도록 웹
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"
}
}
웹 직접 핸드오프 구성
Googlebook의 기본 데스크톱 환경이 웹 애플리케이션인 경우 setAllowHandoffWithoutPackageInstalled(true)와 함께 HandoffActivityParams를 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)
}
}
Googlebook에서 상태 수신 및 복원
사용자가 Googlebook 작업 표시줄에서 '계속' 제안을 선택하면 시스템이 데스크톱 창에서 타겟 ComponentName를 실행하고 PersistableBundle 추가 항목을 실행 Intent에 연결합니다.
Googlebook에서는 데스크톱 창 모드와 다중 창 레이아웃을 염두에 두고 수신 핸드오프를 처리합니다.
- 핸드오프 추가 항목 추출: 수신 활동의
onCreate및onNewIntent메서드에서 수신되는Intent로부터 추가 항목을 검색합니다.onNewIntent를 처리하면 활동이singleTop시작 모드를 사용하거나 기존 데스크톱 창으로 라우팅되는 경우 창이 핸드오프된 항목으로 업데이트됩니다. - 상태를 다중 창 레이아웃에 적응: 휴대전화에서 전체 화면 세부정보 뷰를 차지한 상태는 Googlebook의 목록-세부정보 또는 지원 창 레이아웃 내 선택된 항목에 매핑되는 경우가 많습니다. 기본 목록 선택과 세부정보 창을 모두 채워 데스크톱 창에 전체 컨텍스트가 표시되도록 합니다.
- 창 크기 조절 시 복원된 상태 유지: Googlebook에서 자유 형식 창 크기 조절이 복원된 세션을 재설정하지 않도록 핸드오프된 상태를 Jetpack Compose의
ViewModel(SavedStateHandle) 또는rememberSaveable에 저장합니다.
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 간 연동 테스트
엔드 투 엔드 구현을 확인하려면 다음 단계를 따르세요.
- 두 기기 모두 준비하기
- Android 17 이상을 실행하는 Android 휴대전화와 Googlebook(또는 데스크톱 창 모드에서 실행되는 테스트 기기)에서 동일한 Google 계정에 로그인합니다.
- 블루투스를 사용 설정하고 두 기기를 동일한 Wi-Fi 네트워크에 연결합니다.
- 두 기기에서 설정 > 연결된 기기 > 연결 환경설정 > 기기 간 공유 > 이어서 작업하기를 열고 설정 메시지에 따라 작업을 사용 설정합니다. 전체 환경 필수 요건은 설정 및 테스트를 참고하세요.
- 앱 설치
- 앱 간 핸드오프의 경우 휴대전화와 Googlebook에 빌드를 설치합니다.
- 웹 대체 또는 웹으로 직접 핸드오프의 경우 휴대전화에 빌드를 설치하고 Googlebook에서 앱을 제거한 상태로 두어 브라우저가 예상 URL을 여는지 확인할 수 있습니다.
- 핸드오프 트리거
- 휴대전화에서 앱을 열고 핸드오프를 사용 설정하는 활동으로 이동합니다 (앱 간 핸드오프의 경우
setHandoffEnabled(true, null)호출, Googlebook에 앱이 설치되지 않은 상태에서 테스트하는 경우setAllowHandoffWithoutPackageInstalled(true)와 함께HandoffActivityParams전달). - Googlebook에서 작업 표시줄의 오른쪽에 있는 '계속' 배지가 있는 앱 아이콘을 확인하고 선택합니다.
- 휴대전화에서 앱을 열고 핸드오프를 사용 설정하는 활동으로 이동합니다 (앱 간 핸드오프의 경우
- 데스크톱 복원 확인
- 앱이 휴대전화의 정확한 항목과 스크롤 위치에서 데스크톱 창 (또는 브라우저 탭)에 열리는지 확인합니다.
- Googlebook에서 앱 창의 크기를 조절하고 복원된 콘텐츠와 선택이 창 크기 클래스 전환 전반에 걸쳐 유지되는지 확인합니다.