Modi für faltbare Displays unterstützen

Faltbare Geräte bieten einzigartige Lesefunktionen. Mit dem rückwärtigen Displaymodus und dem Dual Screen-Modus können Sie spezielle Displayfunktionen für faltbare Geräte entwickeln, z. B. eine Vorschau für Selfies mit der Rückkamera und gleichzeitige, aber verschiedene Displays auf dem inneren und äußeren Display.

Modus für hinteres Display

Wenn ein faltbares Gerät aufgeklappt ist, ist normalerweise nur das innere Display aktiv. Mit dem Rückdisplaymodus kannst du eine Aktivität auf den äußeren Bildschirm eines faltbaren Geräts verschieben, das normalerweise vom Nutzer weg zeigt, wenn das Gerät aufgeklappt ist. Das innere Display schaltet sich automatisch aus.

Eine neue Anwendung ist es, die Kameravorschau auf dem äußeren Bildschirm anzuzeigen, damit Nutzer Selfies mit der Rückkamera aufnehmen können, die in der Regel eine viel bessere Bildqualität bietet.

Beim Aktivieren des rückwärtigen Displays reagieren Nutzer auf ein Dialogfeld, in dem sie aufgefordert werden, den Bildschirm zu wechseln, z. B.:

Abbildung 1: Systemdialogfeld, das das Starten des Rückdisplaymodus erlaubt.

Der Dialog wird erstellt, es ist also keine Entwicklung von Ihrer Seite erforderlich. Je nach Gerätestatus werden unterschiedliche Dialogfelder angezeigt. Beispielsweise weist das System die Nutzer an, das Gerät aufzuklappen, wenn es geschlossen ist. Sie können das Dialogfeld nicht anpassen, können es aber je nach Gerät verschiedener OEMs variieren.

Sie können den Modus für das rückseitige Display mit der Kamera App von Pixel Fold ausprobieren. Eine Beispielimplementierung finden Sie im Codelab Kamera öffnen.

Dual Screen-Modus

Mit dem Dual Screen-Modus kannst du Inhalte auf beiden Displays eines faltbaren Smartphones gleichzeitig anzeigen lassen. Der Dual Screen-Modus ist auf Pixel Fold mit Android 14 (API-Level 34) oder höher verfügbar.

Ein Beispiel für einen Anwendungsfall ist der Dual Screen-Dolmetscher.

Abbildung 2. Dual Screen-Dolmetscher mit unterschiedlichen Inhalten auf dem Front- und Rückdisplay

Modi programmatisch aktivieren

Über die Jetpack WindowManager-APIs können Sie ab Bibliotheksversion 1.2.0-beta03 auf den rückwärtigen Display- und den Dual-Screen-Modus zugreifen.

Fügen Sie der Moduldatei build.gradle Ihrer App die WindowManager-Abhängigkeit hinzu:

Groovig

dependencies {
    implementation "androidx.window:window:1.2.0-beta03"
}

Kotlin

dependencies {
    implementation("androidx.window:window:1.2.0-beta03")
}

Der Einstiegspunkt ist der WindowAreaController, der Informationen und das Verhalten beim Verschieben von Fenstern zwischen Bildschirmen oder zwischen Anzeigebereichen auf einem Gerät bereitstellt. Mit WindowAreaController können Sie die Liste der verfügbaren WindowAreaInfo-Objekte abfragen.

Verwenden Sie WindowAreaInfo, um auf WindowAreaSession zuzugreifen, eine Oberfläche, die ein aktives Fensterbereichfeature darstellt. Verwenden Sie WindowAreaSession, um die Verfügbarkeit einer bestimmten WindowAreaCapability zu ermitteln.

Jede Funktion ist mit einem bestimmten WindowAreaCapability.Operation verbunden. In Version 1.2.0-beta03 unterstützt Jetpack WindowManager zwei Arten von Vorgängen:

Hier ein Beispiel, wie Sie Variablen für den rückwärtigen Displaymodus und den Dual Screen-Modus in der Hauptaktivität Ihrer App deklarieren:

Kotlin

private lateinit var windowAreaController: WindowAreaController
private lateinit var displayExecutor: Executor
private var windowAreaSession: WindowAreaSession? = null
private var windowAreaInfo: WindowAreaInfo? = null
private var capabilityStatus: WindowAreaCapability.Status =
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED

private val dualScreenOperation = WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA
private val rearDisplayOperation = WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA

Java

private WindowAreaControllerCallbackAdapter windowAreaController = null;
private Executor displayExecutor = null;
private WindowAreaSessionPresenter windowAreaSession = null;
private WindowAreaInfo windowAreaInfo = null;
private WindowAreaCapability.Status capabilityStatus  =
        WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED;

private WindowAreaCapability.Operation dualScreenOperation =
        WindowAreaCapability.Operation.OPERATION_PRESENT_ON_AREA;
private WindowAreaCapability.Operation rearDisplayOperation =
        WindowAreaCapability.Operation.OPERATION_TRANSFER_ACTIVITY_TO_AREA;

So initialisieren Sie die Variablen in der onCreate()-Methode Ihrer Aktivität:

Kotlin

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 -> windowAreaInfo = info }
            .map { it?.getCapability(operation)?.status ?: WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED }
            .distinctUntilChanged()
            .collect {
                capabilityStatus = it
            }
    }
}

Java

displayExecutor = ContextCompat.getMainExecutor(this);
windowAreaController = new WindowAreaControllerCallbackAdapter(WindowAreaController.getOrCreate());
windowAreaController.addWindowAreaInfoListListener(displayExecutor, this);

windowAreaController.addWindowAreaInfoListListener(displayExecutor,
  windowAreaInfos -> {
    for(WindowAreaInfo newInfo : windowAreaInfos){
        if(newInfo.getType().equals(WindowAreaInfo.Type.TYPE_REAR_FACING)){
            windowAreaInfo = newInfo;
            capabilityStatus = newInfo.getCapability(presentOperation).getStatus();
            break;
        }
    }
});

Bevor Sie einen Vorgang starten, prüfen Sie die Verfügbarkeit der jeweiligen Funktion:

Kotlin

when (capabilityStatus) {
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED -> {
      // The selected display mode is not supported on this device.
    }
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE -> {
      // The selected display mode is not currently available to be enabled.
    }
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
      // The selected display mode is currently available to be enabled.
    }
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE -> {
      // The selected display mode is already active.
    }
    else -> {
      // The selected display mode status is unknown.            
    }
}

Java

if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNSUPPORTED)) {
  // The selected display mode is not supported on this device.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_UNAVAILABLE)) {
  // The selected display mode is not currently available to be enabled.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE)) {
  // The selected display mode is currently available to be enabled.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE)) {
  // The selected display mode is already active.
}
else {
  // The selected display mode status is unknown.    
}

Dual Screen-Modus

Im folgenden Beispiel wird die Sitzung geschlossen, wenn die Funktion bereits aktiv ist, oder anderweitig die Funktion presentContentOnWindowArea() aufgerufen:

Kotlin

fun toggleDualScreenMode() {
    if (windowAreaSession != null) {
        windowAreaSession?.close()
    }
    else {
        windowAreaInfo?.token?.let { token ->
            windowAreaController.presentContentOnWindowArea(
                token = token,
                activity = this,
                executor = displayExecutor,
                windowAreaPresentationSessionCallback = this
            )
        }
    }
}

Java

private void toggleDualScreenMode() {
    if(windowAreaSession != null) {
        windowAreaSession.close();
    }
    else {
        Binder token = windowAreaInfo.getToken();
        windowAreaController.presentContentOnWindowArea( token, this, displayExecutor, this);
    }
}

Beachten Sie die Verwendung der Hauptaktivität der App als WindowAreaPresentationSessionCallback.

Die API verwendet einen Listener-Ansatz: Wenn Sie eine Anfrage stellen, um den Inhalt dem anderen Display eines faltbaren Smartphones zu präsentieren, initiieren Sie eine Sitzung, die über die onSessionStarted()-Methode des Listeners zurückgegeben wird. Wenn Sie die Sitzung schließen, erhalten Sie eine Bestätigung in der Methode onSessionEnded().

Implementieren Sie die WindowAreaPresentationSessionCallback-Schnittstelle, um den Listener zu erstellen:

Kotlin

class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback

Java

public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback

Der Listener muss die Methoden onSessionStarted(), onSessionEnded(), und onContainerVisibilityChanged() implementieren. Mit den Callback-Methoden werden Sie über den Sitzungsstatus informiert und können die App entsprechend aktualisieren.

Der onSessionStarted()-Callback empfängt ein WindowAreaSessionPresenter als Argument. Das Argument ist der Container, mit dem Sie auf einen Fensterbereich zugreifen und Inhalte anzeigen können. Die Präsentation kann automatisch vom System geschlossen werden, wenn der Nutzer das Hauptanwendungsfenster verlässt, oder sie kann durch Aufrufen von WindowAreaSessionPresenter#close() geschlossen werden.

Bei den anderen Callbacks können Sie der Einfachheit halber einfach im Funktionstext nach Fehlern suchen und den Status protokollieren:

Kotlin

override fun onSessionStarted(session: WindowAreaSessionPresenter) {
    windowAreaSession = session
    val view = TextView(session.context)
    view.text = "Hello world!"
    session.setContentView(view)
}

override fun onSessionEnded(t: Throwable?) {
    if(t != null) {
        Log.e(logTag, "Something was broken: ${t.message}")
    }
}

override fun onContainerVisibilityChanged(isVisible: Boolean) {
    Log.d(logTag, "onContainerVisibilityChanged. isVisible = $isVisible")
}

Java

@Override
public void onSessionStarted(@NonNull WindowAreaSessionPresenter session) {
    windowAreaSession = session;
    TextView view = new TextView(session.getContext());
    view.setText("Hello world, from the other screen!");
    session.setContentView(view);
}

@Override public void onSessionEnded(@Nullable Throwable t) {
    if(t != null) {
        Log.e(logTag, "Something was broken: ${t.message}");
    }
}

@Override public void onContainerVisibilityChanged(boolean isVisible) {
    Log.d(logTag, "onContainerVisibilityChanged. isVisible = " + isVisible);
}

Verwenden Sie das offizielle Dual Screen-Symbol, um Nutzern anzuzeigen, wie der Dual Screen-Modus aktiviert oder deaktiviert wird, um die Einheitlichkeit in der gesamten Umgebung zu wahren.

Ein funktionierendes Beispiel ist DualScreenActivity.kt.

Modus für hinteres Display

Ähnlich wie das Beispiel für den Dual Screen-Modus schließt das folgende Beispiel einer toggleRearDisplayMode()-Funktion die Sitzung, wenn die Funktion bereits aktiv ist, oder ruft anderweitig die Funktion transferActivityToWindowArea() auf:

Kotlin

fun toggleRearDisplayMode() {
    if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
        if(windowAreaSession == null) {
            windowAreaSession = windowAreaInfo?.getActiveSession(
                operation
            )
        }
        windowAreaSession?.close()
    } else {
        windowAreaInfo?.token?.let { token ->
            windowAreaController.transferActivityToWindowArea(
                token = token,
                activity = this,
                executor = displayExecutor,
                windowAreaSessionCallback = this
            )
        }
    }
}

Java

void toggleDualScreenMode() {
    if(capabilityStatus == WindowAreaCapability.Status.WINDOW_AREA_STATUS_ACTIVE) {
        if(windowAreaSession == null) {
            windowAreaSession = windowAreaInfo.getActiveSession(
                operation
            )
        }
        windowAreaSession.close()
    }
    else {
        Binder token = windowAreaInfo.getToken();
        windowAreaController.transferActivityToWindowArea(token, this, displayExecutor, this);
    }
}

In diesem Fall wird die angezeigte Aktivität als WindowAreaSessionCallback, verwendet, was einfacher zu implementieren ist, da der Callback keinen Presenter empfängt, der das Anzeigen von Inhalten in einem Fensterbereich zulässt, sondern stattdessen die gesamte Aktivität in einen anderen Bereich überträgt:

Kotlin

override fun onSessionStarted() {
    Log.d(logTag, "onSessionStarted")
}

override fun onSessionEnded(t: Throwable?) {
    if(t != null) {
        Log.e(logTag, "Something was broken: ${t.message}")
    }
}

Java

@Override public void onSessionStarted(){
    Log.d(logTag, "onSessionStarted");
}

@Override public void onSessionEnded(@Nullable Throwable t) {
    if(t != null) {
        Log.e(logTag, "Something was broken: ${t.message}");
    }
}

Verwende das offizielle Symbol für die Rückkamera, um Nutzern zu zeigen, wie sie den Modus für das rückseitige Display aktivieren oder deaktivieren können.

Weitere Informationen