Obsługa trybów składanego wyświetlacza

Składane urządzenia zapewniają wyjątkowe wrażenia podczas oglądania. Tryb tylnego wyświetlacza i tryb podwójnego ekranu umożliwiają tworzenie specjalnych funkcji wyświetlacza na urządzeniach składanych, takich jak podgląd selfie z tylnego aparatu oraz wyświetlanie kilku różnych wyświetlaczy na ekranach wewnętrznym i zewnętrznym.

Tryb tylnego wyświetlacza

Zazwyczaj, gdy urządzenie składane jest rozłożone, aktywny jest tylko ekran wewnętrzny. Tryb tylnego wyświetlacza pozwala przenieść aktywność na ekran zewnętrzny urządzenia składanego. który zazwyczaj jest skierowany w stronę użytkownika, gdy urządzenie jest rozkładane. Wewnętrzny ekran wyłączy się automatycznie.

Nowatorska aplikacja pozwala wyświetlić podgląd z aparatu na ekranie zewnętrznym. dzięki czemu użytkownicy mogą robić selfie tylnym aparatem, który zwykle pozwala robić zdjęcia znacznie lepiej.

Aby włączyć tryb tylnego wyświetlacza, użytkownik musi odpowiedzieć w oknie, w którym zezwala aplikacji na przełączanie ekranu, na przykład:

Rysunek 1. Okno systemowe pozwalające na włączenie trybu tylnego wyświetlacza.

Okno tworzy system, więc nie musisz niczego programować. W zależności od stanu urządzenia wyświetlane są różne okna. np. system każe użytkownikom otwierać je, jeśli jest zamknięte. Nie możesz dostosować tego okna, ale może się ono różnić w zależności od urządzenia od różnych dostawców OEM.

Tryb tylnego wyświetlacza możesz wypróbować w aplikacji aparatu Pixel Fold. Zapoznaj się z przykładową implementacją w ramach ćwiczenia z programowania (w języku angielskim) na temat rozwijania działania aparatu.

Tryb Dual Screen

Tryb 2 ekranów umożliwia wyświetlanie treści na obu wyświetlaczach urządzenia składanego jednocześnie. Tryb 2 ekranu jest dostępny na Pixelu Fold z Androidem 14 (poziom interfejsu API 34) lub nowszym.

Przykładem może być korzystanie z funkcji tłumaczenia rozmowy na dwóch ekranach.

Rysunek 2. Tłumacz na 2 ekrany pokazujący różne treści na przednim i tylnym wyświetlaczu.

Automatyczne włączanie trybów

Dostęp do trybu tylnego wyświetlacza i trybu podwójnego ekranu możesz uzyskać za pomocą interfejsów API Jetpack WindowManager, począwszy od biblioteki w wersji 1.2.0-beta03.

Dodaj zależność WindowManager do pliku build.gradle modułu aplikacji:

Odlotowe

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

Kotlin

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

Punkt wejścia to WindowAreaController, który udostępnia informacje i zachowania związane z przenoszeniem okien między wyświetlaczami lub obszarami wyświetlania na urządzeniu. WindowAreaController umożliwia wysyłanie zapytań o listę dostępnych elementów WindowAreaInfo. obiektów.

Użyj adresu WindowAreaInfo, aby uzyskać dostęp do: WindowAreaSession, interfejsu reprezentującego funkcję obszaru aktywnego okna. Użyj właściwości WindowAreaSession, aby określić dostępność WindowAreaCapability

Każda możliwość jest powiązana z konkretnym elementem WindowAreaCapability.Operation. W wersjach 1.2.0-beta03 Jetpack WindowManager obsługuje dwa rodzaje operacji:

Oto przykład deklaracji zmiennych dla trybu tylnego wyświetlacza i trybu Dual Screen w głównej aktywności aplikacji:

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;

Oto jak zainicjować zmienne w metodzie onCreate() w Twojej aktywności:

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

Zanim rozpoczniesz operację, sprawdź dostępność konkretnej funkcji:

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

Tryb Dual Screen

Poniższy przykład zamyka sesję, jeśli funkcja jest już aktywna. lub w inny sposób wywołuje funkcję presentContentOnWindowArea():

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

Zwróć uwagę na to, że WindowAreaPresentationSessionCallback jest wykorzystywane w głównej aktywności w aplikacji.

Interfejs API wykorzystuje podejście detektora: gdy przesyłasz żądanie prezentacji treści na ekran urządzenia składanego, inicjujesz sesję, która jest zwracana. za pomocą metody onSessionStarted() detektora. Gdy zamkniesz sesję, w metodzie onSessionEnded() pojawi się potwierdzenie.

Aby utworzyć odbiornik, zaimplementuj interfejs WindowAreaPresentationSessionCallback:

Kotlin

class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback

Java

public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback

Detektor musi zaimplementować onSessionStarted(), onSessionEnded(), i onContainerVisibilityChanged(). Metody wywołania zwrotnego powiadamiają o stanie sesji i umożliwiają odpowiednie zaktualizowanie aplikacji.

Wywołanie zwrotne onSessionStarted() otrzymuje WindowAreaSessionPresenter jako argument. Ten argument to kontener umożliwiający dostęp do obszaru okna i pokazywanie jego zawartości. Prezentacja może zostać automatycznie zamknięta przez system, gdy użytkownik opuści główne okno aplikacji. Prezentację można też zamknąć, wywołując metodę WindowAreaSessionPresenter#close().

W przypadku pozostałych wywołań zwrotnych dla uproszczenia wystarczy sprawdzić, czy w treści funkcji nie występują błędy, i zapisać stan:

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

Aby zachować spójność w całym ekosystemie, użyj oficjalnej ikony Dual Screen, aby wskazać użytkownikom, jak włączyć lub wyłączyć tryb Dual Screen.

Praktyczny przykład znajdziesz na stronie DualScreenActivity.kt.

Tryb tylnego wyświetlacza

Podobnie jak w przypadku trybu Dual Screen w trybie Dual Screen, ten przykład przedstawia toggleRearDisplayMode(). zamyka sesję, jeśli funkcja jest już aktywna, lub w inny sposób wywołuje funkcję transferActivityToWindowArea():

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

W takim przypadku wyświetlana aktywność jest używana jako WindowAreaSessionCallback, Jest to prostsze do wdrożenia, ponieważ wywołanie zwrotne nie otrzymuje osoby prowadzącej który pozwala na wyświetlanie zawartości w obszarze okna, ale przenosi całą aktywność do innego obszaru:

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

Aby zachować spójność w całym ekosystemie, używaj oficjalnej ikony tylnego aparatu, aby wskazać użytkownikom, jak włączyć lub wyłączyć tryb tylnego wyświetlacza.

Dodatkowe materiały

. .