Katlanabilir ekran modlarını destekleme

Katlanabilir cihazlar benzersiz bir izleme deneyimi sunar. Arka ekran modu ve çift ekran modu, katlanabilir cihazlarda arka kamera selfie önizlemesi gibi özel ekran özellikleri ve iç ve dış ekranlarda aynı anda farklı ekranlar oluşturmanızı sağlar.

Arka ekran modu

Genellikle katlanabilir cihazlar açık olduğunda yalnızca iç ekranı etkin olur. Arka ekran modu, etkinlikleri katlanabilir cihazların dış ekranına taşımanızı sağlar. Cihaz henüz katlanmamış durumdayken kullanıcının gözü arkaya bakmalıdır. İç ekran otomatik olarak kapanır.

Yeni bir uygulama, kamera önizlemesini dış ekranda görüntülemektir. Böylece kullanıcılar arka kamerayla selfie çekebilir. Bu da genellikle çok daha iyi resim çekme performansı sağlar.

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

Şekil 1. Arka görüntü modunun başlatılmasına izin veren sistem iletişim kutusu.

Diyalog sistem tarafından oluşturulur, böylece sizin herhangi bir geliştirme yapmanız gerekmez. Cihaz durumuna bağlı olarak farklı iletişim kutuları görünür. Örneğin sistem, cihaz kapalıyken kullanıcıları cihazı açmaları için yönlendirir. İletişim kutusunu özelleştiremezsiniz ancak farklı OEM'lere ait cihazlara göre değişebilir.

Arka ekran modunu Pixel Fold kamera uygulamasıyla deneyebilirsiniz. Kamera deneyiminizi geliştirme codelab'indeki örnek uygulamaya göz atın.

Dual Screen modu

Çift ekran modu, aynı anda iki katlanabilir cihaz ekranında içerik göstermenize olanak tanır. Çift ekran modu, Android 14 (API düzeyi 34) veya sonraki sürümleri çalıştıran Pixel Fold'da kullanılabilir.

Kullanım örneklerinden biri çift ekran çevirmenidir.

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

Modları programatik olarak etkinleştirme

Arka görüntü modu ve çift ekran moduna, kitaplık sürümünden itibaren 1.2.0-beta03 sürümünden itibaren Jetpack WindowManager API'leri aracılığıyla erişebilirsiniz.

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

Modern

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

Kotlin

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

Giriş noktası, pencerelerin ekranlar veya cihazdaki ekran alanları arasında taşınmasıyla ilgili bilgileri ve davranışları sağlayan WindowAreaController adresidir. WindowAreaController, kullanılabilir WindowAreaInfo nesneleri listesini sorgulamanıza olanak tanır.

Etkin pencere alanı özelliğini temsil eden bir arayüz olan WindowAreaSession arayüzüne erişmek için WindowAreaInfo kısayolunu kullanın. Belirli bir WindowAreaCapability'in kullanılabilirliğini belirlemek için WindowAreaSession öğesini kullanın.

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

Uygulamanızın ana etkinliğinde arka görüntü modu ve çift ekran modu için değişkenlerin nasıl tanımlanacağına dair bir örnek aşağıda verilmiştir:

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önteminde değişkenleri nasıl başlatacağınız aşağıda açıklanmıştır:

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şleme başlamadan önce belirli bir ö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 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 modu

Aşağıdaki örnek, özellik zaten etkinse veya presentContentOnWindowArea() işlevini çağırıyorsa oturumu kapatı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 olarak kullanıldığına dikkat edin.

API, işleyici yaklaşımını kullanır: İçeriği katlanabilir cihazın diğer ekranına sunmak için istekte bulunduğunuzda dinleyicinin onSessionStarted() yöntemi aracılığıyla döndürülen bir oturum başlatırsınız. Oturumu kapattığınızda onSessionEnded() yönteminden onay alırsınız.

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

Kotlin

class MainActivity : AppCompatActivity(), windowAreaPresentationSessionCallback

Java

public class MainActivity extends AppCompatActivity implements WindowAreaPresentationSessionCallback

Dinleyicinin onSessionStarted(), onSessionEnded(), ve onContainerVisibilityChanged() yöntemlerini uygulaması gerekir. Geri çağırma yöntemleri, sizi oturum durumu hakkında bilgilendirir ve uygulamayı buna göre güncellemenize olanak tanır.

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

Diğer geri çağırma işlemleri için kolaylık olması açısından işlev gövdesinde 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);
}

Ekosistem genelinde tutarlılığı sağlamak için kullanıcılara Dual Screen modunu nasıl etkinleştireceklerini veya devre dışı bırakacaklarını belirtmek üzere Çift Ekran 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 örneği, özellik zaten etkinse veya başka bir şekilde transferActivityToWindowArea() işlevini çağırıyorsa oturumu kapatı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 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);
    }
}

Bu durumda, görüntülenen etkinlik bir WindowAreaSessionCallback, olarak kullanılır. Bu işlevin uygulanması daha basittir. Bunun nedeni, geri çağırmanın içeriğin bir pencere alanında gösterilmesine izin veren bir sunucuyu almaması, bunun yerine tüm etkinliği başka bir alana aktarmasıdı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}");
    }
}

Ekosistem genelinde tutarlılığı sağlamak için kullanıcılara arka ekran modunu nasıl etkinleştireceklerini veya devre dışı bırakacaklarını belirtmek üzere Resmi Arka Kamera simgesini kullanın.

Ek kaynaklar