Mem-build Host Widget Aplikasi

Layar utama Android yang tersedia di sebagian besar perangkat Android memungkinkan pengguna menyematkan widget aplikasi untuk akses cepat ke konten. Jika mem-build pengganti Layar utama atau aplikasi serupa, Anda juga dapat mengizinkan pengguna menyematkan widget aplikasi dengan menerapkan AppWidgetHost. Tidak semua aplikasi perlu melakukan hal ini, tetapi jika Anda membuat host sendiri, sebaiknya pahami kewajiban kontraktual yang disetujui host secara implisit.

Dokumen ini berfokus pada tanggung jawab yang termasuk dalam penerapan AppWidgetHost kustom. Untuk contoh cara menerapkan AppWidgetHost, lihat kode sumber untuk Peluncur Layar utama Android.

Berikut adalah ringkasan class dan konsep utama yang termasuk dalam penerapan AppWidgetHost kustom:

  • Host Widget AplikasiAppWidgetHost menyediakan interaksi dengan layanan AppWidget untuk aplikasi, seperti layar utama, yang ingin menyematkan widget aplikasi dalam UI-nya. AppWidgetHost harus memiliki ID yang unik dalam paket host sendiri. ID ini akan dipertahankan di seluruh penggunaan host. ID biasanya berupa nilai hard code yang Anda tetapkan di aplikasi.
  • ID Widget Aplikasi—Setiap instance widget aplikasi diberi ID unik pada saat binding (lihat bindAppWidgetIdIfAllowed(), yang dibahas secara mendetail dalam Melakukan binding widget aplikasi). Host mendapatkan ID unik menggunakan allocateAppWidgetId(). ID ini akan tetap ada selama masa aktif widget, yaitu hingga widget dihapus dari host. Semua status khusus host (seperti ukuran dan lokasi widget) harus dipertahankan oleh paket hosting dan dikaitkan dengan ID widget aplikasi.
  • Tampilan Host Widget AplikasiAppWidgetHostView dapat dilihat sebagai bingkai yang menggabungkan widget setiap kali perlu ditampilkan. Widget aplikasi ditetapkan ke AppWidgetHostView setiap kali widget di-inflate oleh host.
  • Paket OpsiAppWidgetHost menggunakan paket opsi untuk menyampaikan informasi ke AppWidgetProvider tentang cara widget ditampilkan (misalnya, rentang ukuran, dan apakah widget berada di layar kunci atau layar utama). Informasi ini memungkinkan AppWidgetProvider menyesuaikan konten dan tampilan widget berdasarkan cara dan lokasi widget ditampilkan. Anda dapat menggunakan updateAppWidgetOptions() dan updateAppWidgetSize() untuk mengubah paket widget aplikasi. Kedua metode ini memicu callback ke AppWidgetProvider.

Melakukan Binding Widget Aplikasi

Saat pengguna menambahkan widget aplikasi ke host, akan terjadi proses yang disebut binding. Binding mengacu pada pengaitan ID widget aplikasi ke host tertentu, dan ke AppWidgetProvider tertentu. Ada berbagai cara untuk melakukan binding, bergantung pada versi Android yang digunakan untuk menjalankan aplikasi Anda.

Melakukan binding widget aplikasi di Android 4.0 dan yang lebih rendah

Pada perangkat yang menjalankan Android versi 4.0 dan yang lebih rendah, pengguna menambahkan widget aplikasi melalui aktivitas sistem yang memungkinkan mereka memilih widget. Secara implisit, tindakan ini melakukan pemeriksaan izin—yaitu, dengan menambahkan widget aplikasi, pengguna memberikan izin secara implisit ke aplikasi Anda untuk menambahkan widget ke host. Berikut adalah contoh yang menggambarkan pendekatan ini, yang diambil dari Peluncur asli. Dalam cuplikan ini, pengendali peristiwa memanggil startActivityForResult() dengan kode permintaan REQUEST_PICK_APPWIDGET sebagai respons terhadap tindakan pengguna:

Kotlin

val REQUEST_CREATE_APPWIDGET = 5
val REQUEST_PICK_APPWIDGET = 9
...
override fun onClick(dialog: DialogInterface?, which: Int) {
    when (which) {
        ...
        AddAdapter.ITEM_APPWIDGET -> {
            ...
            val appWidgetId: Int = appWidgetHost.allocateAppWidgetId()
            val pickIntent = Intent(AppWidgetManager.ACTION_APPWIDGET_PICK).apply {
                putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            }
            ...
            startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET)
        }
        ...
    }
}

Java

private static final int REQUEST_CREATE_APPWIDGET = 5;
private static final int REQUEST_PICK_APPWIDGET = 9;
...
public void onClick(DialogInterface dialog, int which) {
    switch (which) {
    ...
        case AddAdapter.ITEM_APPWIDGET: {
            ...
            int appWidgetId =
                    Launcher.this.appWidgetHost.allocateAppWidgetId();
            Intent pickIntent =
                    new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
            pickIntent.putExtra
                    (AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
            ...
            startActivityForResult(pickIntent, REQUEST_PICK_APPWIDGET);
            break;
    }
    ...
}

Ketika aktivitas sistem selesai, sistem akan menampilkan hasil dengan widget aplikasi yang dipilih pengguna ke aktivitas Anda. Dalam contoh berikut, aktivitas merespons dengan memanggil addAppWidget() untuk menambahkan widget aplikasi:

Kotlin

class Launcher : Activity(), View.OnClickListener, View.OnLongClickListener {
    ...
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
        waitingFroResult = false

        if (resultCode == RESULT_OK && addItemCellInfo != null) {
            when (requestCode) {
                ...
                REQUEST_PICK_APPWIDGET -> addAppWidget(data)
                REQUEST_CREATE_APPWIDGET ->
                    completeAddAppWidget(data, addItemCellInfo, !desktopLocked)
                ...
            }
        }
        ...
    }
}

Java

public final class Launcher extends Activity
        implements View.OnClickListener, OnLongClickListener {
    ...
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        waitingForResult = false;

        if (resultCode == RESULT_OK && addItemCellInfo != null) {
            switch (requestCode) {
                ...
                case REQUEST_PICK_APPWIDGET:
                    addAppWidget(data);
                    break;
                case REQUEST_CREATE_APPWIDGET:
                    completeAddAppWidget(data, addItemCellInfo, !desktopLocked);
                    break;
                }
        }
        ...
    }
}

Metode addAppWidget() akan memeriksa untuk menentukan apakah widget aplikasi perlu dikonfigurasi sebelum ditambahkan:

Kotlin

fun addAppWidget(data: Intent?) {
    if (data != null) {
        val appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)

        val customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET)
        val appWidget: AppWidgetProviderInfo? = appWidgetManager.getAppWidgetInfo(appWidgetId)

        appWidget?.configure?.apply {
            // Launch over to configure widget, if needed.
            val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE)
            intent.component = this
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
            startActivityForResult(intent, REQUEST_CREATE_APPWIDGET)
        } ?: run {
            // Otherwise, finish adding the widget.
        }
    }
}

Java

void addAppWidget(Intent data) {
    int appWidgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);

    String customWidget = data.getStringExtra(EXTRA_CUSTOM_WIDGET);
    AppWidgetProviderInfo appWidget =
            appWidgetManager.getAppWidgetInfo(appWidgetId);

    if (appWidget.configure != null) {
        // Launch over to configure widget, if needed.
        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
        intent.setComponent(appWidget.configure);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        startActivityForResult(intent, REQUEST_CREATE_APPWIDGET);
    } else {
        // Otherwise, finish adding the widget.
    }
}

Untuk pembahasan selengkapnya tentang konfigurasi, lihat Membuat Aktivitas Konfigurasi Widget Aplikasi.

Setelah widget aplikasi siap, langkah selanjutnya adalah menambahkannya ke ruang kerja. Peluncur asli menggunakan metode yang disebut completeAddAppWidget() untuk melakukannya.

Melakukan binding widget aplikasi di Android 4.1 dan yang lebih tinggi

Android 4.1 menambahkan API untuk proses binding yang lebih sederhana. API ini juga memungkinkan host menyediakan UI kustom untuk binding. Untuk menggunakan proses yang telah diperbaiki ini, aplikasi Anda harus menyatakan izin BIND_APPWIDGET dalam manifesnya:

<uses-permission android:name="android.permission.BIND_APPWIDGET" />

Namun, ini hanyalah langkah pertama. Pada waktu proses, pengguna harus memberikan izin ke aplikasi secara eksplisit untuk mengizinkannya menambahkan widget aplikasi ke host. Untuk menguji apakah aplikasi memiliki izin untuk menambahkan widget, Anda dapat menggunakan metode bindAppWidgetIdIfAllowed(). Jika bindAppWidgetIdIfAllowed() menampilkan false, aplikasi Anda harus menampilkan dialog yang meminta pengguna untuk memberikan izin ("izinkan" atau "selalu izinkan", untuk mencakup semua penambahan widget aplikasi selanjutnya). Cuplikan berikut menunjukkan contoh cara menampilkan dialog:

Kotlin

val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_BIND).apply {
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName)
    // This is the options bundle discussed above
    putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options)
}
startActivityForResult(intent, REQUEST_BIND_APPWIDGET)

Java

Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
// This is the options bundle discussed above
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);

Host juga harus memeriksa apakah pengguna menambahkan widget aplikasi yang memerlukan konfigurasi. Untuk pembahasan selengkapnya tentang topik ini, baca Membuat Aktivitas Konfigurasi Widget Aplikasi.

Tanggung Jawab Host

Developer widget dapat menentukan jumlah setelan konfigurasi untuk widget menggunakan metadata AppWidgetProviderInfo. Opsi konfigurasi tersebut, yang dibahas secara mendetail di bawah, dapat diambil oleh host dari objek AppWidgetProviderInfo yang terkait dengan penyedia widget.

Terlepas dari versi Android yang Anda targetkan, semua host memiliki tanggung jawab berikut:

  • Saat menambahkan widget, Anda harus mengalokasikan ID widget seperti yang dijelaskan di atas. Pastikan juga saat widget dihapus dari host, Anda memanggil deleteAppWidgetId() untuk menghapus alokasi ID widget.
  • Saat menambahkan widget, pastikan Anda meluncurkan aktivitas konfigurasinya jika ada, seperti yang dijelaskan dalam Memperbarui Widget Aplikasi dari Aktivitas Konfigurasi. Langkah ini perlu dilakukan agar widget aplikasi dapat ditampilkan dengan benar.
  • Setiap widget aplikasi menentukan tinggi dan lebar minimum dalam dp, seperti yang ditetapkan dalam metadata AppWidgetProviderInfo (menggunakan android:minWidth dan android:minHeight). Pastikan tata letak widget setidaknya sesuai dengan ukuran dalam dp ini. Misalnya, sebagian besar host meratakan ikon dan widget dalam formasi petak. Dalam skenario ini, secara default host harus menambahkan widget aplikasi menggunakan jumlah sel minimum yang memenuhi batasan minWidth dan minHeight.

Selain persyaratan yang tercantum di atas, versi platform tertentu memperkenalkan fitur yang memberikan tanggung jawab baru kepada host.

Versi Mana yang Anda Targetkan?

Pendekatan yang Anda gunakan dalam menerapkan host sebaiknya didasarkan pada versi Android yang Anda targetkan. Banyak fitur yang dijelaskan dalam bagian ini diperkenalkan di versi 3.0 atau yang lebih baru. Contoh:

  • Android 3.0 (API Level 11) memperkenalkan perilaku maju otomatis untuk widget.
  • Android 3.1 (API Level 12) memperkenalkan opsi untuk mengubah ukuran widget.
  • Android 4.0 (API Level 15) memperkenalkan perubahan pada kebijakan padding yang memberikan tanggung jawab kepada host untuk mengelola padding.
  • Android 4.1 (API Level 16) menambahkan API yang memungkinkan penyedia widget mendapatkan informasi yang lebih detail tentang lingkungan tempat instance widgetnya dihosting.
  • Android 4.2 (API Level 17) memperkenalkan paket opsi dan metode bindAppWidgetIdIfAllowed(). Versi ini juga memperkenalkan widget layar kunci.

Jika Anda menargetkan perangkat versi lama, lihat Peluncur asli sebagai contoh.

Bagian berikut akan menjelaskan detail selengkapnya tentang fitur yang memberikan tanggung jawab baru kepada host.

Android 3.0

Android 3.0 (API Level 11) memperkenalkan opsi bagi widget untuk menentukan autoAdvanceViewId(). ID tampilan ini harus mengarah ke instance Advanceable, seperti StackView atau AdapterViewFlipper. Hal ini menunjukkan bahwa host harus memanggil advance() di tampilan ini pada interval yang dianggap sesuai oleh host (dengan mempertimbangkan apakah akan lebih baik jika widget dimajukan—misalnya, host mungkin tidak ingin memajukan widget jika berada di halaman lain, atau jika layar dimatikan).

Android 3.1

Android 3.1 (API Level 12) memperkenalkan opsi untuk mengubah ukuran widget. Widget dapat menentukan apakah ukurannya dapat diubah menggunakan atribut android:resizeMode dalam metadata AppWidgetProviderInfo, dan menunjukkan apakah widget mendukung pengubahan ukuran secara horizontal dan/atau vertikal atau tidak. Seperti yang diperkenalkan di Android 4.0 (API Level 14), widget juga dapat menentukan android:minResizeWidth dan/atau android:minResizeHeight.

Host bertanggung jawab untuk memastikan ukuran widget dapat diubah secara horizontal dan/atau vertikal, seperti yang ditentukan oleh widget. Jika widget menentukan bahwa ukurannya dapat diubah, widget dapat diubah ukurannya secara bebas, tetapi tidak boleh lebih kecil dari nilai yang ditentukan oleh android:minResizeWidth dan android:minResizeHeight. Untuk contoh implementasi, lihat AppWidgetResizeFrame dalam Launcher2.

Android 4.0

Android 4.0 (API Level 15) memperkenalkan perubahan pada kebijakan padding yang memberikan tanggung jawab kepada host untuk mengelola padding. Mulai versi 4.0, widget aplikasi tidak lagi menyertakan padding. Sebagai gantinya, sistem menambahkan padding untuk setiap widget, berdasarkan karakteristik layar yang sedang aktif. Hal ini akan menciptakan penyajian widget yang lebih seragam dan konsisten dalam formasi petak. Untuk membantu aplikasi yang menghosting widget aplikasi, platform menyediakan metode getDefaultPaddingForWidget(). Aplikasi dapat memanggil metode ini untuk mendapatkan padding yang ditentukan oleh sistem dan memperhitungkannya saat menghitung jumlah sel yang akan dialokasikan ke widget.

Android 4.1

Android 4.1 (API Level 16) menambahkan API yang memungkinkan penyedia widget mendapatkan informasi yang lebih detail tentang lingkungan tempat instance widgetnya dihosting. Secara khusus, host memberikan petunjuk kepada penyedia widget tentang ukuran widget saat ditampilkan. Host bertanggung jawab untuk memberikan informasi ukuran ini.

Host memberikan informasi ini melalui updateAppWidgetSize(). Ukuran ditetapkan sebagai lebar/tinggi minimum dan maksimum dalam satuan dp. Alasan ditetapkannya rentang ukuran (bukan ukuran tetap) adalah karena lebar dan tinggi widget dapat berubah berdasarkan orientasi. Sebaiknya Anda tidak mewajibkan host memperbarui semua widgetnya secara bergantian, karena hal ini dapat menyebabkan pelambatan sistem yang serius. Nilai tersebut harus diperbarui setelah widget ditempatkan, setiap kali ukuran widget diubah, dan setiap kali peluncur meng-inflate widget untuk pertama kalinya dalam boot tertentu (karena nilai tidak dipertahankan di seluruh boot).

Android 4.2

Android 4.2 (API Level 17) menambahkan opsi untuk menentukan paket opsi pada waktu binding. Ini adalah cara yang ideal untuk menentukan opsi widget aplikasi, termasuk ukuran, karena memberi AppWidgetProvider akses langsung ke data opsi pada update pertama. Hal ini dapat dilakukan menggunakan metode bindAppWidgetIdIfAllowed(). Untuk pembahasan topik ini lebih lanjut, baca Melakukan binding widget aplikasi.

Android 4.2 juga memperkenalkan widget layar kunci. Saat menghosting widget di layar kunci, host harus menentukan informasi ini dalam paket opsi widget aplikasi (AppWidgetProvider dapat menggunakan informasi ini untuk menyesuaikan gaya widget dengan tepat). Untuk menentukan widget sebagai widget layar kunci, gunakan updateAppWidgetOptions() dan sertakan kolom OPTION_APPWIDGET_HOST_CATEGORY dengan nilai WIDGET_CATEGORY_KEYGUARD. Opsi ini ditetapkan secara default ke WIDGET_CATEGORY_HOME_SCREEN, sehingga secara eksplisit tidak perlu ditetapkan untuk host layar utama.

Pastikan host hanya menambahkan widget aplikasi yang sesuai untuk aplikasi Anda—misalnya, jika host adalah layar utama, pastikan atribut android:widgetCategory dalam metadata AppWidgetProviderInfo menyertakan flag WIDGET_CATEGORY_HOME_SCREEN. Demikian pula, untuk layar kunci, pastikan kolom tersebut menyertakan flag WIDGET_CATEGORY_KEYGUARD. Untuk pembahasan topik ini lebih lanjut, baca Mengaktifkan Widget Aplikasi di Layar Kunci.