Kamera aufklappen

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.

6caebc2739522a1b.png

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

  1. 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
  1. 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.

Screenshot von Android Studio mit dem Code für dieses Codelab

Wenn Sie aufgefordert werden, die neueste Gradle-Version zu verwenden, aktualisieren Sie sie.

3. Ausführen und beobachten

  1. 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.

a34aca632d75aa09.png

  1. 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:

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.

  1. 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
  1. 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()
      }
  }
}
  1. 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.

  1. Implementieren Sie nun die Funktion toggleRearDisplayMode, die die Sitzung schließt, wenn die Funktion bereits aktiv ist, oder rufen Sie die Funktion transferActivityToWindowArea 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.

  1. Ändere die MainActivity-Deklaration so, dass die WindowAreaSessionCallback-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]")
}
  1. 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:

3fa50cce0b0d4b8d.png

  1. 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.

  1. Überwachen Sie diese Änderungen in der onResume()-Methode von Activity, um auf Fensteränderungen zu warten:

step1/MainActivity.kt

lifecycleScope.launch {
    foldingStateActor.checkFoldingState(
         this@MainActivity, 
         binding.viewFinder
    )
}
  1. Öffnen Sie nun die Datei FoldingStateActor, um die checkFoldingState()-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().

  1. Achte darauf, dass das activityLayoutInfo einige DisplayFeature-Properties enthält und dass mindestens eines davon eine FoldingFeature ist. Andernfalls möchtest du nichts unternehmen:

step1/FoldingStateActor.kt

val foldingFeature = activeWindowLayoutInfo?.displayFeatures
            ?.firstOrNull { it is FoldingFeature } as FoldingFeature?
            ?: return
  1. 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.

  1. Prüfe, ob FoldingFeature auf HALF_OPEN gesetzt ist. Andernfalls stellst du einfach die Position deiner Inhalte wieder her. Wenn es sich um HALF_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.

  1. 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.

Weitere Informationen

Referenz