Katlanabilir ekran modlarını destekleme

Katlanabilir cihazlar benzersiz izleme deneyimleri sunar. Arka ekran modu ve çift ekran modu, katlanabilir cihazlar için özel ekran özellikleri (ör. arka kamera selfie önizlemesi ve iç ile dış ekranda eş zamanlı ancak farklı görüntüler) oluşturmanıza olanak tanır.

Arka ekran modu

Katlanabilir bir cihaz açıldığında genellikle yalnızca iç ekran etkindir. Arka ekran modu, katlanabilir bir cihazın dış ekranına etkinlik taşımanıza olanak tanır. Bu ekran, cihaz katlanmamışken genellikle kullanıcıdan uzağa bakar. İç ekran otomatik olarak kapanır.

Yeni bir uygulama olarak, kamera önizlemesini dış ekranda görüntüleyebilirsiniz. Böylece kullanıcılar, genellikle ön kameradan çok daha iyi fotoğraf çekme performansı sunan arka kamerayla selfie çekebilir.

Kullanıcılar, arka ekran modunu etkinleştirmek için uygulamanın ekran değiştirmesine izin veren bir iletişim kutusuna yanıt verir. Örneğin:

Şekil 1. Arka ekran modunun başlatılmasına izin veren sistem iletişim kutusu.

Sistem, iletişim kutusunu oluşturur. Bu nedenle, sizin tarafınızda geliştirme yapılması gerekmez. Cihazın durumuna bağlı olarak farklı iletişim kutuları gösterilir. Örneğin, cihaz kapalıysa sistem kullanıcıları cihazı açmaya yönlendirir. İletişim kutusunu özelleştiremezsiniz ve farklı OEM'lerin cihazlarında değişiklik gösterebilir.

Pixel Fold kamera uygulamasıyla arka ekran modunu deneyebilirsiniz. Optimize your camera app on foldable devices with Jetpack WindowManager (Katlanabilir cihazlarda Jetpack WindowManager ile kamera uygulamanızı optimize etme) adlı codelab'de örnek bir uygulama görebilirsiniz.

Çift ekran modu

Çift ekran modu, katlanabilir cihazın her iki ekranında da aynı anda içerik göstermenize olanak tanır. Çift ekran modu, Android 14 (API düzeyi 34) veya sonraki sürümlerin yüklü olduğu Pixel Fold'da kullanılabilir.

Çift ekran çevirmen, örnek bir kullanım alanıdır.

Şekil 2. Ön ve arka ekranlarda farklı içerikler gösteren çift ekranlı çevirmen.

Modları programatik olarak etkinleştirme

Kitaplık sürümü 1.2.0-beta03'ten itibaren Jetpack WindowManager API'leri aracılığıyla arka ekran moduna ve çift ekran moduna erişebilirsiniz.

WindowManager bağımlılığını uygulamanızın modül build.gradle dosyasına ekleyin:

Groovy

dependencies {
    // TODO: Define window_version in your project's build configuration.
    implementation "androidx.window:window:$window_version"
}

Kotlin

dependencies {
    // Define window_version in your project's build configuration.
    implementation("androidx.window:window:$window_version")
}

Giriş noktası, pencereleri ekranlar arasında veya bir cihazdaki ekran alanları arasında taşımayla ilgili bilgileri ve davranışları sağlayan WindowAreaController'dir. WindowAreaController, kullanılabilir WindowAreaInfo nesnelerinin listesini sorgulamanıza olanak tanır.

WindowAreaSession'a erişmek için WindowAreaInfo kısayolunu kullanın. Bu arayüz, etkin bir pencere alanı özelliğini temsil eder. Belirli bir WindowAreaCapability stok durumunu belirlemek için WindowAreaSession kullanın.

Her özellik belirli bir WindowAreaCapability.Operation ile ilgilidir. Jetpack WindowManager, 1.2.0-beta03 sürümünde iki tür işlemi destekler:

Uygulamanızın ana etkinliğinde arka ekran modu ve çift ekran modu için değişkenleri nasıl beyan edeceğinize dair bir örneği aşağıda bulabilirsiniz:

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;

Etkinliğinizin onCreate() yöntemindeki değişkenleri nasıl başlatacağınız aşağıda açıklanmıştır:

WindowAreaControllerCallbackAdapter

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

Bir işlemi başlatmadan önce ilgili özelliğin kullanılabilirliğini kontrol edin:

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 available.
    }
    WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE -> {
      // The selected display mode is available and can 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 available.
}
else if (capabilityStatus.equals(WindowAreaCapability.Status.WINDOW_AREA_STATUS_AVAILABLE)) {
  // The selected display mode is available and can 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.
}

Çift ekran modu

Aşağıdaki örnek, özellik zaten etkinse oturumu kapatır, aksi takdirde presentContentOnWindowArea() işlevini çağırır:

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

Uygulamanın ana etkinliğinin WindowAreaPresentationSessionCallback bağımsız değişkeni olarak kullanıldığına dikkat edin.

API, bir dinleyici yaklaşımı kullanır: İçeriği katlanabilir cihazın diğer ekranında gösterme isteğinde bulunduğunuzda, dinleyicinin onSessionStarted() yöntemiyle döndürülen bir oturum başlatırsınız. Oturumu kapattığınızda onSessionEnded() yönteminde onay alırsınız.

Dinleyiciyi oluşturmak için WindowAreaPresentationSessionCallback arayüzünü uygulayın:

Kotlin

class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback

Java

public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback

İşleyicinin onSessionStarted(), onSessionEnded(), ve onContainerVisibilityChanged() yöntemlerini uygulaması gerekir. Geri çağırma yöntemleri, oturum durumu hakkında sizi bilgilendirir ve uygulamayı buna göre güncellemenizi sağlar.

onSessionStarted() geri çağırması, bağımsız değişken olarak WindowAreaSessionPresenter alır. Bağımsız değişken, pencere alanına erişmenize ve içerik göstermenize olanak tanıyan kapsayıcıdır. Kullanıcı birincil uygulama penceresinden çıktığında sunu sistem tarafından otomatik olarak kapatılabilir veya WindowAreaSessionPresenter#close() çağrılarak kapatılabilir.

Diğer geri çağırmalar için basitlik adına yalnızca işlev gövdesinde herhangi bir hata olup olmadığını kontrol edin ve durumu günlüğe kaydedin:

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

Ekosistemde tutarlılığı korumak için kullanıcılara çift ekran modunu nasıl etkinleştireceklerini veya devre dışı bırakacaklarını göstermek üzere Dual Screen resmi simgesini kullanın.

Çalışan bir örnek için DualScreenActivity.kt dosyasına bakın.

Arka ekran modu

Çift ekran modu örneğine benzer şekilde, aşağıdaki toggleRearDisplayMode() işlevi, özellik zaten etkinse oturumu kapatır, aksi takdirde transferActivityToWindowArea() işlevini çağırır:

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 toggleRearDisplayMode() {
    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);
    }
}

Bu durumda, gösterilen etkinlik WindowAreaSessionCallback olarak kullanılır. Geri çağırma, bir pencere alanında içerik gösterilmesine izin veren bir sunucu almadığı, bunun yerine etkinliğin tamamını başka bir alana aktardığı için bu daha kolay uygulanır:

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

Ekosistemde tutarlılığı korumak için kullanıcılara arka ekran modunu nasıl etkinleştireceklerini veya devre dışı bırakacaklarını göstermek üzere Arka Kamera resmi simgesini kullanın.

Ek kaynaklar