Modi für faltbare Displays unterstützen

Faltbare Geräte bieten ein einzigartiges Videoerlebnis. Mit dem Rückdisplay- und Dual-Screen-Modus können Sie spezielle Displayfunktionen für faltbare Geräte entwickeln, z. B. eine Rückkamera-Selfie-Vorschau und gleichzeitig verschiedene Displays auf dem inneren und äußeren Display.

Rückdisplaymodus

Wenn ein faltbares Gerät aufgeklappt wird, ist normalerweise nur das innere Display aktiv. Mit dem rückseitigen Displaymodus können Sie eine Aktivität auf den äußeren Bildschirm eines faltbaren Geräts verschieben. die dem Nutzer beim Aufklappen des Geräts normalerweise weg zeigt. Das innere Display schaltet sich automatisch aus.

Eine neuartige App ist die Anzeige der Kameravorschau auf dem äußeren Bildschirm, So können Nutzer mit der Rückkamera Selfies aufnehmen, was normalerweise eine deutlich bessere Leistung bei der Aufnahme von Fotos bietet.

Zum Aktivieren des rückseitigen Displaymodus reagieren Nutzer auf ein Dialogfeld, in dem sie der App erlauben, den Bildschirm zu wechseln. Beispiele:

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> Abbildung 1: Systemdialogfeld zum Starten des rückwärtigen Displaymodus.

Der Dialog wird vom System erstellt, sodass von Ihrer Seite keine Entwicklung erforderlich ist. Je nach Gerätestatus werden unterschiedliche Dialogfelder angezeigt. zum Beispiel weist das System den Nutzer an, das Gerät aufzuklappen, wenn es zugeklappt ist. Das Dialogfeld lässt sich nicht anpassen, kann aber auf Geräten verschiedener OEMs variieren.

Sie können den Rückdisplaymodus mit der Kamera App von Pixel Fold ausprobieren. Eine Beispielimplementierung findest du im Codelab Unfold your camera.

Dual Screen-Modus

Mit dem Dual Screen-Modus können Sie Inhalte auf beiden Displays eines faltbaren Geräts gleichzeitig anzeigen. Der Dual Screen-Modus ist auf Google Pixel Fold mit Android 14 (API-Level 34) oder höher verfügbar.

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

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
</ph> Abbildung 2: Dual Screen-Dolmetscher, der verschiedene Inhalte auf dem Front- und Rückdisplay anzeigt.

Modi programmatisch aktivieren

Sie können den Rückdisplay- und Dual Screen-Modus über die Jetpack WindowManager-APIs ab Bibliotheksversion 1.2.0-beta03 aufrufen.

Füge der Modul-build.gradle-Datei deiner App die WindowManager-Abhängigkeit hinzu:

Cool

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

Kotlin

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

Der Einstiegspunkt ist der WindowAreaController, die Informationen und Verhaltensweisen in Bezug auf das Verschieben von Fenstern zwischen Displays oder zwischen Anzeigebereichen auf einem Gerät enthält. Mit WindowAreaController können Sie die Liste der verfügbaren WindowAreaInfo Objekte.

Verwenden Sie WindowAreaInfo, um auf WindowAreaSession zuzugreifen. eine Schnittstelle, die ein aktives Fensterbereich-Feature darstellt. Mit WindowAreaSession können Sie die Verfügbarkeit eines bestimmten WindowAreaCapability

Jede Funktion bezieht sich auf einen bestimmten WindowAreaCapability.Operation. In Version 1.2.0-beta03 unterstützt Jetpack WindowManager zwei Arten von Vorgängen:

Hier ein Beispiel für die Deklaration von Variablen für den Modus „Rückseite“ und „Dual Screen“ in der Hauptaktivität Ihrer App:

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 Capability bereits aktiv ist. oder auf andere Weise die Funktion presentContentOnWindowArea() aufrufen:

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, dass die Hauptaktivität der App als WindowAreaPresentationSessionCallback verwendet wird.

Die API verwendet einen Listener-Ansatz: Wenn Sie eine Anfrage zur Darstellung des Inhalts stellen anderen Display eines faltbaren Geräts an, dann initiieren Sie eine Sitzung, mithilfe der Methode onSessionStarted() des Listeners. Wenn Sie die Sitzung schließen, wird in der onSessionEnded()-Methode eine Bestätigung angezeigt.

Implementieren Sie zum Erstellen des Listeners die Schnittstelle WindowAreaPresentationSessionCallback:

Kotlin

class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback

Java

public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback

Der Listener muss onSessionStarted() (onSessionEnded(),) implementieren. und onContainerVisibilityChanged()-Methoden. Die Callback-Methoden informieren Sie über den Sitzungsstatus und ermöglichen es Ihnen, die App entsprechend zu aktualisieren.

Der onSessionStarted()-Callback empfängt 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 primäre Anwendungsfenster verlässt, oder sie lässt sich durch Aufrufen von WindowAreaSessionPresenter#close() schließen.

Überprüfen Sie bei den anderen Callbacks einfach den Funktionstext auf Fehler und protokollieren Sie den Status:

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 Nutzer darüber zu informieren, wie sie den Dual Screen-Modus aktivieren oder deaktivieren können.

Ein funktionierendes Beispiel finden Sie unter DualScreenActivity.kt.

Rückdisplaymodus

Ähnlich wie beim Dual Screen-Modus ist das folgende Beispiel eines toggleRearDisplayMode() -Funktion die Sitzung schließt, wenn die Capability bereits aktiv ist, oder auf andere Weise die Funktion transferActivityToWindowArea() aufrufen:

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 Vortragenden empfängt. , mit dem Inhalte in einem Fensterbereich angezeigt werden können, aber die gesamte Aktivität stattdessen auf einen anderen Bereich übertragen wird:

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}");
    }
}

Um eine einheitliche Funktionsweise zu gewährleisten, kannst du das offizielle Rückkamera-Symbol verwenden, um Nutzer darauf hinzuweisen, wie sie den Rückdisplaymodus aktivieren oder deaktivieren können.

Weitere Informationen