1. Prima di iniziare
Che cosa hanno di speciale gli smartphone pieghevoli?
I pieghevoli sono innovazioni uniche nel loro genere. Offrono esperienze uniche e con loro arrivano opportunità uniche per deliziare gli utenti con funzionalità differenziate come l'interfaccia utente da tavolo per l'utilizzo a mani libere.
Prerequisiti
- Conoscenza di base dello sviluppo di app per Android
- Conoscenza di base del framework di inserimento delle dipendenze Hilt
Cosa creerai
In questo codelab, creerai un'app fotocamera con layout ottimizzati per i dispositivi pieghevoli.

Inizi con un'app Fotocamera di base che non reagisce alla postura del dispositivo e non sfrutta la fotocamera posteriore migliore per selfie migliorati. Aggiorni il codice sorgente per spostare l'anteprima sul display più piccolo quando il dispositivo è aperto e reagire all'impostazione dello smartphone in modalità Tavolo.
Sebbene l'app Fotocamera sia il caso d'uso più conveniente per questa API, entrambe le funzionalità che impari in questo codelab possono essere applicate a qualsiasi app.
Cosa imparerai a fare
- Come utilizzare Jetpack Window Manager per reagire al cambio di postura
- Come spostare l'app sul display più piccolo di un pieghevole
Che cosa ti serve
- Una versione recente di Android Studio
- Un dispositivo pieghevole o un emulatore pieghevole.
2. Configurazione
Ottieni il codice iniziale
- Se hai installato Git, puoi semplicemente eseguire il comando riportato di seguito. Per verificare se Git è installato, digita
git --versionnel terminale o nella riga di comando e verifica che venga eseguito correttamente.
git clone https://github.com/android/large-screen-codelabs.git
- (Facoltativo) Se non hai Git, puoi fare clic sul pulsante seguente per scaricare tutto il codice per questo codelab:
Apri il primo modulo
- In Android Studio, apri il primo modulo in
/step1.

Se ti viene chiesto di utilizzare l'ultima versione di Gradle, procedi con l'aggiornamento.
3. Esegui e osserva
- Esegui il codice sul modulo
step1.
Come puoi vedere, si tratta di una semplice app Fotocamera. Puoi passare dalla fotocamera anteriore a quella posteriore e viceversa e regolare le proporzioni. Tuttavia, il primo pulsante da sinistra non fa nulla, ma sarà il punto di accesso alla modalità Selfie con la fotocamera posteriore.

- Ora prova a mettere il dispositivo in posizione semiaperta, in cui la cerniera non è completamente piatta o chiusa, ma forma un angolo di 90 gradi.
Come puoi vedere, l'app non risponde alle diverse posture del dispositivo, quindi il layout non cambia, lasciando la cerniera al centro del mirino.
4. Scopri di più su Jetpack WindowManager
La libreria Jetpack WindowManager aiuta gli sviluppatori di app a creare esperienze ottimizzate per i dispositivi pieghevoli. Contiene la classe FoldingFeature che descrive una piega in un display flessibile o una cerniera tra due pannelli del display fisici. La sua API fornisce l'accesso a informazioni importanti relative al dispositivo:
state()restituisceFLATse la cerniera è aperta a 180 gradi oHALF_OPENEDin caso contrario.orientation()restituisceFoldingFeature.Orientation.HORIZONTALse la larghezza diFoldingFeatureè maggiore dell'altezza; altrimenti restituisceFoldingFeature.Orientation.VERTICAL.bounds()fornisce i limiti diFoldingFeaturein un formatoRect.
La classe FoldingFeature contiene informazioni aggiuntive, come occlusionType() o isSeparating(), ma questo codelab non le esplora in dettaglio.
A partire dalla versione 1.2.0-beta01, la libreria utilizza WindowAreaController, un'API che consente alla modalità Display posteriore di spostare la finestra corrente sul display allineato alla fotocamera posteriore, il che è ideale per scattare selfie con la fotocamera posteriore e per molti altri casi d'uso.
Aggiungere dipendenze
- Per utilizzare Jetpack WindowManager nella tua app, devi aggiungere le seguenti dipendenze al file
build.gradlea livello di modulo:
step1/build.gradle
def work_version = '1.2.0-beta01'
implementation "androidx.window:window:$work_version"
implementation "androidx.window:window-java:$work_version"
implementation "androidx.window:window-core:$work_version"
Ora puoi accedere alle classi FoldingFeature e WindowAreaController nella tua app. Utilizzale per creare la migliore esperienza di fotocamera pieghevole.
5. Implementare la modalità Selfie con la fotocamera posteriore
Inizia con la modalità Display posteriore.
L'API che consente questa modalità è WindowAreaController, che fornisce le informazioni e il comportamento relativi allo spostamento delle finestre tra display o aree di visualizzazione su un dispositivo.
Consente di eseguire query sull'elenco di WindowAreaInfo con cui è attualmente possibile interagire.
Utilizzando WindowAreaInfo puoi accedere a WindowAreaSession, un'interfaccia per rappresentare una funzionalità di area della finestra attiva e lo stato di disponibilità per un determinato WindowAreaCapability.
- Dichiara queste variabili in
MainActivity:
step1/MainActivity.kt
private lateinit var windowAreaController: WindowAreaController
private lateinit var displayExecutor: Executor
private var rearDisplaySession: WindowAreaSession? = null
private var rearDisplayWindowAreaInfo: WindowAreaInfo? = null
private var rearDisplayStatus: WindowAreaCapability.Status =
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED
private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA
- e inizializzali nel metodo
onCreate():
step1/MainActivity.kt
displayExecutor = ContextCompat.getMainExecutor(this)
windowAreaController = WindowAreaController.getOrCreate()
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
windowAreaController.windowAreaInfos
.map{info->info.firstOrNull{it.type==WindowAreaInfo.Type.TYPE_REAR_FACING}}
.onEach { info -> rearDisplayWindowAreaInfo = info }
.map{it?.getCapability(rearDisplayOperation)?.status?: WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED }
.distinctUntilChanged()
.collect {
rearDisplayStatus = it
updateUI()
}
}
}
- Ora implementa la funzione
updateUI()per attivare o disattivare il pulsante per i selfie posteriori, a seconda dello stato attuale:
step1/MainActivity.kt
private fun updateUI() {
if(rearDisplaySession != null) {
binding.rearDisplay.isEnabled = true
// A session is already active, clicking on the button will disable it
} else {
when(rearDisplayStatus) {
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> {
binding.rearDisplay.isEnabled = false
// RearDisplay Mode is not supported on this device"
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> {
binding.rearDisplay.isEnabled = false
// RearDisplay Mode is not currently available
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
binding.rearDisplay.isEnabled = true
// You can enable RearDisplay Mode
}
WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> {
binding.rearDisplay.isEnabled = true
// You can disable RearDisplay Mode
}
else -> {
binding.rearDisplay.isEnabled = false
// RearDisplay status is unknown
}
}
}
}
Quest'ultimo passaggio è facoltativo, ma è molto utile per conoscere tutti i possibili stati di un WindowAreaCapability.
- Ora implementa la funzione
toggleRearDisplayMode, che chiuderà la sessione se la funzionalità è già attiva o chiamerà la funzionetransferActivityToWindowArea:
step1/CameraViewModel.kt
private fun toggleRearDisplayMode() {
if(rearDisplayStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
if(rearDisplaySession == null) {
rearDisplaySession = rearDisplayWindowAreaInfo?.getActiveSession(rearDisplayOperation)
}
rearDisplaySession?.close()
} else {
rearDisplayWindowAreaInfo?.token?.let { token ->
windowAreaController.transferActivityToWindowArea(
token = token,
activity = this,
executor = displayExecutor,
windowAreaSessionCallback = this
)
}
}
}
Nota l'utilizzo di MainActivity come WindowAreaSessionCallback.
L'API Rear Display funziona con un approccio basato su listener: quando richiedi di spostare i contenuti sull'altro display, avvii una sessione restituita tramite il metodo onSessionStarted() del listener. Quando invece vuoi tornare al display interno (e più grande), chiudi la sessione e ricevi una conferma nel metodo onSessionEnded(). Per creare un listener di questo tipo, devi implementare l'interfaccia WindowAreaSessionCallback.
- Modifica la dichiarazione
MainActivityin modo che implementi l'interfacciaWindowAreaSessionCallback:
step1/MainActivity.kt
class MainActivity : AppCompatActivity(), WindowAreaSessionCallback
Ora implementa i metodi onSessionStarted e onSessionEnded all'interno di MainActivity. Questi metodi di callback sono estremamente utili per ricevere una notifica dello stato della sessione e aggiornare l'app di conseguenza.
Questa volta, però, per semplicità, controlla solo nel corpo della funzione se sono presenti errori e registra lo stato.
step1/MainActivity.kt
override fun onSessionEnded(t: Throwable?) {
if(t != null) {
Log.d("Something was broken: ${t.message}")
}else{
Log.d("rear session ended")
}
}
override fun onSessionStarted(session: WindowAreaSession) {
Log.d("rear session started [session=$session]")
}
- Crea ed esegui l'app. Se poi apri il dispositivo e tocchi il pulsante del display posteriore, viene visualizzato un messaggio come questo:

- Seleziona "Cambia schermi ora" per vedere i tuoi contenuti spostati sul display esterno.
6. Implementare la modalità da tavolo
Ora è il momento di rendere la tua app consapevole della piega: sposta i contenuti di lato o sopra la cerniera del dispositivo in base all'orientamento della piega. Per farlo, agirai all'interno di FoldingStateActor in modo che il codice sia separato da Activity per una maggiore leggibilità.
La parte principale di questa API è costituita dall'interfaccia WindowInfoTracker, creata con un metodo statico che richiede un Activity:
step1/CameraCodelabDependencies.kt
@Provides
fun provideWindowInfoTracker(activity: Activity) =
WindowInfoTracker.getOrCreate(activity)
Non è necessario scrivere questo codice perché è già presente, ma è utile capire come viene creato WindowInfoTracker.
- Per rilevare eventuali modifiche alla finestra, ascolta queste modifiche nel metodo
onResume()del tuoActivity:
step1/MainActivity.kt
lifecycleScope.launch {
foldingStateActor.checkFoldingState(
this@MainActivity,
binding.viewFinder
)
}
- Ora apri il file
FoldingStateActor, perché è il momento di compilare il metodocheckFoldingState().
Come hai già visto, viene eseguito nella fase RESUMED del Activity e sfrutta WindowInfoTracker per rilevare eventuali modifiche al layout.
step1/FoldingStateActor.kt
windowInfoTracker.windowLayoutInfo(activity)
.collect { newLayoutInfo ->
activeWindowLayoutInfo = newLayoutInfo
updateLayoutByFoldingState(cameraViewfinder)
}
Utilizzando l'interfaccia WindowInfoTracker, puoi chiamare windowLayoutInfo() per raccogliere un Flow di WindowLayoutInfo che contiene tutte le informazioni disponibili in DisplayFeature.
L'ultimo passaggio consiste nel reagire a queste modifiche e spostare i contenuti di conseguenza. Lo fai all'interno del metodo updateLayoutByFoldingState(), un passaggio alla volta.
- Assicurati che
activityLayoutInfocontenga alcune proprietàDisplayFeaturee che almeno una di queste siaFoldingFeature, altrimenti non devi fare nulla:
step1/FoldingStateActor.kt
val foldingFeature = activeWindowLayoutInfo?.displayFeatures
?.firstOrNull { it is FoldingFeature } as FoldingFeature?
?: return
- Calcola la posizione della piega per assicurarti che la posizione del dispositivo influisca sul layout e non si trovi al di fuori dei limiti della gerarchia:
step1/FoldingStateActor.kt
val foldPosition = FoldableUtils.getFeaturePositionInViewRect(
foldingFeature,
cameraViewfinder.parent as View
) ?: return
Ora hai la certezza di avere un FoldingFeature che influisce sul layout, quindi devi spostare i contenuti.
- Controlla se
FoldingFeatureèHALF_OPEN, altrimenti ripristina la posizione dei tuoi contenuti. Se èHALF_OPEN, devi eseguire un altro controllo e agire in modo diverso in base all'orientamento della piega:
step1/FoldingStateActor.kt
if (foldingFeature.state == FoldingFeature.State.HALF_OPENED) {
when (foldingFeature.orientation) {
FoldingFeature.Orientation.VERTICAL -> {
cameraViewfinder.moveToRightOf(foldPosition)
}
FoldingFeature.Orientation.HORIZONTAL -> {
cameraViewfinder.moveToTopOf(foldPosition)
}
}
} else {
cameraViewfinder.restore()
}
Se la piega è VERTICAL, sposta i contenuti a destra, altrimenti sopra la posizione della piega.
- Crea ed esegui l'app, quindi apri il dispositivo e posizionalo in modalità Da tavolo per vedere i contenuti spostarsi di conseguenza.
7. Complimenti!
In questo codelab hai scoperto alcune funzionalità uniche dei dispositivi pieghevoli, come la modalità Display posteriore o la modalità Tavolo, e come sbloccarle utilizzando Jetpack WindowManager.
Ora puoi implementare esperienze utente eccezionali per la tua app fotocamera.