폴더블폰용 앱 빌드

Android 10 (API 레벨 29)에서는 폴더블폰 기기와 다양한 폴딩 패턴에 더 많은 지원을 추가합니다.

기기를 펼쳐서 더 큰 화면을 제공하면 사용자에게 긍정적 영향을 줄 수 있습니다.

  • 일반적으로 화면이 크면 더욱 실감 나는 경험을 할 수 있습니다.
  • 멀티 윈도우로 사용자가 동시에 여러 가지 작업을 할 수 있습니다.

기기를 접고 펴면 화면 크기, 밀도 또는 비율이 변경될 수 있습니다. 이는 Android 개발에 있어서 처음 발생한 문제는 아닙니다. 이미 폴더블폰 기기가 아닌 사례에서도 존재하던 문제입니다.

  • 전화기: 가로 모드와 세로 모드 간 전환.
  • 데스크톱 모드에서 실행되는 Chrome OS: Android 앱 크기 조정.
  • 화면이 여러 개이거나 추가된 기기.

이 페이지에서는 앱이 폴더블폰 폼 팩터와 잘 호환되도록 하기 위한 권장사항을 설명합니다.

폴더블폰 지원과 관련된 변경사항에 대한 Android 10 요약문을 읽어보는 것도 좋습니다.

앱 연속성

앱을 폴더블폰 기기에서 실행할 때는 한 화면에서 다른 화면으로 자동 전환할 수 있습니다. 우수한 사용자 환경을 제공하려면 화면이 전환된 뒤에도 기존의 작업을 매끄럽게 지속하는 것이 중요합니다. 앱은 동일한 상태와 위치에서 다시 시작되어야 합니다. 폴더블폰 기기는 여러 가지 방식으로 접을 수 있습니다(예를 들어 안으로 접거나 밖으로 접을 수 있음).

시스템이 화면 전환 중에 구성을 변경하기 때문에 앱은 UI 상태를 저장하고 구성 변경을 적절히 지원해야 합니다.

크기 조정이 가능하도록 앱 빌드

앱이 멀티 윈도우 모드와 동적인 크기 조정에서 작동하도록 해야 합니다. 이를 위해서는 resizeableActivity=true로 설정하면 됩니다. 이렇게 하면 앱이 어떤 폼 팩터와 환경(예: 폴더블폰, 데스크톱 모드 또는 자유형 창)을 사용하든 최대한의 호환성을 제공할 수 있습니다. 앱의 동작을 분할된 화면이나 폴더블폰 에뮬레이터에서 테스트하세요.

앱에서 resizeableActivity=false로 설정하면 멀티 윈도우를 지원하지 않는다는 의미입니다. 그래도 시스템에서 앱의 크기를 조정하거나 멀티 윈도우 모드에 넣을 수는 있지만 앱의 모든 구성요소에 동일한 구성을 적용하여 호환성을 구성합니다(모든 Activity, 서비스 등을 포함). 경우에 따라서 큰 변경사항(예: 디스플레이 크기 변경)이 있으면 구성을 변경하기보다는 프로세스를 다시 시작할 수도 있습니다.

예를 들어 아래의 Activity는 maxAspectRatio와 함께 resizableActivity=false로 설정되었습니다. 기기를 펼쳤을 때 앱을 호환성 모드에 넣어서 Activity 구성, 크기, 화면비를 유지합니다.

resizeableActivity를 설정하지 않거나 true로 설정하지 않으면 시스템에서는 앱이 멀티 윈도우를 완전히 지원하고 크기 조정이 가능하다고 가정합니다.

일부 OEM은 Activity의 디스플레이 영역이 변경될 때마다 화면에 작은 재시작 아이콘을 추구하는 기능을 구현할 수도 있습니다. 이렇게 하면 사용자가 새로운 구성으로 Activity를 다시 시작할 수 있습니다.

새로운 화면비

Android 10 (API 레벨 29) 이상는 다양한 화면비를 지원합니다. 폴더블폰은 매우 길고 얇은 화면(예: 접힌 기기의 경우 21:9)에서 1:1 비율에 이르기까지 폼 팩터가 매우 다양할 수 있습니다.

최대한 많은 기기에 호환성을 제공하려면 최대한 많은 화면비로 앱을 테스트해야 합니다.

이런 화면비를 일부 지원할 수 없을 경우 maxAspectRatio(이전과 동일)와 minAspectRatio를 사용하여 앱이 처리할 수 있는 가장 높은 비율과 가장 낮은 비율을 표시해야 합니다. 화면이 이 제한을 초과할 경우, 앱에 호환성 모드를 적용할 수도 있습니다.

하단 탐색 뷰에 5개의 아이콘이 있다면 Android 10 (API 레벨 29) 이상를 실행하는 기기는 터치 타겟 크기가 2인치 이상이 보장됩니다. 호환성 정의 문서를 참조하세요.

멀티 윈도우

멀티 윈도우를 실행하는 기능은 큰 화면을 사용하는 장점 중 하나입니다. 예전에는 일부 기기에서 두 개의 앱을 나란히 사용하는 경우가 많았습니다. 기술이 발달된 덕분에 화면에서 세 개 이상의 앱을 동시에 실행하고 그 앱들이 서로 콘텐츠를 공유할 수 있는 수준이 되었습니다.

앱이 멀티 윈도우를 적절히 지원하지 않는다면 resizeableActivity=false로 설정할 수 있습니다. 자세한 내용은 멀티 윈도우 가이드를 참조하세요.

멀티 윈도우가 더욱 일반화되면 앱에서 드래그 앤 드롭을 지원하는 방안도 생각해보세요.

멀티 재시작

Android 9.0 이하를 실행할 때는 포커스에 있는 앱만 재개됨 상태에 들어갑니다. 그 외에 보이는 다른 모든 Activity는 일시정지됩니다. 앱이 리소스를 닫거나 일시정지했을 때 콘텐츠 재생을 중지하면 문제가 생길 수 있습니다.

Android 10에서는 기기가 멀티 윈도우 모드에 있을 때 모든 Activity가 재개됨 상태로 남아 있도록 동작을 변경했습니다. 이를 멀티 재시작이라고 합니다. 위에 투명한 Activity가 있거나 Activity에 포커스를 주지 못할 경우(예: PIP 모드) 해당 Activity를 일시정지할 수 있습니다. 또한 특정 시간에는 어떤 Activity에도 포커스를 주지 않을 수 있습니다. 예를 들어 알림 창이 열려 있는 경우가 해당합니다. OnStop은 평소와 같이 작동합니다. Activity가 화면에서 벗어날 때마다 호출될 것입니다.

멀티 재시작은 Android 9.0을 실행하는 일부 기기에도 제공됩니다. 이 기기에서 멀티 재시작을 옵트인하려면 다음 매니페스트 메타데이터를 추가할 수 있습니다.

<meta-data
    android:name="android.allow_multiple_resumed_activities" android:value="true" />

특정 기기가 이 매니페스트 메타데이터를 지원하는지 확인하려면 기기 사양을 참조하세요.

독점적 리소스 액세스

멀티 재시작 기능을 지원하는 데 도움을 주기 위해서 새로운 수명 주기 콜백인 Activity#onTopResumedActivityChanged()가 도입됩니다.

이 메서드는 Activity가 가장 위에 다시 시작된 Activity 위치를 얻거나 잃을 때 호출됩니다. Activity가 공유된 단일 사용자 리소스(예: 마이크, 카메라)를 사용할 때 알아두는 것이 중요합니다.

protected void onTopResumedActivityChanged(boolean topResumed) {
    if (topResumed) {
        // Top resumed activity
        // Can be a signal to re-acquire exclusive resources
    } else {
        // No longer the top resumed activity
    }
}

앱은 여러 가지 다른 이유로 리소스를 잃을 수 있습니다(예: 하드웨어의 공유된 부분 제거).

어떤 경우에도 앱은 리소스 손실 이벤트와 이용 가능한 리소스에 영향을 미치는 상태 변경사항을 적절히 처리해야 합니다.

카메라를 사용하는 앱은 카메라에 액세스하기에 적절한 시점이 되었다는 힌트로 CameraManager.AvailabilityCallback#onCameraAccessPrioritiesChanged() 메서드를 사용하는 것이 좋습니다. 이 메서드는 Android 10 (API 레벨 29) 이상에서 제공됩니다.

resizeableActivity=false는 독점적 카메라 액세스를 보증하지 않습니다. 카메라를 사용하는 다른 앱이 다른 디스플레이에서 열릴 수 있기 때문입니다.

멀티 윈도우 모드의 카메라.

앱이 포커스를 잃었을 때 반드시 카메라를 해제해야 할 필요는 없습니다. 예를 들어 사용자가 새롭게 포커스를 받고 가장 위에 다시 시작된 앱과 상호작용하는 동안 계속해서 카메라 미리보기를 원할 수도 있습니다. 최상위에서 다시 시작된 앱이 아닐 때 카메라를 계속 실행하는 것은 상관없지만, 연결 해제 사례를 적절히 처리해야 합니다. 최상위에서 다시 시작된 앱이 카메라 사용을 원한다면 이를 열 수 있고, 여러분의 앱이 카메라에 대한 액세스를 잃게 됩니다. 앱은 포커스를 다시 가져왔을 때 카메라를 다시 열 수 있습니다.

앱이 CameraDevice.StateCallback#onDisconnected() 콜백을 수신하고, 그 이후에 카메라 기기에 호출을 보내면 CameraAccessException이 발생합니다.

멀티 디스플레이

앞으로는 두 개 이상의 화면을 지원하거나 한 번에 여러 화면을 표시하는 폴더블폰이 출시될 수도 있습니다. 이 구성을 처리하는 것은 현재 개발자가 Chrome OS에서 투사된 화면을 처리하는 방법과 유사합니다.

Android 10 (API 레벨 29) 이상는 보조 디스플레이의 Activity를 지원합니다. Activity가 여러 디스플레이가 있는 기기에서 실행될 경우, 사용자는 다른 디스플레이로 Activity를 옮길 수 있습니다. 멀티 재시작은 멀티 스크린 시나리오에도 적용됩니다. 여러 Activity가 동시에 사용자 입력을 받을 수 있습니다.

앱은 시작하거나 다른 Activity를 만들 때, 어느 디스플레이에 실행해야 할지 지정할 수 있습니다. 이 동작은 매니페스트 파일에 정의된 Activity 시작 모드, 인텐트 플래그, Activity를 실행하는 엔터티에서 설정한 옵션에 따라 달라집니다. 자세한 내용은 ActivityOptions를 참조하세요.

기기를 접을 때의 전환과 마찬가지로 Activity가 보조 디스플레이로 이동하면 컨텍스트 업데이트, 창 크기 변경, 구성 및 리소스 변경을 거치게 될 수 있습니다. Activity가 구성 변경을 처리할 경우 onConfigurationChanged()에서 알림을 받게 됩니다. 그렇지 않을 경우에는 Activity가 다시 시작됩니다.

Activity는 onCreateonConfigurationChanged(처리할 경우)에서 현재 디스플레이를 확인해야 합니다. 디스플레이가 변경될 때 리소스와 레이아웃을 업데이트하세요.

Activity에서 선택된 시작 모드가 여러 개의 인스턴스를 허용할 경우, 보조 화면에서 시작하면 Activity의 새로운 인스턴스가 생성될 수 있다는 점을 명심하세요. 두 Activity는 동시에 다시 시작됩니다.

여러 디스플레이에 표시되는 Activity의 여러 인스턴스.

Android 8.0에 도입된 기존의 멀티 디스플레이 API에 대해 읽어보는 것도 좋습니다.

디스플레이 컷아웃

폴더블폰 기기는 접었을 때와 펼쳤을 때 컷아웃 형태가 다를 수 있습니다. 컷아웃 문제를 방지하려면 디스플레이 컷아웃 권장사항을 참조하세요.

Activity vs 애플리케이션 컨텍스트

멀티 디스플레이에서는 적절한 컨텍스트를 사용하는 것이 매우 중요합니다. 리소스에 액세스할 때 (화면에 표시된) Activity 컨텍스트는 (표시되지 않은) 애플리케이션 컨텍스트와 다릅니다.

Activity 컨텍스트에는 디스플레이에 대한 정보가 포함되고 항상 표시할 디스플레이 영역에 맞게 조정됩니다. 현재 디스플레이 지표와 리소스를 얻으려면 Activity 컨텍스트를 사용하세요. 이는 컨텍스트의 정보를 사용하는 일부 시스템 API(예: Toast)에도 영향을 미칩니다.

Activity 창 구성과 상위 디스플레이가 리소스와 컨텍스트를 정의합니다. 현재 디스플레이 사용권을 얻는 방법:

val activityDisplay = activity.windowManager.defaultDisplay

현재 Activity 창 지표 사용권을 얻는 방법:

val windowMetrics = DisplayMetrics()
activityDisplay.getMetrics(windowMetrics)

또는:

val windowMetrics = activity.resources.displayMetrics

보조 화면 사용

DisplayManager 시스템 서비스에서 이용 가능한 디스플레이를 가져올 수 있습니다.

val dm = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displays = dm.displays

Display 클래스를 사용하여 특정 디스플레이에 대한 정보를 가져오세요.

  • 디스플레이 지표는 화면의 크기, 해상도, 밀도에 대한 정보를 제공합니다.
  • 플래그에서 디스플레이가 안전한지 확인합니다.

Activity를 디스플레이에 시작할 수 있는지 확인하는 방법은 다음과 같습니다.

activityManager.isActivityStartAllowedOnDisplay(context, displayId, intent)

그리고 디스플레이에 Activity를 시작하는 방법은 다음과 같습니다.

val options = ActivityOptions.makeBasic()
options.launchDisplayId = targetDisplay.displayId
startActivity(intent, options.toBundle())

멀티 디스플레이 지원

Android는 이미 소프트웨어 키보드, 배경화면, 런처를 지원합니다.

소프트웨어 키보드

디스플레이가 시스템 장식을 지원하도록 구성된 경우, 키보드를 보조 화면에 표시할 수 있습니다. 텍스트 필드가 그 디스플레이에 입력을 요청하면 입력 메서드 편집기가 자동으로 나타납니다.

보조 디스플레이의 키보드.

배경화면

Android 10 (API 레벨 29) 이상에서 보조 화면은 배경화면을 적용할 수 있습니다. 프레임워크는 각 디스플레이에 대해 WallpaperService.Engine의 별도 인스턴스를 생성합니다. 각 엔진의 표면이 독립적으로 그려지도록 해야 합니다. 개발자는 WallpaperService.Engine#getDisplayContext()에서 디스플레이 컨텍스트를 사용하여 에셋을 로드할 수 있습니다. 또한 WallpaperInfo.xml 파일이 android:supportsMultipleDisplays="true"로 설정되도록 해야 합니다.

전화 및 보조 디스플레이의 배경화면.

런처

보조 화면에 전용 Activity를 제공하기 위한 새로운 인텐트 필터 카테고리인 SECONDARY_HOME이 도입되었습니다. 이 Activity의 인스턴스는 시스템 장식을 지원하는 모든 디스플레이에 각 1개씩 사용됩니다.

<activity>
    ...
    <intent-filter>
        <category android:name="android.intent.category.SECONDARY_HOME" />
        ...
    </intent-filter>
</activity>

Activity는 다중 인스턴스를 차단하지 않는 시작 모드가 있어야 하고 다양한 화면 크기에 맞게 조정될 수 있어야 합니다. 시작 모드는 singleInstance 또는 singleTask가 될 수 없습니다.

예를 들어 Launcher3의 AOSP 구현은 SECONDARY_HOME Activity를 지원합니다.

전화의 머티리얼 디자인 런처.

보조 디스플레이의 머티리얼 디자인 런처.

테스트

폴더블폰에 맞게 앱을 준비하려면 앱이 다음 항목에 대해 어떻게 반응하는지 테스트해야 합니다.

  • 구성 변경사항
  • 멀티 윈도우 및 멀티 재시작
  • 크기 조정 및 새로운 화면비

폴더블폰 에뮬레이터

AOSP 에뮬레이터는 접히는 기기를 지원합니다. 이렇게 하면 개발자들이 기기가 접히는 시나리오에서 앱을 테스트할 수 있습니다.

7.3’’ 폴더블폰 에뮬레이터

7.3" 디스플레이 해상도 논리적 디스플레이
크기 X Y densityDpi 크기
펼친 상태 7.3 1536 2152 420 대형
접힌 상태 4.6 840 1960 420 보통

8’’ 폴더블폰 에뮬레이터

8" 디스플레이 해상도 논리적 디스플레이
크기 X Y densityDpi 크기
펼친 상태 8.03 2200 2480 420 대형
접힌 상태 6.62 1148 2480 420 보통

AOSP 폴딩 에뮬레이터.

멀티 디스플레이 테스트

강제 데스크톱 모드라는 새로운 개발 옵션을 사용하면 개발자가 모든 보조 디스플레이에 대해 시스템 장식 지원을 켜고, 기본 디스플레이 대신 보조 디스플레이에 마우스 포인터를 표시할 수 있습니다. 자유형 창을 활성화한 상태에서 사용할 때는 강제 데스크톱이 멀티 윈도우를 사용하는 데스크톱 환경과 창 크기 조정 기능을 시뮬레이션합니다.

Pixel에서는 시뮬레이션된 디스플레이를 사용하여 이 기능을 사용해볼 수 있습니다. 또는 HDMI나 USB-C 디스플레이 포트를 지원하는 기기가 있을 경우, 유선 연결을 사용하여 테스트할 수 있습니다.

시뮬레이션된 디스플레이.