Penyematan aktivitas

Penyematan aktivitas mengoptimalkan aplikasi di perangkat layar besar dengan membagi jendela tugas aplikasi antara dua aktivitas atau dua instance aktivitas yang sama.

Gambar 1. Setelan aplikasi dengan aktivitas yang berdampingan.

Jika aplikasi Anda terdiri dari beberapa aktivitas, penyematan aktivitas memungkinkan Anda memberikan pengalaman pengguna yang ditingkatkan di tablet, perangkat foldable, dan perangkat ChromeOS.

Penyematan aktivitas tidak memerlukan pemfaktoran ulang kode. Anda menentukan cara aplikasi menampilkan aktivitasnya—secara berdampingan atau bertumpuk—dengan membuat file konfigurasi XML atau dengan melakukan panggilan API Jetpack WindowManager.

Dukungan untuk layar kecil dikelola secara otomatis. Saat aplikasi Anda berada di perangkat yang memiliki layar berukuran kecil, aktivitas akan ditumpuk di atas aktivitas yang lain. Pada layar besar, aktivitas akan ditampilkan secara berdampingan. Sistem menentukan presentasi berdasarkan konfigurasi yang telah Anda buat, dan tidak memerlukan logika cabang.

Penyematan aktivitas mendukung perubahan orientasi perangkat dan berfungsi dengan baik di perangkat foldable, aktivitas penumpukan dan pembongkaran saat perangkat dilipat dan dibentangkan.

Penyematan aktivitas didukung di sebagian besar perangkat layar besar yang menjalankan Android 12L (API level 32) dan yang lebih tinggi.

Jendela pemisahan tugas

Penyematan aktivitas memisahkan jendela tugas aplikasi menjadi dua penampung: primer dan sekunder. Penampung menyimpan aktivitas yang diluncurkan dari aktivitas utama atau dari aktivitas lain yang sudah ada di penampung.

Aktivitas ditumpuk di penampung sekunder saat diluncurkan, dan penampung sekunder ditumpuk di atas penampung utama pada layar kecil sehingga penumpukan aktivitas dan navigasi kembali konsisten dengan pengurutan aktivitas yang sudah ada di dalam aplikasi.

Dengan penyematan aktivitas tersebut, Anda menampilkan aktivitas dalam berbagai cara. Aplikasi Anda dapat memisahkan jendela tugas dengan meluncurkan dua aktivitas secara berdampingan:

Gambar 2. Dua aktivitas berdampingan.

Atau, aktivitas yang menempati seluruh jendela tugas dapat membuat pemisahan dengan meluncurkan aktivitas baru bersama:

Gambar 3. Aktivitas A memulai aktivitas B ke samping.

Aktivitas yang sudah dipisah dan berbagi jendela tugas dapat meluncurkan aktivitas lain dengan cara berikut:

  • Ke samping di atas aktivitas lain:

    Gambar 4. Aktivitas A memulai aktivitas C ke samping di atas aktivitas B.
  • Ke samping, dan geser pemisahan ke samping untuk menyembunyikan aktivitas utama sebelumnya:

    Gambar 5. Aktivitas B memulai aktivitas C ke samping dan menggeser pemisahan ke samping.
  • Meluncurkan aktivitas yang telah diterapkan di atas; yaitu, dalam tumpukan aktivitas yang sama:

    Gambar 6. Aktivitas B memulai aktivitas C tanpa flag intent tambahan.
  • Meluncurkan jendela penuh aktivitas dalam tugas yang sama:

    Gambar 7. Aktivitas A atau aktivitas B memulai aktivitas C yang mengisi jendela tugas.

Navigasi kembali

Jenis aplikasi yang berbeda dapat memiliki aturan navigasi kembali yang berbeda pula dalam status jendela tugas terpisah, bergantung pada dependensi antara aktivitas atau cara pengguna memicu peristiwa kembali, misalnya:

  • Bersamaan: Jika aktivitas berkaitan, dan salah satunya tidak boleh ditampilkan tanpa pasangannya, navigasi kembali dapat dikonfigurasi untuk menyelesaikan keduanya.
  • Sendiri: Jika aktivitas sepenuhnya bersifat independen, navigasi kembali pada aktivitas tidak memengaruhi status aktivitas lain di jendela tugas.

Peristiwa kembali dikirim ke aktivitas yang terakhir difokuskan saat menggunakan navigasi tombol.

Untuk navigasi berbasis gestur:

  • Android 14 (level API 34) dan yang lebih rendah — Peristiwa kembali dikirim ke aktivitas tempat gestur terjadi. Saat pengguna menggeser dari sisi kiri layar, peristiwa kembali akan dikirim ke aktivitas di panel kiri jendela terpisah. Saat pengguna menggeser dari sisi kanan layar, peristiwa kembali akan dikirim ke aktivitas di panel kanan.

  • Android 15 (level API 35) dan yang lebih tinggi

    • Saat menangani beberapa aktivitas dari aplikasi yang sama, gestur akan menyelesaikan aktivitas teratas, terlepas dari arah geser, sehingga memberikan pengalaman yang lebih terpadu.

    • Dalam skenario yang melibatkan dua aktivitas dari aplikasi yang berbeda (overlay), peristiwa kembali diarahkan ke aktivitas terakhir yang difokuskan, yang selaras dengan perilaku navigasi tombol.

Tata letak multi-panel

Jetpack WindowManager memungkinkan Anda membuat aktivitas yang menyematkan tata letak multipanel pada perangkat layar besar dengan Android 12L (level API 32) atau yang lebih baru, serta pada beberapa perangkat dengan versi platform sebelumnya. Aplikasi yang sudah ada berdasarkan beberapa aktivitas, bukan fragmen atau tata letak berbasis tampilan seperti SlidingPaneLayout dapat memberikan pengalaman pengguna yang lebih baik di layar besar tanpa memfaktorkan ulang kode sumber.

Salah satu contoh umumnya adalah pemisahan detail daftar. Untuk memastikan presentasi berkualitas tinggi, sistem akan memulai aktivitas daftar, lalu aplikasi akan segera memulai aktivitas detail. Sistem transisi akan menunggu hingga kedua aktivitas digambar, lalu menampilkannya secara bersamaan. Bagi pengguna, kedua aktivitas tersebut akan diluncurkan sebagai satu aktivitas.

Gambar 8. Dua aktivitas dimulai secara bersamaan dalam tata letak multipanel.

Atribut pemisahan

Anda dapat menentukan cara jendela tugas disejajarkan dengan antara penampung pemisahan dan cara penampung tersebut disusun relatif terhadap satu sama lain.

Untuk aturan yang ditentukan dalam file konfigurasi XML, tetapkan atribut berikut:

  • splitRatio: Menetapkan proporsi penampung. Nilainya adalah angka floating point dalam interval terbuka (0,0, 1,0).
  • splitLayoutDirection: Menentukan cara penampung pemisahan disusun secara relatif terhadap satu sama lain. Nilai mencakup:
    • ltr: Kiri ke kanan
    • rtl: Kanan ke kiri
    • locale: ltr atau rtl ditentukan dari setelan lokalitas

Lihat bagian Konfigurasi XML untuk mengetahui contohnya.

Untuk aturan yang dibuat menggunakan WindowManager API, buat objek SplitAttributes dengan SplitAttributes.Builder dan panggil metode builder berikut:

Lihat bagian WindowManager API untuk mengetahui contohnya.

Gambar 9. Dua pemisahan aktivitas disusun dari kiri ke kanan, tetapi dengan rasio pemisahan yang berbeda.

Placeholder

Aktivitas placeholder adalah aktivitas sekunder kosong yang menempati area pemisahan aktivitas. Tujuannya agar aktivitas kosong tersebut diganti dengan aktivitas lain yang berisi konten. Misalnya, aktivitas placeholder dapat menempati sisi sekunder pemisahan aktivitas dalam tata letak daftar-detail hingga item dari daftar dipilih, yang pada saat itu aktivitas yang berisi informasi detail untuk item daftar yang dipilih akan menggantikan placeholder.

Secara default, sistem hanya menampilkan placeholder jika ada cukup ruang untuk pemisahan aktivitas. Placeholder otomatis selesai jika ukuran tampilan berubah menjadi lebar atau tinggi yang terlalu kecil untuk menampilkan pemisahan. Jika ruang memungkinkan, sistem akan meluncurkan kembali placeholder dengan status diinisialisasi ulang.

Gambar 10. Perangkat foldable terlipat dan terbuka. Aktivitas placeholder selesai dan dibuat ulang saat ukuran layar berubah.

Namun, atribut stickyPlaceholder dari metode SplitPlaceholderRule atau setSticky() SplitPlaceholder.Builder dapat mengganti perilaku default. Jika atribut atau metode menentukan nilai true, sistem akan menampilkan placeholder sebagai aktivitas teratas di jendela tugas saat ukuran layar diubah menjadi tampilan satu panel dari tampilan dua panel (lihat Konfigurasi pemisahan sebagai contoh).

Gambar 11. Perangkat foldable terlipat dan terbuka. Aktivitas placeholder melekat.

Perubahan ukuran jendela

Saat perubahan konfigurasi perangkat mengurangi lebar jendela tugas sehingga tidak cukup besar untuk tata letak multipanel (misalnya, saat perangkat foldable layar besar dilipat dari ukuran tablet ke ukuran ponsel atau jendela aplikasi diubah ukurannya dalam mode multi-aplikasi), aktivitas non-placeholder di panel sekunder jendela tugas akan ditumpuk di atas aktivitas di panel utama.

Aktivitas placeholder hanya ditampilkan jika memiliki lebar tampilan yang cukup untuk pemisahan. Pada layar dengan ukuran lebih kecil, placeholder akan otomatis ditutup. Saat ukuran area layar menjadi cukup besar lagi, placeholder akan dibuat ulang. (Lihat bagian Placeholder.)

Penumpukan aktivitas dapat dilakukan karena WindowManager melakukan pengurutan z pada aktivitas di panel sekunder di atas aktivitas di panel utama.

Beberapa aktivitas di panel sekunder

Aktivitas B memulai aktivitas C tanpa flag intent tambahan:

Pemisahan aktivitas yang berisi aktivitas A, B, dan C dengan C yang bertumpuk di
          atas B.

yang menghasilkan aktivitas urutan z berikut dalam tugas yang sama:

Tumpukan aktivitas sekunder yang berisi aktivitas C yang ditumpuk di atas B.
          Tumpukan sekunder ditumpuk di atas tumpukan aktivitas utama yang
          berisi aktivitas A.

Jadi, dalam jendela tugas yang lebih kecil, aplikasi akan dikecilkan satu aktivitas dengan C di bagian atas tumpukan:

Jendela kecil hanya menampilkan aktivitas C.

Kembali ke jendela yang lebih kecil akan membuka aktivitas yang ditumpuk di atas aktivitas yang lain.

Jika konfigurasi jendela tugas dikembalikan ke ukuran lebih besar yang dapat mengakomodasi beberapa panel, aktivitas akan ditampilkan lagi secara berdampingan.

Pemisahan bertumpuk

Aktivitas B memulai aktivitas C ke samping dan menggeser pemisahan ke samping:

Jendela tugas akan menampilkan aktivitas A dan B, lalu aktivitas B dan C.

Hasilnya adalah aktivitas urutan z berikut dalam tugas yang sama:

Aktivitas A, B, dan C dalam satu tumpukan. Aktivitas ditumpuk
          dalam urutan berikut dari atas ke bawah: C, B, A.

Pada jendela tugas yang lebih kecil, aplikasi akan dikecilkan ke satu aktivitas dengan C di bagian atas:

Jendela kecil hanya menampilkan aktivitas C.

Orientasi potret tetap

Setelan manifes android:screenOrientation memungkinkan aplikasi membatasi aktivitas menjadi orientasi potret atau lanskap. Untuk meningkatkan pengalaman pengguna di perangkat layar besar seperti tablet dan perangkat foldable, produsen perangkat (OEM) dapat mengabaikan permintaan orientasi layar dan menjadikan aplikasi tampilan lebar dalam orientasi potret pada tampilan lanskap atau orientasi lanskap pada tampilan potret.

Gambar 12. Aktivitas tampilan lebar: potret tetap pada perangkat lanskap (kiri), lanskap tetap pada perangkat potret (kanan).

Demikian pula, saat penyematan aktivitas diaktifkan, OEM dapat menyesuaikan perangkat ke aktivitas potret tetap tampilan lebar dalam orientasi lanskap di perangkat layar besar (lebar ≥ 600 dp). Saat aktivitas potret tetap meluncurkan aktivitas kedua, perangkat dapat menampilkan dua aktivitas secara berdampingan dalam tampilan dua panel.

Gambar 13. Aktivitas potret tetap A memulai aktivitas B ke samping.

Selalu tambahkan properti android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED ke file manifes aplikasi Anda untuk memberi tahu perangkat bahwa aplikasi Anda mendukung penyematan aktivitas (lihat bagian Konfigurasi pemisahan). Perangkat yang disesuaikan OEM kemudian dapat menentukan apakah akan membuat tampilan lebar aktivitas potret tetap.

Konfigurasi pemisahan

Aturan pemisahan mengonfigurasi pemisahan aktivitas. Anda menentukan aturan pemisahan di file konfigurasi XML atau dengan melakukan panggilan API WindowManager Jetpack.

Dalam kedua kasus tersebut, aplikasi Anda harus mengakses library WindowManager dan harus memberi tahu sistem bahwa aplikasi telah menerapkan penyematan aktivitas.

Lakukan hal berikut:

  1. Tambahkan dependensi library WindowManager terbaru ke file build.gradle level modul aplikasi, misalnya:

    implementation 'androidx.window:window:1.1.0-beta02'

    Library WindowManager menyediakan semua komponen yang diperlukan untuk penyematan aktivitas.

  2. Beri tahu sistem bahwa aplikasi Anda telah menerapkan penyematan aktivitas.

    Tambahkan properti android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED ke elemen <application> file manifes aplikasi, dan tetapkan nilai ke benar (true), misalnya:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <application>
            <property
                android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
                android:value="true" />
        </application>
    </manifest>
    

    Pada rilis WindowManager 1.1.0-alpha06 dan yang lebih baru, pemisahan penyematan aktivitas dinonaktifkan kecuali jika properti ditambahkan ke manifes dan ditetapkan ke true.

    Selain itu, produsen perangkat menggunakan setelan untuk mengaktifkan kemampuan kustom untuk aplikasi yang mendukung penyematan aktivitas. Misalnya, perangkat dapat membuat tampilan lebar aktivitas khusus potret pada tampilan lanskap untuk mengorientasikan aktivitas untuk transisi ke tata letak dua panel saat aktivitas kedua dimulai (lihat Orientasi potret tetap).

Konfigurasi XML

Untuk membuat implementasi penyematan aktivitas berbasis XML, selesaikan langkah-langkah berikut:

  1. Buat file resource XML yang melakukan hal berikut:

    • Menentukan aktivitas yang membagikan pemisahan
    • Mengonfigurasi opsi pemisahan
    • Membuat placeholder untuk penampung sekunder pemisahan jika konten tidak tersedia
    • Menentukan aktivitas yang tidak boleh menjadi bagian dari pemisahan

    Contoh:

    <!-- main_split_config.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always"
            window:clearTop="false">
            <SplitPairFilter
                window:primaryActivityName=".ListActivity"
                window:secondaryActivityName=".DetailActivity"/>
        </SplitPairRule>
    
        <!-- Specify a placeholder for the secondary container when content is
             not available. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".PlaceholderActivity"
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:stickyPlaceholder="false">
            <ActivityFilter
                window:activityName=".ListActivity"/>
        </SplitPlaceholderRule>
    
        <!-- Define activities that should never be part of a split. Note: Takes
             precedence over other split rules for the activity named in the
             rule. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. Buat penginisialisasi.

    Komponen RuleController WindowManager menguraikan file konfigurasi XML dan membuat aturan tersedia untuk sistem. Library Jetpack Startup Initializer membuat file XML tersedia untuk RuleController saat aplikasi dimulai sehingga aturan akan berlaku saat aktivitas dimulai.

    Untuk membuat penginisialisasi, lakukan hal berikut:

    1. Tambahkan dependensi library Jetpack Startup terbaru ke file build.gradle level modul, misalnya:

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. Buat class yang mengimplementasikan antarmuka Initializer.

      Penginisialisasi membuat aturan pemisahan tersedia untuk RuleController dengan meneruskan ID file konfigurasi XML (main_split_config.xml) ke metode RuleController.parseRules().

      Kotlin

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      Java

      public class SplitInitializer implements Initializer<RuleController> {
      
           @NonNull
           @Override
           public RuleController create(@NonNull Context context) {
               RuleController ruleController = RuleController.getInstance(context);
               ruleController.setRules(
                   RuleController.parseRules(context, R.xml.main_split_config)
               );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }
  3. Membuat penyedia konten untuk definisi aturan.

    Tambahkan androidx.startup.InitializationProvider ke file manifes aplikasi sebagai <provider>. Sertakan referensi ke implementasi penginisialisasi RuleController, SplitInitializer:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    InitializationProvider menemukan dan melakukan inisialisasi SplitInitializer sebelum metode onCreate() aplikasi dipanggil. Akibatnya, aturan pemisahan berlaku saat aktivitas utama aplikasi dimulai.

WindowManager API

Anda dapat mengimplementasikan penyematan aktivitas secara terprogram dengan beberapa panggilan API. Lakukan panggilan di metode onCreate() dari subclass Application untuk memastikan aturan berlaku sebelum aktivitas diluncurkan.

Untuk membuat pemisahan aktivitas secara terprogram, lakukan hal berikut:

  1. Buat aturan pemisahan:

    1. Buat SplitPairFilter yang mengidentifikasi aktivitas yang berbagi pemisahan:

      Kotlin

      val splitPairFilter = SplitPairFilter(
         ComponentName(this, ListActivity::class.java),
         ComponentName(this, DetailActivity::class.java),
         null
      )

      Java

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );
    2. Tambahkan filter ke kumpulan filter:

      Kotlin

      val filterSet = setOf(splitPairFilter)

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
    3. Buat atribut tata letak untuk pemisahan:

      Kotlin

      val splitAttributes: SplitAttributes = SplitAttributes.Builder()
          .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
          .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
          .build()

      Java

      final SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();

      SplitAttributes.Builder membuat objek yang berisi atribut tata letak:

      • setSplitType(): Menentukan cara area tampilan yang tersedia dialokasikan ke setiap penampung aktivitas. Jenis pemisahan rasio menentukan proporsi area tampilan tersedia yang dialokasikan ke penampung utama; penampung sekunder menempati sisa area tampilan yang tersedia.
      • setLayoutDirection(): Menentukan cara penampung aktivitas disusun relatif terhadap satu sama lain, penampung utama terlebih dahulu.
    4. Buat SplitPairRule:

      Kotlin

      val splitPairRule = SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build()

      Java

      SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build();

      SplitPairRule.Builder membuat dan mengonfigurasi aturan:

      • filterSet: Berisi filter pasangan pemisahan yang menentukan kapan aturan diterapkan dengan mengidentifikasi aktivitas yang memiliki pemisahan yang sama.
      • setDefaultSplitAttributes(): Menerapkan atribut tata letak ke aturan.
      • setMinWidthDp(): Menetapkan lebar tampilan minimum (dalam piksel kepadatan mandiri, dp) yang memungkinkan pemisahan.
      • setMinSmallestWidthDp(): Menetapkan nilai minimum (dalam dp) yang harus diikuti oleh ukuran yang lebih kecil dari dua dimensi tampilan, terlepas dari orientasi perangkat.
      • setMaxAspectRatioInPortrait(): Menetapkan rasio aspek tampilan maksimum (tinggi:lebar) dalam orientasi potret yang menampilkan pemisahan aktivitas. Jika rasio aspek tampilan potret melebihi rasio aspek maksimum, pemisahan akan dinonaktifkan terlepas dari lebar tampilan. Catatan: Nilai default-nya adalah 1,4, yang menyebabkan aktivitas menempati seluruh jendela tugas dalam orientasi potret di sebagian besar tablet. Lihat juga SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT dan setMaxAspectRatioInLandscape(). Nilai default untuk lanskap adalah ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary(): Menetapkan pengaruh penyelesaian semua aktivitas di penampung sekunder terhadap aktivitas di penampung utama. NEVER menunjukkan bahwa sistem tidak boleh menyelesaikan aktivitas utama saat semua aktivitas dalam penampung sekunder selesai (lihat Menyelesaikan aktivitas).
      • setFinishSecondaryWithPrimary(): Menetapkan pengaruh menyelesaikan semua aktivitas di penampung utama terhadap aktivitas di penampung sekunder. ALWAYS menunjukkan bahwa sistem harus selalu menyelesaikan aktivitas dalam penampung sekunder saat semua aktivitas dalam penampung utama selesai (lihat Menyelesaikan aktivitas).
      • setClearTop(): Menentukan apakah semua aktivitas di penampung sekunder selesai saat aktivitas baru diluncurkan di penampung. Nilai false menentukan bahwa aktivitas baru ditumpuk di atas aktivitas yang sudah ada di penampung sekunder.
    5. Dapatkan instance singleton WindowManager RuleController, dan tambahkan aturan:

      Kotlin

        val ruleController = RuleController.getInstance(this)
        ruleController.addRule(splitPairRule)
        

      Java

        RuleController ruleController = RuleController.getInstance(this);
        ruleController.addRule(splitPairRule);
        
  2. Buat placeholder untuk penampung sekunder saat konten tidak tersedia:

    1. Buat ActivityFilter yang mengidentifikasi aktivitas yang digunakan placeholder untuk membagikan pemisahan jendela tugas:

      Kotlin

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. Tambahkan filter ke kumpulan filter:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. Buat SplitPlaceholderRule:

      Kotlin

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            Intent(context, PlaceholderActivity::class.java)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build()

      Java

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            new Intent(context, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder membuat dan mengonfigurasi aturan:

      • placeholderActivityFilterSet: Berisi filter aktivitas yang menentukan kapan aturan diterapkan dengan mengidentifikasi aktivitas yang terkait dengan aktivitas placeholder.
      • Intent: Menentukan peluncuran aktivitas placeholder.
      • setDefaultSplitAttributes(): Menerapkan atribut tata letak ke aturan.
      • setMinWidthDp(): Menetapkan lebar tampilan minimum (dalam piksel kepadatan mandiri, dp) yang memungkinkan pemisahan.
      • setMinSmallestWidthDp(): Menetapkan nilai minimum (dalam dp) yang harus diikuti oleh ukuran yang lebih kecil dari dua dimensi tampilan, terlepas dari orientasi perangkat.
      • setMaxAspectRatioInPortrait(): Menetapkan rasio aspek tampilan maksimum (tinggi:lebar) dalam orientasi potret yang menampilkan pemisahan aktivitas. Catatan: Nilai default-nya adalah 1,4, yang menyebabkan aktivitas memenuhi jendela tugas dalam orientasi potret di sebagian besar tablet. Lihat juga SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT dan setMaxAspectRatioInLandscape(). Nilai default untuk lanskap adalah ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder(): Menetapkan pengaruh penyelesaian aktivitas placeholder pada aktivitas di penampung utama. ALWAYS menunjukkan bahwa sistem harus selalu menyelesaikan aktivitas dalam penampung utama saat placeholder selesai (lihat Menyelesaikan aktivitas).
      • setSticky(): Menentukan apakah aktivitas placeholder akan muncul di atas tumpukan aktivitas pada tampilan layar kecil setelah placeholder pertama kali muncul dalam pemisahan dengan lebar minimum yang memadai.
    4. Tambahkan aturan ke WindowManager RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      Java

      ruleController.addRule(splitPlaceholderRule);
  3. Tentukan aktivitas yang tidak boleh menjadi bagian dari pemisahan:

    1. Buat ActivityFilter yang mengidentifikasi aktivitas yang harus selalu menempati seluruh area tampilan tugas:

      Kotlin

      val expandedActivityFilter = ActivityFilter(
        ComponentName(this, ExpandedActivity::class.java),
        null
      )

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
    2. Tambahkan filter ke kumpulan filter:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
    3. Buat ActivityRule:

      Kotlin

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()

      Java

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();

      ActivityRule.Builder membuat dan mengonfigurasi aturan:

      • expandedActivityFilterSet: Berisi filter aktivitas yang menentukan kapan aturan diterapkan dengan mengidentifikasi aktivitas yang ingin Anda kecualikan dari pemisahan.
      • setAlwaysExpand(): Menentukan apakah aktivitas harus mengisi seluruh jendela tugas.
    4. Tambahkan aturan ke WindowManager RuleController:

      Kotlin

      ruleController.addRule(activityRule)

      Java

      ruleController.addRule(activityRule);

Penyematan lintas-aplikasi

Di Android 13 (API level 33) dan yang lebih baru, aplikasi dapat menyematkan aktivitas dari aplikasi lain. Penyematan aktivitas lintas-aplikasi, atau lintas-UID, memungkinkan integrasi visual aktivitas dari beberapa aplikasi Android. Sistem menampilkan aktivitas aplikasi host dan aktivitas tersemat dari aplikasi lain di layar secara berdampingan atau atas dan bawah seperti dalam penyematan aktivitas aplikasi tunggal.

Misalnya, aplikasi Setelan dapat menyematkan aktivitas pemilih wallpaper dari aplikasi WallpaperPicker:

Gambar 14. Aplikasi setelan (menu di sebelah kiri) dengan pemilih wallpaper sebagai aktivitas tersemat (kanan).

Model kepercayaan

Proses host yang menyematkan aktivitas dari aplikasi lain dapat menentukan ulang presentasi aktivitas tersemat, termasuk ukuran, posisi, pemangkasan, dan transparansi. Host berbahaya dapat menggunakan kemampuan ini untuk menyesatkan pengguna dan membuat serangan clickjacking atau penggantian UI lainnya.

Untuk mencegah penyalahgunaan penyematan aktivitas lintas aplikasi, Android mewajibkan aplikasi memilih untuk mengizinkan penyematan aktivitas. Aplikasi dapat menetapkan host sebagai tepercaya atau tidak tepercaya.

Host tepercaya

Untuk mengizinkan aplikasi lain menyematkan dan mengontrol sepenuhnya presentasi aktivitas dari aplikasi Anda, tentukan sertifikat SHA-256 aplikasi host di atribut android:knownActivityEmbeddingCerts dari elemen <activity> atau <application> dari file manifes aplikasi Anda.

Setel nilai android:knownActivityEmbeddingCerts sebagai string:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

atau, untuk menentukan beberapa sertifikat, array string:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

yang mereferensikan resource seperti berikut:

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

Pemilik aplikasi bisa mendapatkan ringkasan sertifikat SHA dengan menjalankan tugas signingReport Gradle. Ringkasan sertifikat adalah sidik jari SHA-256 tanpa titik dua pemisah. Untuk mengetahui informasi selengkapnya, lihat Menjalankan laporan penandatanganan dan Mengautentikasi Klien.

Host tidak tepercaya

Untuk mengizinkan aplikasi menyematkan aktivitas aplikasi dan mengontrol presentasinya, tentukan atribut android:allowUntrustedActivityEmbedding dalam elemen <activity> atau <application> di manifes aplikasi, misalnya:

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

Nilai default atribut ini adalah false, yang mencegah penyematan aktivitas lintas-aplikasi.

Autentikasi kustom

Untuk mengurangi risiko penyematan aktivitas tidak tepercaya, buat mekanisme autentikasi kustom yang memverifikasi identitas host. Jika Anda mengetahui sertifikat host, gunakan library androidx.security.app.authenticator untuk melakukan autentikasi. Jika host mengautentikasi setelah aktivitas disematkan, Anda dapat menampilkan konten sebenarnya. Jika tidak, Anda dapat memberi tahu pengguna bahwa tindakan tersebut tidak diizinkan dan memblokir konten.

Gunakan metode ActivityEmbeddingController#isActivityEmbedded() dari library Jetpack WindowManager untuk memeriksa apakah host menyematkan aktivitas Anda, misalnya:

Kotlin

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

Java

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity);
}

Batasan ukuran minimum

Sistem Android menerapkan tinggi dan lebar minimum yang ditentukan dalam elemen manifes aplikasi <layout> ke aktivitas tersemat. Jika aplikasi tidak menentukan tinggi dan lebar minimum, nilai default sistem akan berlaku (sw220dp).

Jika host mencoba mengubah ukuran penampung tersemat menjadi ukuran yang lebih kecil dari penampung minimum, penampung tersemat akan diperluas untuk menempati seluruh batas tugas.

<activity-alias>

Agar penyematan aktivitas tepercaya atau tidak tepercaya berfungsi dengan elemen <activity-alias>, android:knownActivityEmbeddingCerts atau android:allowUntrustedActivityEmbedding harus diterapkan ke aktivitas target, bukan alias. Kebijakan yang memverifikasi keamanan di server sistem didasarkan pada flag yang ditetapkan pada target, bukan alias.

Aplikasi host

Aplikasi host menerapkan penyematan aktivitas lintas aplikasi dengan cara yang sama seperti saat menerapkan penyematan aktivitas aplikasi tunggal. Objek SplitPairRule dan SplitPairFilter atau ActivityRule dan ActivityFilter menentukan aktivitas tersemat dan pemisahan jendela tugas. Aturan pemisahan ditentukan secara statis di XML atau saat runtime menggunakan panggilan API Jetpack WindowManager.

Jika aplikasi host mencoba menyematkan aktivitas yang belum ikut serta dalam penyematan lintas aplikasi, aktivitas tersebut akan menempati seluruh batas tugas. Akibatnya, aplikasi host perlu mengetahui apakah aktivitas target memungkinkan penyematan lintas aplikasi.

Jika aktivitas tersemat memulai aktivitas baru dalam tugas yang sama dan aktivitas baru belum memilih untuk ikut serta dalam penyematan lintas aplikasi, aktivitas tersebut akan menempati seluruh batas tugas, bukan menempatkan aktivitas dalam penampung tersemat.

Aplikasi host dapat menyematkan aktivitasnya sendiri tanpa batasan selama aktivitas diluncurkan pada tugas yang sama.

Contoh pemisahan

Memisahkan dari jendela penuh

Gambar 15. Aktivitas A memulai aktivitas B ke samping.

Tidak perlu memfaktorkan ulang. Anda dapat menentukan konfigurasi pemisahan secara statis atau saat runtime, lalu memanggil Context#startActivity() tanpa parameter tambahan.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Memisahkan secara default

Saat halaman landing aplikasi didesain untuk dibagi menjadi dua penampung pada layar besar, pengalaman pengguna akan optimal jika kedua aktivitas dibuat dan ditampilkan secara bersamaan. Namun, konten mungkin tidak tersedia untuk penampung sekunder pemisahan hingga pengguna berinteraksi dengan aktivitas di penampung utama (misalnya, pengguna memilih item dari menu navigasi). Aktivitas placeholder dapat mengisi kekosongan hingga konten dapat ditampilkan dalam penampung sekunder pemisahan (lihat bagian Placeholder).

Gambar 16. Pemisahan dibuat dengan membuka dua aktivitas secara bersamaan. Satu aktivitas adalah placeholder.

Untuk membuat pemisahan dengan placeholder, buat placeholder dan kaitkan dengan aktivitas utama:

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

Saat aplikasi menerima intent, aktivitas target dapat ditampilkan sebagai bagian sekunder dari pemisahan aktivitas. Misalnya, permintaan untuk menampilkan layar detail dengan informasi tentang item dari suatu daftar. Pada layar kecil, detail ditampilkan di jendela tugas penuh; pada perangkat yang lebih besar, ditampilkan di samping daftar.

Gambar 17. Aktivitas detail deep link ditampilkan sendiri di layar kecil, tetapi ditampilkan bersama dengan aktivitas daftar di layar besar.

Permintaan peluncuran harus diarahkan ke aktivitas utama, dan aktivitas detail target harus diluncurkan dalam pemisahan. Sistem otomatis memilih presentasi yang benar—ditumpuk atau berdampingan—berdasarkan lebar tampilan yang tersedia.

Kotlin

override fun onCreate(savedInstanceState Bundle?) {
    . . .
    RuleController.getInstance(this)
        .addRule(SplitPairRule.Builder(filterSet).build())
    startActivity(Intent(this, DetailActivity::class.java))
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    RuleController.getInstance(this)
        .addRule(new SplitPairRule.Builder(filterSet).build());
    startActivity(new Intent(this, DetailActivity.class));
}

Tujuan deep link mungkin satu-satunya aktivitas yang harus tersedia bagi pengguna di data navigasi sebelumnya, dan Anda mungkin tidak ingin mengabaikan aktivitas detail dan hanya menyisakan aktivitas utama:

Tampilan besar yang berisi aktivitas daftar dan detail aktivitas secara berdampingan.
          Navigasi kembali tidak dapat menutup aktivitas detail dan keluar dari
          aktivitas daftar di layar.

Layar kecil dengan aktivitas detail saja. Navigasi kembali tidak dapat
          menutup aktivitas detail dan menampilkan aktivitas daftar.

Sebagai gantinya, Anda dapat menyelesaikan kedua aktivitas secara bersamaan menggunakan atribut finishPrimaryWithSecondary:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

Lihat bagian Atribut konfigurasi.

Beberapa aktivitas di penampung terpisah

Dengan penumpukan beberapa aktivitas dalam penampung terpisah, pengguna dapat mengakses konten yang dalam. Misalnya, dengan pemisahan detail daftar, mungkin pengguna harus membuka bagian sub-detail, tetapi tetap mempertahankan aktivitas utama:

Gambar 18. Aktivitas dibuka di panel sekunder jendela tugas.

Kotlin

class DetailActivity {
    . . .
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

Java

public class DetailActivity {
    . . .
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

Aktivitas sub-detail ditempatkan di atas aktivitas detail, dengan menyembunyikannya:

Kemudian pengguna dapat kembali ke tingkat detail sebelumnya dengan menavigasi kembali melalui tumpukan:

Gambar 19. Aktivitas dihapus dari bagian atas tumpukan.

Penumpukan aktivitas di atas aktivitas lain adalah perilaku default jika aktivitas diluncurkan dari aktivitas dalam penampung sekunder yang sama. Aktivitas yang diluncurkan dari penampung utama dalam pemisahan aktif juga akan berakhir di penampung sekunder di bagian atas tumpukan aktivitas.

Aktivitas dalam tugas baru

Saat aktivitas di jendela tugas terpisah memulai aktivitas di tugas baru, tugas baru dipisahkan dari tugas yang menyertakan pemisahan dan ditampilkan sebagai jendela penuh. Layar Terbaru akan menampilkan dua tugas: tugas di pemisahan dan tugas baru.

Gambar 20. Memulai aktivitas C dalam tugas baru dari aktivitas B.

Penggantian aktivitas

Aktivitas dapat diganti di tumpukan penampung sekunder; misalnya, saat aktivitas utama digunakan untuk navigasi level atas dan aktivitas sekunder adalah tujuan yang dipilih. Setiap pilihan dari navigasi level atas harus memulai aktivitas baru di penampung sekunder dan menghapus aktivitas atau aktivitas yang sebelumnya ada di penampung tersebut.

Gambar 21. Aktivitas navigasi level atas di panel utama menggantikan aktivitas tujuan di panel sekunder.

Jika aplikasi tidak menyelesaikan aktivitas dalam penampung sekunder saat pilihan navigasi berubah, navigasi kembali mungkin akan membingungkan saat pemisahan diciutkan (saat perangkat dilipat). Misalnya, jika Anda memiliki menu di panel utama dan layar A dan B bertumpuk di panel sekunder, saat pengguna melipat ponsel, B berada di atas A, dan A berada di atas menu. Saat pengguna kembali dari B, A yang akan muncul, bukan menu.

Dalam kasus semacam ini, layar A harus dihapus dari data sebelumnya.

Perilaku default saat meluncurkan ke sisi dalam penampung baru selama pemisahan yang ada adalah menempatkan penampung sekunder baru di atas dan mempertahankan yang lama di data sebelumnya. Anda dapat mengonfigurasi pemisahan untuk menghapus penampung sekunder sebelumnya dengan clearTop dan meluncurkan aktivitas baru seperti biasa.

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

Kotlin

class MenuActivity {
    . . .
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

Java

public class MenuActivity {
    . . .
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

Atau, gunakan aktivitas sekunder yang sama, dan dari aktivitas utama (menu), kirimkan intent baru yang mengatasi instance yang sama, tetapi memicu status atau update UI di penampung sekunder.

Beberapa pemisahan

Aplikasi dapat menyediakan navigasi mendalam multi-level dengan meluncurkan aktivitas tambahan ke samping.

Saat aktivitas di penampung sekunder meluncurkan aktivitas baru ke samping, pemisahan baru akan dibuat di atas pemisahan yang ada.

Gambar 22. Aktivitas B memulai aktivitas C ke samping.

Data sebelumnya berisi semua aktivitas yang sebelumnya telah dibuka sehingga pengguna dapat menuju ke pemisahan A/B setelah menyelesaikan C.

Aktivitas A, B, dan C dalam tumpukan. Aktivitas ditumpuk dalam
          urutan berikut dari atas ke bawah: C, B, A.

Untuk membuat pemisahan baru, luncurkan aktivitas baru dari penampung sekunder yang ada ke samping. Deklarasikan konfigurasi untuk pemisahan A/B dan B/C dan luncurkan aktivitas C biasanya dari B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

Kotlin

class B {
    . . .
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

Java

public class B {
    . . .
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

Merespons perubahan status pemisahan

Berbagai aktivitas dalam aplikasi dapat memiliki elemen UI yang melakukan fungsi yang sama; misalnya, kontrol yang membuka jendela yang berisi setelan akun.

Gambar 23. Berbagai aktivitas dengan elemen UI yang identik secara fungsional.

Akan berlebihan jika dua aktivitas yang memiliki elemen UI yang sama dipisah dan mungkin membingungkan untuk menampilkan elemen di kedua aktivitas.

Gambar 24. Duplikasi elemen UI dalam pemisahan aktivitas.

Untuk mengetahui waktu pemisahan aktivitas, periksa alur SplitController.splitInfoList atau daftarkan pemroses dengan SplitControllerCallbackAdapter untuk perubahan status terpisah. Kemudian, sesuaikan UI sesuai dengan:

Kotlin

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

Coroutine dapat diluncurkan dalam status siklus proses apa pun, tetapi biasanya diluncurkan dalam status STARTED untuk menghemat resource (lihat Menggunakan coroutine Kotlin dengan komponen yang mendukung siklus proses untuk informasi selengkapnya).

Callback dapat dibuat dalam status siklus proses apa pun, termasuk saat suatu aktivitas dihentikan. Pemroses biasanya harus terdaftar di onStart() dan tidak terdaftar di onStop().

Modal jendela penuh

Beberapa aktivitas akan memblokir pengguna agar tidak berinteraksi dengan aplikasi hingga tindakan yang ditentukan dilakukan; misalnya, aktivitas layar login, layar konfirmasi kebijakan, atau pesan error. Aktivitas modal harus dicegah agar tidak muncul dalam pemisahan.

Suatu aktivitas dapat dipaksa untuk selalu mengisi jendela tugas menggunakan konfigurasi perluasan:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

Menyelesaikan aktivitas

Pengguna dapat menyelesaikan aktivitas di kedua sisi pemisahan dengan menggeser dari tepi layar:

Gambar 25. Geser gestur untuk menyelesaikan aktivitas B.
Gambar 26. Geser gestur untuk menyelesaikan aktivitas A.

Jika perangkat disiapkan untuk menggunakan tombol kembali, bukan navigasi gestur, input akan dikirim ke aktivitas yang difokuskan—aktivitas yang disentuh atau diluncurkan terakhir.

Efek dari menyelesaikan semua aktivitas dalam penampung pada penampung yang berlawanan bergantung pada konfigurasi pemisahan.

Atribut konfigurasi

Anda dapat menentukan atribut aturan pasangan pemisahan untuk mengonfigurasi bagaimana menyelesaikan semua aktivitas di satu sisi pemisahan memengaruhi aktivitas di sisi lain pemisahan. Atribut tersebut adalah:

  • window:finishPrimaryWithSecondary — Bagaimana menyelesaikan semua aktivitas di penampung sekunder dapat memengaruhi aktivitas di penampung utama
  • window:finishSecondaryWithPrimary — Bagaimana menyelesaikan semua aktivitas di penampung utama dapat memengaruhi aktivitas di penampung sekunder

Kemungkinan nilai atribut meliputi:

  • always — Selalu selesaikan aktivitas di penampung terkait
  • never — Jangan pernah menyelesaikan aktivitas di penampung terkait
  • adjacent — Menyelesaikan aktivitas di penampung terkait saat dua penampung ditampilkan berdekatan, tetapi tidak saat dua penampung ditumpuk

Contoh:

<SplitPairRule
    &lt;!-- Do not finish primary container activities when all secondary container activities finish. --&gt;
    window:finishPrimaryWithSecondary="never"
    &lt;!-- Finish secondary container activities when all primary container activities finish. --&gt;
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Konfigurasi default

Saat semua aktivitas dalam satu penampung pemisahan selesai, penampung yang tersisa akan menempati seluruh jendela:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Pemisahan yang berisi aktivitas A dan B. A selesai, menyisakan B yang menempati
          seluruh jendela.

Pemisahan yang berisi aktivitas A dan B. B selesai, menyisakan A yang menempati
          seluruh jendela.

Menyelesaikan aktivitas bersama

Selesaikan aktivitas di penampung utama secara otomatis saat semua aktivitas di penampung sekunder selesai:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Pemisahan yang berisi aktivitas A dan B. B selesai, yang juga
          menyelesaikan A, membuat jendela tugas menjadi kosong.

Pemisahan yang berisi aktivitas A dan B. A selesai, menyisakan hanya B
          di jendela tugas.

Selesaikan aktivitas di penampung sekunder secara otomatis saat semua aktivitas di penampung utama selesai:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Pemisahan yang berisi aktivitas A dan B. A selesai, yang juga
          menyelesaikan B, membuat jendela tugas menjadi kosong.

Pemisahan yang berisi aktivitas A dan B. B sudah selesai dan hanya menyisakan B
          di jendela tugas.

Selesaikan aktivitas bersama jika semua aktivitas di penampung utama atau sekunder selesai:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Pemisahan yang berisi aktivitas A dan B. A selesai, yang juga
          menyelesaikan B, membuat jendela tugas menjadi kosong.

Pemisahan yang berisi aktivitas A dan B. B selesai, yang juga
          menyelesaikan A, membuat jendela tugas menjadi kosong.

Menyelesaikan beberapa aktivitas dalam penampung

Jika beberapa aktivitas ditumpuk dalam penampung terpisah, menyelesaikan aktivitas di bagian bawah tumpukan tidak otomatis menyelesaikan aktivitas di bagian atas.

Misalnya, jika dua aktivitas berada dalam penampung sekunder, C di atas B:

Tumpukan aktivitas sekunder yang berisi aktivitas C yang ditumpuk di atas B
          ditumpuk di atas tumpukan aktivitas utama yang berisi aktivitas
          A.

dan konfigurasi pemisahan ditentukan oleh konfigurasi aktivitas A dan B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

menyelesaikan aktivitas teratas akan mempertahankan pemisahan.

Pisahkan dengan aktivitas A dalam penampung utama serta aktivitas B dan C di
          sekunder, C yang ditumpuk di atas B. C selesai, menyisakan A dan B dalam
          pemisahan aktivitas.

Menyelesaikan aktivitas bagian bawah (root) dari penampung sekunder tidak akan menghapus aktivitas di atasnya; dan, juga tetap akan mempertahankan pemisahan.

Pisahkan dengan aktivitas A dalam penampung utama serta aktivitas B dan C di
          sekunder, C yang ditumpuk di atas B. B selesai, menyisakan A dan C dalam
          pemisahan aktivitas.

Setiap aturan tambahan untuk menyelesaikan aktivitas secara bersamaan, seperti menyelesaikan aktivitas sekunder dengan aktivitas utama, juga akan dijalankan:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Pisahkan dengan aktivitas A dalam penampung utama serta aktivitas B dan C dalam
          penampung sekunder, C ditumpuk di atas B. A selesai, yang juga
          menyelesaikan B dan C.

Ketika pemisahan dikonfigurasi untuk menyelesaikan penampung primer dan sekunder sekaligus:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Pisahkan dengan aktivitas A dalam penampung utama serta aktivitas B dan C di
          sekunder, C yang ditumpuk di atas B. C selesai, menyisakan A dan B dalam
          pemisahan aktivitas.

Pisahkan dengan aktivitas A dalam penampung utama serta aktivitas B dan C di
          sekunder, C yang ditumpuk di atas B. B selesai, menyisakan A dan C dalam
          pemisahan aktivitas.

Pisahkan dengan aktivitas A dalam penampung utama serta aktivitas B dan C di
          sekunder, C yang ditumpuk di atas B. A selesai, yang juga menyelesaikan B dan
          C.

Mengubah properti pemisahan saat runtime

Properti pemisahan yang aktif dan terlihat tidak dapat diubah. Mengubah aturan pemisahan akan memengaruhi peluncuran aktivitas tambahan dan penampung baru, tetapi tidak memengaruhi pemisahan yang sudah ada dan aktif.

Untuk mengubah properti pemisahan aktif, selesaikan aktivitas samping atau aktivitas dalam pemisahan dan luncurkan lagi ke samping menggunakan konfigurasi baru.

Properti pemisahan dinamis

Android 15 (API level 35) dan yang lebih tinggi yang didukung oleh Jetpack WindowManager 1.4 dan yang lebih tinggi menawarkan fitur dinamis yang memungkinkan konfigurasi pemisahan penyematan aktivitas, termasuk:

  • Perluasan panel: Pemisah interaktif yang dapat ditarik memungkinkan pengguna mengubah ukuran panel dalam presentasi terpisah.
  • Penyematan tumpukan aktivitas: Pengguna dapat menyematkan konten di satu penampung dan mengisolasi navigasi di penampung dari navigasi di penampung lainnya.
  • Peredupan layar penuh dialog: Saat menampilkan dialog, aplikasi dapat menentukan apakah akan meredupkan seluruh jendela tugas atau hanya penampung yang membuka dialog.

Perluasan panel

Perluasan panel memungkinkan pengguna menyesuaikan jumlah ruang layar yang dialokasikan untuk kedua aktivitas dalam tata letak panel ganda.

Untuk menyesuaikan tampilan pemisah jendela dan menetapkan rentang pemisah yang dapat ditarik, lakukan hal berikut:

  1. Membuat instance DividerAttributes

  2. Sesuaikan atribut pemisah:

    • color: Warna pemisah panel yang dapat ditarik.

    • widthDp: Lebar pemisah panel yang dapat ditarik. Tetapkan ke WIDTH_SYSTEM_DEFAULT untuk memungkinkan sistem menentukan lebar pemisah.

    • Rentang tarik: Persentase minimum layar yang dapat diduduki oleh panel. Dapat berkisar dari 0,33 hingga 0,66. Tetapkan ke DRAG_RANGE_SYSTEM_DEFAULT untuk memungkinkan sistem menentukan rentang tarik.

Kotlin

val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder()
    .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
    .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)

if (WindowSdkExtensions.getInstance().extensionVersion >= 6) {
    splitAttributesBuilder.setDividerAttributes(
      DividerAttributes.DraggableDividerAttributes.Builder()
        .setColor(getColor(context, R.color.divider_color))
        .setWidthDp(4)
        .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
        .build()
    )
}
val splitAttributes: SplitAttributes = splitAttributesBuilder.build()

Java

SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder()
    .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
    .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT);

if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) {
    splitAttributesBuilder.setDividerAttributes(
      new DividerAttributes.DraggableDividerAttributes.Builder()
        .setColor(ContextCompat.getColor(context, R.color.divider_color))
        .setWidthDp(4)
        .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT)
        .build()
    );
}
SplitAttributes splitAttributes = splitAttributesBuilder.build();

Penyematan data sebelumnya aktivitas

Penyematan tumpukan aktivitas memungkinkan pengguna menyematkan salah satu jendela terpisah sehingga aktivitas tetap seperti semula saat pengguna membuka-buka jendela lain. Penyematan stack aktivitas memberikan pengalaman multitasking yang ditingkatkan.

Untuk mengaktifkan penyematan stack aktivitas di aplikasi Anda, lakukan hal berikut:

  1. Tambahkan tombol ke file tata letak aktivitas yang ingin Anda sematkan, misalnya, aktivitas detail tata letak daftar-detail:

    <androidx.constraintlayout.widget.ConstraintLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/detailActivity"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@color/white"
     tools:context=".DetailActivity">
    
    <TextView
       android:id="@+id/textViewItemDetail"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textSize="36sp"
       android:textColor="@color/obsidian"
       app:layout_constraintBottom_toTopOf="@id/pinButton"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />
    
    <androidx.appcompat.widget.AppCompatButton
       android:id="@+id/pinButton"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/pin_this_activity"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. Dalam metode onCreate() aktivitas, tetapkan pemroses onclick pada tombol:

    Kotlin

    pinButton = findViewById(R.id.pinButton)
    pinButton.setOnClickListener {
        val splitAttributes: SplitAttributes = SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build()
    
        val pinSplitRule = SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build()
    
        SplitController.getInstance(applicationContext).pinTopActivityStack(taskId, pinSplitRule)
    }

    Java

    Button pinButton = findViewById(R.id.pinButton);
    pinButton.setOnClickListener( (view) => {
        SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.66f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();
    
        SplitPinRule pinSplitRule = new SplitPinRule.Builder()
            .setSticky(true)
            .setDefaultSplitAttributes(splitAttributes)
            .build();
    
        SplitController.getInstance(getApplicationContext()).pinTopActivityStack(getTaskId(), pinSplitRule);
    });

Peredupan dialog layar penuh

Aktivitas biasanya meredupkan layar untuk menarik perhatian ke dialog. Dalam penyematan aktivitas, kedua panel tampilan panel ganda harus meredup, bukan hanya panel yang berisi aktivitas yang membuka dialog, untuk pengalaman UI terpadu.

Dengan WindowManager 1.4 dan yang lebih baru, seluruh jendela aplikasi akan meredup secara default saat dialog terbuka (lihat EmbeddingConfiguration.DimAreaBehavior.ON_TASK).

Untuk meredupkan hanya penampung aktivitas yang membuka dialog, gunakan EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK.

Mengekstrak aktivitas dari pemisahan ke jendela penuh

Buat konfigurasi baru yang akan menampilkan jendela penuh aktivitas samping, lalu luncurkan kembali aktivitas dengan intent yang telah di-resolve ke instance yang sama.

Memeriksa dukungan pemisahan saat runtime

Penyematan aktivitas didukung di Android 12L (API level 32) dan yang lebih tinggi, tetapi juga tersedia di beberapa perangkat yang menjalankan versi platform yang lebih lama. Untuk memeriksa ketersediaan fitur pada runtime, gunakan properti SplitController.splitSupportStatus atau metode SplitController.getSplitSupportStatus():

Kotlin

if (SplitController.getInstance(this).splitSupportStatus ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

Java

if (SplitController.getInstance(this).getSplitSupportStatus() ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

Jika pemisahan tidak didukung, aktivitas akan diluncurkan di atas tumpukan aktivitas (dengan mengikuti model penyematan non-aktivitas).

Mencegah penggantian sistem

Produsen perangkat Android (pabrikan peralatan asli atau OEM), dapat mengimplementasikan penyematan aktivitas sebagai fungsi dari sistem perangkat. Sistem menentukan aturan terpisah untuk aplikasi multi-aktivitas, mengganti perilaku jendela aplikasi. Penggantian sistem memaksa aplikasi multi-aktivitas ke mode penyematan aktivitas yang ditentukan sistem.

Penyematan aktivitas sistem dapat meningkatkan penyajian aplikasi melalui tata letak multipanel, seperti daftar-detail, tanpa perubahan apa pun pada aplikasi. Namun, penyematan aktivitas sistem juga dapat menyebabkan kesalahan tata letak aplikasi, bug, atau konflik dengan penyematan aktivitas yang diimplementasikan oleh aplikasi.

Aplikasi Anda dapat mencegah atau mengizinkan penyematan aktivitas sistem dengan menetapkan properti di file manifes aplikasi, misalnya:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

Nama properti ditentukan di objek WindowProperties Jetpack WindowManager. Setel nilai ke false jika aplikasi Anda menerapkan penyematan aktivitas, atau jika Anda ingin mencegah sistem menerapkan aturan penyematan aktivitasnya ke aplikasi Anda. Setel nilai ke true untuk mengizinkan sistem menerapkan penyematan aktivitas yang ditentukan sistem ke aplikasi Anda.

Batasan, pembatasan, dan peringatan

  • Hanya aplikasi host tugas yang diidentifikasi sebagai pemilik aktivitas root dalam tugas yang dapat mengatur dan menyematkan aktivitas lain dalam tugas. Jika aktivitas yang mendukung penyematan dan pemisahan berjalan di tugas milik aplikasi lain, penyematan dan pemisahan untuk aktivitas tersebut tidak akan berfungsi.
  • Aktivitas hanya dapat diatur dalam satu tugas. Meluncurkan aktivitas dalam tugas baru akan selalu menempatkannya di jendela baru yang diperluas di luar pemisahan yang ada.
  • Hanya aktivitas dalam proses sama yang dapat diatur dan dipisahkan. Callback SplitInfo hanya melaporkan aktivitas yang termasuk dalam proses yang sama, karena tidak ada cara untuk mengetahui aktivitas dalam proses yang berbeda.
  • Setiap aturan aktivitas yang berpasangan atau tunggal hanya berlaku untuk peluncuran aktivitas yang terjadi setelah aturan didaftarkan. Saat ini, tidak ada cara untuk memperbarui pemisahan yang sudah ada ataupun properti visualnya.
  • Konfigurasi filter pasangan pemisahan harus cocok dengan intent yang digunakan saat meluncurkan seluruh aktivitas. Pencocokan terjadi pada saat aktivitas baru dimulai dari proses aplikasi sehingga mungkin tidak mengetahui nama komponen yang akan diselesaikan dalam proses sistem nantinya saat menggunakan intent implisit. Jika nama komponen tidak diketahui pada saat peluncuran, karakter pengganti dapat digunakan ("*/*") dan pemfilteran dapat dilakukan berdasarkan tindakan intent.
  • Saat ini, tidak ada cara untuk memindahkan aktivitas antara penampung atau keluar-masuk pemisahan setelah dibuat. Pemisahan hanya dibuat oleh library WindowManager saat aktivitas baru dengan aturan yang cocok diluncurkan, dan pemisahan akan dihancurkan saat aktivitas terakhir dalam penampung terpisah telah selesai.
  • Aktivitas dapat diluncurkan ulang ketika konfigurasi berubah sehingga saat pemisahan dibuat atau dihapus dan batas aktivitas berubah, aktivitas dapat melewati penghancuran total instance sebelumnya dan pembuatan instance baru. Oleh karena itu, developer aplikasi harus berhati-hati dengan hal-hal seperti meluncurkan aktivitas baru dari callback siklus proses.
  • Perangkat harus menyertakan antarmuka ekstensi jendela untuk mendukung penyematan aktivitas. Hampir semua perangkat layar besar yang menjalankan Android 12L (API level 32) atau yang lebih tinggi menyertakan antarmuka. Namun, beberapa perangkat layar besar yang tidak dapat menjalankan beberapa aktivitas tidak menyertakan antarmuka ekstensi jendela. Jika perangkat layar besar tidak mendukung mode multi-aplikasi, perangkat tersebut mungkin tidak mendukung penyematan aktivitas.

Referensi lainnya