Katlanabilir ekran modlarını destekleme

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

Arka ekran modu

Genellikle katlanabilir cihaz açıldığında yalnızca iç ekran etkindir. Arka ekran modu, etkinlikleri katlanabilir cihazların dış ekranına taşımanıza olanak tanır. Bu ekranlar, genellikle cihaz katlanmış durumdayken kullanıcıdan uzaktaki ekrana bakacaktır. Dahili ekran otomatik olarak kapanır.

Kamera önizlemesini dış ekranda göstermek de yeni bir uygulamadır. Böylece kullanıcılar, genellikle ön kameradan çok daha iyi fotoğraflar çeken arka kamerayla selfie çekebilir.

Arka ekran modunu etkinleştirmek için kullanıcılar, uygulamanın ekranları değiştirmesine izin vermek üzere bir iletişim kutusuna yanıt verir. Örneğin:

Şekil 1. Arka ekran modunu başlatmaya izin veren sistem iletişim kutusu.

İletişim, sistem tarafından oluşturulur. Sizin herhangi bir geliştirme yapmanız gerekmez. Cihaz durumuna bağlı olarak farklı iletişim kutuları gösterilir. Örneğin, sistem kapalıysa kullanıcıları cihazı açmaya yönlendirir. İletişim kutusunu özelleştiremezsiniz ve bu iletişim kutusu farklı OEM'lerin cihazlarında değişiklik gösterebilir.

Pixel Fold kamera uygulamasıyla arka ekran modunu deneyebilirsiniz. Jetpack WindowManager ile katlanabilir cihazlarda kamera uygulamanızı optimize etme codelab'inde örnek uygulamaya göz atın.

Çift ekran modu

Çift ekran modu, katlanabilir cihazların her iki ekranında aynı anda 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.

Dual Screen Çevirmen, bu kullanım alanı için örnek olarak verilebilir.

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

Modları programatik olarak etkinleştirme

1.2.0-beta03 kitaplık sürümünden 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:

Eski

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

Kotlin

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

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

Etkin bir pencere alanı özelliğini temsil eden WindowAreaSession arayüzüne erişmek için WindowAreaInfo öğesini kullanın. Belirli bir WindowAreaCapability öğesinin kullanılabilirliğini belirlemek için WindowAreaSession 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 ekran modu ve çift ekran modu için değişkenleri nasıl beyan edeceğinize 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öntemindeki değişkenleri başlatmak için aşağıdakileri yapın:

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 ilgili özelliğin kullanılabilir olup olmadığını 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 örnekte, özellik zaten etkinse oturum kapatılır veya aksi takdirde presentContentOnWindowArea() işlevi çağrılı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);
    }
}

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

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

Dinleyiciyi oluşturmak için WindowAreaPresentationSessionCallback arabirimini 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 durumunu size bildirir ve uygulamayı uygun şekilde güncellemenize olanak tanır.

onSessionStarted() geri çağırması, bağımsız değişken olarak bir WindowAreaSessionPresenter alır. Bağımsız değişken, bir pencere alanına erişip içerik 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ğırmalarda kolaylık olması için 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ık sağlamak için kullanıcılara çift ekran modunu nasıl etkinleştireceklerini veya devre dışı bırakacaklarını belirtmek üzere Dual Screen resmi simgesini kullanın.

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

Arka ekran modu

Dual-ekran modu örneğine benzer şekilde, aşağıdaki toggleRearDisplayMode() işlevi örneğinde, özellik zaten etkinse oturumu kapatır veya transferActivityToWindowArea() işlevini başka bir şekilde ç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 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, geri çağırma işleminin bir pencere alanında içerik gösterilmesine izin veren bir sunucu alınmadığı, bunun yerine tüm etkinliği başka bir alana aktardığı için uygulanması daha kolay olan WindowAreaSessionCallback olarak gösterilen etkinlik kullanılı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ığı korumak için Arka Kamera resmi simgesini kullanarak kullanıcılara arka ekran modunu nasıl etkinleştireceklerini veya devre dışı bırakacaklarını gösterin.

Ek kaynaklar