1. Hinweis
Was ist das Besondere an faltbaren Smartphones?
Faltbare Geräte sind innovative Lösungen der Generation. Sie bieten einzigartige Funktionen und bieten einzigartige Gelegenheiten, Ihre Nutzer mit differenzierten Funktionen wie „Auf dem Tisch“-UI zur Bedienung per Sprachbefehl zu begeistern.
Voraussetzungen
- Grundkenntnisse in der Entwicklung von Android-Apps
- Grundkenntnisse des Hilt Dependency Injection-Frameworks
Inhalt
In diesem Codelab erstellen Sie eine Kamera-App mit optimierten Layouts für faltbare Geräte.
Du beginnst mit einer einfachen Kamera-App, die nicht auf die Körperhaltung des Geräts reagiert und die bessere Rückkamera nicht für optimierte Selfies nutzt. Sie aktualisieren den Quellcode, um die Vorschau auf das kleinere Display zu verschieben, wenn das Gerät aufgeklappt ist, und darauf zu reagieren, wenn das Smartphone in den Modus „Auf dem Tisch“ gestellt wird.
Die Kamera-App ist zwar der praktischste Anwendungsfall für diese API, aber beide Funktionen, die Sie in diesem Codelab lernen, können auf jede App angewendet werden.
Aufgaben in diesem Lab
- So können Sie mit dem Jetpack-Fenstermanager auf Änderungen am Sicherheitsstatus reagieren
- App auf das kleinere Display eines faltbaren Geräts verschieben
Voraussetzungen
- Aktuelle Version von Android Studio
- Ein faltbares Gerät oder einen faltbaren Emulator
2. Einrichten
Startcode abrufen
- Wenn Sie Git installiert haben, können Sie einfach den folgenden Befehl ausführen. Um zu prüfen, ob Git installiert ist, geben Sie im Terminal oder in der Befehlszeile
git --version
ein und prüfen Sie, ob es korrekt ausgeführt wird.
git clone https://github.com/android/large-screen-codelabs.git
- Optional: Wenn Sie Git nicht installiert haben, können Sie auf die folgende Schaltfläche klicken, um den gesamten Code für dieses Codelab herunterzuladen: .
Erstes Modul öffnen
- Öffnen Sie in Android Studio das erste Modul unter
/step1
.
Wenn Sie aufgefordert werden, die neueste Gradle-Version zu verwenden, aktualisieren Sie sie.
3. Ausführen und beobachten
- Führen Sie den Code im Modul
step1
aus.
Wie du siehst, ist das eine einfache Kamera-App. Sie können zwischen der Front- und Rückkamera umschalten und das Seitenverhältnis anpassen. Die erste Schaltfläche auf der linken Seite hat im Moment zwar keine Funktion, sie wird aber als Einstiegspunkt für den Modus Rückkamera verwendet.
- Versuchen Sie nun, das Gerät in eine halb geöffnete Position zu bringen, in der das Scharnier nicht ganz flach oder geschlossen ist, sondern einen 90-Grad-Winkel bildet.
Wie Sie sehen, reagiert die App nicht auf verschiedene Gerätehaltungen und das Layout ändert sich nicht, sodass das Scharnier in der Mitte des Suchers bleibt.
4. Jetpack WindowManager
Mit der Jetpack WindowManager-Bibliothek können App-Entwickler optimierte Umgebungen für faltbare Geräte entwickeln. Sie enthält die Klasse FoldingFeature
, die eine Faltung in einem flexiblen Display oder ein Scharnier zwischen zwei physischen Displays beschreibt. Die API bietet Zugriff auf wichtige Informationen in Bezug auf das Gerät:
state()
gibtFLAT
zurück, wenn das Scharnier bei 180 Grad geöffnet ist, andernfallsHALF_OPENED
.orientation()
gibtFoldingFeature.Orientation.HORIZONTAL
zurück, wenn die Breite vonFoldingFeature
größer als die Höhe ist. Andernfalls wirdFoldingFeature.Orientation.VERTICAL
zurückgegeben.bounds()
gibt die Grenzen vonFoldingFeature
im FormatRect
an.
Die Klasse FoldingFeature
enthält zusätzliche Informationen wie occlusionType()
oder isSeparating()
, aber in diesem Codelab werden sie nicht ausführlich behandelt.
Ab Version 1.2.0-beta01 verwendet die Bibliothek WindowAreaController
, eine API, die es dem Rückdisplay ermöglicht, das aktuelle Fenster auf das Display zu verschieben, das mit der Rückkamera ausgerichtet ist. Das eignet sich hervorragend für die Aufnahme von Selfies mit der Rückkamera und für viele andere Anwendungsfälle.
Abhängigkeiten hinzufügen
- Um Jetpack WindowManager in Ihrer App zu verwenden, müssen Sie der Datei
build.gradle
auf Modulebene die folgenden Abhängigkeiten hinzufügen:
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"
Jetzt können Sie in Ihrer App sowohl auf die Klasse FoldingFeature
als auch auf die Klasse WindowAreaController
zugreifen. Damit erschaffen Sie ein ultimatives faltbares Kameraerlebnis!
5. Rückkamera-Modus implementieren
Starte mit dem Rückdisplaymodus.
Die API, die diesen Modus ermöglicht, ist die WindowAreaController
. Sie stellt Informationen und das Verhalten beim Verschieben von Fenstern zwischen Displays oder Anzeigebereichen auf einem Gerät bereit.
Sie können damit die Liste der WindowAreaInfo
abfragen, die derzeit für eine Interaktion zur Verfügung stehen.
Mit WindowAreaInfo
kannst du auf WindowAreaSession
zugreifen, eine Schnittstelle zur Darstellung eines aktiven Fensterbereichs und des Verfügbarkeitsstatus für eine bestimmte WindowAreaCapability.
- Deklarieren Sie diese Variablen in Ihrem
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
- Initialisieren Sie sie in der Methode
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()
}
}
}
- Implementieren Sie nun die
updateUI()
-Funktion, um je nach aktuellem Status die Rückkamera-Schaltfläche für Selfies zu aktivieren oder zu deaktivieren:
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
}
}
}
}
Dieser letzte Schritt ist optional, aber es ist sehr nützlich, alle möglichen Zustände eines WindowAreaCapability.
kennenzulernen.
- Implementieren Sie nun die Funktion
toggleRearDisplayMode
, die die Sitzung schließt, wenn die Funktion bereits aktiv ist, oder rufen Sie die FunktiontransferActivityToWindowArea
auf:
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
)
}
}
}
Beachten Sie die Verwendung von MainActivity
als WindowAreaSessionCallback
.
Die Rear Display API arbeitet mit einem Listener-Ansatz: Wenn Sie das Verschieben des Inhalts auf das andere Display anfordern, initiieren Sie eine Sitzung, die über die onSessionStarted()
-Methode des Listeners zurückgegeben wird. Wenn Sie stattdessen zum inneren (und größeren) Display zurückkehren möchten, schließen Sie die Sitzung und erhalten eine Bestätigung in der onSessionEnded()
-Methode. Um einen solchen Listener zu erstellen, müssen Sie die WindowAreaSessionCallback
-Schnittstelle implementieren.
- Ändere die
MainActivity
-Deklaration so, dass dieWindowAreaSessionCallback
-Schnittstelle implementiert wird:
step1/MainActivity.kt
class MainActivity : AppCompatActivity(), WindowAreaSessionCallback
Implementieren Sie nun die Methoden onSessionStarted
und onSessionEnded
im MainActivity
. Diese Callback-Methoden sind äußerst nützlich, um über den Sitzungsstatus informiert zu werden und die App entsprechend zu aktualisieren.
Dieses Mal sollten Sie jedoch der Einfachheit halber einfach im Funktionstext nachsehen, ob Fehler vorliegen, und den Status protokollieren.
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]")
}
- Erstellen Sie die Anwendung und führen Sie sie aus. Wenn Sie Ihr Gerät dann aufklappen und auf die Taste auf der Rückseite des Displays tippen, wird eine Meldung wie die folgende angezeigt:
- Wähle Bildschirm jetzt wechseln aus, um zu sehen, wie deine Inhalte auf das äußere Display verschoben werden.
6. Modus „Auf dem Tisch“ implementieren
Jetzt ist es an der Zeit, Ihre App faltbar zu machen: Sie platzieren Ihre Inhalte je nach Ausrichtung des Geräts an der Seite oder über dem Scharnier des Geräts. Dazu führst du den Code innerhalb der FoldingStateActor
aus, damit dein Code zur besseren Lesbarkeit von Activity
entkoppelt wird.
Der Hauptteil dieser API besteht aus der WindowInfoTracker
-Schnittstelle, die mit einer statischen Methode erstellt wird, die ein Activity
erfordert:
step1/CameraCodelabDependencies.kt
@Provides
fun provideWindowInfoTracker(activity: Activity) =
WindowInfoTracker.getOrCreate(activity)
Sie müssen diesen Code nicht schreiben, da er bereits vorhanden ist. Es ist aber hilfreich, zu verstehen, wie die WindowInfoTracker
aufgebaut ist.
- Überwachen Sie diese Änderungen in der
onResume()
-Methode vonActivity
, um auf Fensteränderungen zu warten:
step1/MainActivity.kt
lifecycleScope.launch {
foldingStateActor.checkFoldingState(
this@MainActivity,
binding.viewFinder
)
}
- Öffnen Sie nun die Datei
FoldingStateActor
, um diecheckFoldingState()
-Methode auszufüllen.
Wie Sie bereits gesehen haben, läuft es in der RESUMED
-Phase Ihrer Activity
und nutzt WindowInfoTracker
, um auf Layoutänderungen zu hören.
step1/FoldingStateActor.kt
windowInfoTracker.windowLayoutInfo(activity)
.collect { newLayoutInfo ->
activeWindowLayoutInfo = newLayoutInfo
updateLayoutByFoldingState(cameraViewfinder)
}
Über die WindowInfoTracker
-Oberfläche können Sie windowLayoutInfo()
aufrufen, um ein Flow
von WindowLayoutInfo
zu erfassen, das alle verfügbaren Informationen in DisplayFeature
enthält.
Der letzte Schritt besteht darin, auf diese Änderungen zu reagieren und die Inhalte entsprechend zu verschieben. Dies erfolgt Schritt für Schritt in der Methode updateLayoutByFoldingState()
.
- Achte darauf, dass das
activityLayoutInfo
einigeDisplayFeature
-Properties enthält und dass mindestens eines davon eineFoldingFeature
ist. Andernfalls möchtest du nichts unternehmen:
step1/FoldingStateActor.kt
val foldingFeature = activeWindowLayoutInfo?.displayFeatures
?.firstOrNull { it is FoldingFeature } as FoldingFeature?
?: return
- Berechnen Sie die Position des Fold, um sicherzustellen, dass sich die Geräteposition auf Ihr Layout auswirkt und nicht außerhalb der Grenzen Ihrer Hierarchie liegt:
step1/FoldingStateActor.kt
val foldPosition = FoldableUtils.getFeaturePositionInViewRect(
foldingFeature,
cameraViewfinder.parent as View
) ?: return
Jetzt sind Sie sicher, dass ein FoldingFeature
vorhanden ist, der sich auf Ihr Layout auswirkt. Daher müssen Sie Ihre Inhalte verschieben.
- Prüfe, ob
FoldingFeature
aufHALF_OPEN
gesetzt ist. Andernfalls stellst du einfach die Position deiner Inhalte wieder her. Wenn es sich umHALF_OPEN
handelt, müssen Sie eine weitere Prüfung durchführen und je nach Ausrichtung des Folds anders vorgehen:
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()
}
Wenn für den sichtbaren Bereich „VERTICAL
“ festgelegt ist, verschieben Sie den Inhalt nach rechts. Andernfalls verschieben Sie ihn an die obere Position.
- Erstelle deine App und führe sie aus. Klappe dann dein Gerät auf und versetze es in den Modus „Auf dem Tisch“, damit sich die Inhalte entsprechend bewegen.
7. Glückwunsch!
In diesem Codelab haben Sie einige Funktionen kennengelernt, die es nur bei faltbaren Geräten gibt, z. B. den Modus „Rückdisplay“ oder „Auf dem Tisch“. Außerdem haben Sie gelernt, wie Sie ihn mit dem Jetpack WindowManager entsperren.
Jetzt bist du bereit, deine Kamera-App nutzerfreundlicher zu gestalten.