Belajar Membuat Widget Di Android


Pada artikel Membuat Aplikasi Android Dengan Groovy, saya menunjukkan cara membuat activity. Pada artikel Belajar Membuat Service Di Android, saya membuat sebuah service. Kali ini, saya akan mencoba membuat sebuah bentuk aplikasi Android yang disebut sebagai widget. Sama seperti activity, widget juga memiliki UI. Hanya saja UI pada widget biasanya terbatas karena ia ditujukan untuk disertakan di dalam aplikasi lain yang disebut sebagai widget host. Salah satu contoh widget host adalah home launcher bawaan Android atau pihak ketiga seperti Samsung TouchWiz.

Sebagai latihan, saya akan membuat sebuah widget sederhana yang akan menyalakan atau mematikan lampu flash bila di-klik. Dengan demikian, saya dapat memanfaatkan perangkat saya sebagai sebuah senter. Untuk itu, saya akan mulai dengan membuat sebuah proyek baru di Android Studio dengan nama MySenter. Seperti biasa, saya memilih Add No Activity pada saat membuat proyek baru. Setelah itu, saya men-klik kanan package com.snake.mysenter dan memilih menu New, Widget, App Widget seperti pada gambar berikut ini:

Membuat Widget Baru di Android Studio

Membuat Widget Baru di Android Studio

Saya mengisi dialog yang muncul seperti pada gambar berikut ini:

Membuat Widget Baru di Android Studio

Membuat Widget Baru di Android Studio

Saya mengisi nilai Minimum Width dan Minimum Height dengan 1 sel karena widget ini hanya sebuah tombol sederhana. Saya memilih Not Resizable agar widget ini selalu berukuran 1×1 sel. Seusai men-klik tombol Finish, Android Studio akan menambahkan sebuah <receiver> pada AndroidManifest.xml berupa:

<receiver android:name=".MySenter" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/my_senter_info" />
</receiver>

Pada XML di atas, terdapat <meta-data> yang berisi referensi ke my_senter_info.xml. Isi my_senter_info.xml yang dihasilkan oleh Android Studio akan terlihat seperti berikut ini:

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp" android:minHeight="40dp" android:updatePeriodMillis="86400000"
    android:previewImage="@drawable/example_appwidget_preview"
    android:initialLayout="@layout/my_senter" android:widgetCategory="home_screen|keyguard"
    android:initialKeyguardLayout="@layout/my_senter"></appwidget-provider>

Karena saya tidak perlu memperbaharui widget secara periodik, saya bisa mengubah nilai android:updatePeriodMillis menjadi "0".

Nilai android:initialLayout berisi referensi ke XML yang mendefinisikan layout untuk widget ini. Android Studio sudah membuatkan sebuah layout dengan nama my_senter yang berisi sebuah TextView. Sebagai latihan, saya akan mengubah layout tersebut sehingga hanya berisi sebuah ImageButton seperti pada:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent"
    android:padding="@dimen/widget_margin" android:background="@null">

    <ImageButton android:layout_width="fill_parent" android:layout_height="fill_parent"
        android:background="@null" android:id="@+id/tombol"
        android:contentDescription="Senter" android:src="@drawable/senter_mati"
        android:layout_alignParentStart="true" />

</RelativeLayout>

Saya perlu menambahkan dua gambar dengan nama senter_aktif.png dan senter_mati.png pada directory drawable yang mewakili tampilan widget. Karena ini hanya latihan, saya akan menggunakan gambar lingkaran berwarna kuning dan abu-abu. Untuk memakai latar belakang transparan, saya mengisi nilai android:background dengan @null. Agar hasil preview widget konsisten dengan tampilan widget, saya juga perlu mengubah nilai android:previewImage di my_senter_info.xml menjadi "@drawable/senter_mati".

Sekarang, saya perlu memberikan event handler yang akan dikerjakan bila ImageButton di-klik oleh pengguna. Untuk itu saya perlu melakukan perubahan pada class MySenter (sebuah turunan dari AppWidgetProvider) sehingga isinya menjadi seperti berikut ini:

package com.snake.mysenter;

import ...;

public class MySenter extends AppWidgetProvider {

    public static final String TOGGLE_SENTER = "TOGGLE_SENTER";

    public static boolean NYALA = false;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        for (int id : appWidgetIds) {
            Intent toggleIntent = new Intent(context, MySenter.class);
            toggleIntent.setAction(TOGGLE_SENTER);
            PendingIntent pendingToggleIntent = PendingIntent.getBroadcast(context, 0,
                    toggleIntent, 0);
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_senter);
            views.setOnClickPendingIntent(R.id.tombol, pendingToggleIntent);
            appWidgetManager.updateAppWidget(id, views);
        }
    }

    @Override
    public void onReceive(@NonNull Context context, @NonNull Intent intent) {
        super.onReceive(context, intent);
        if (intent.getAction().equals(TOGGLE_SENTER)) {
            NYALA = !NYALA;
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_senter);
            int iconBaru = NYALA ? R.drawable.senter_aktif: R.drawable.senter_mati;
            if (NYALA) {
                nyalakanSenter();
            } else {
                matikanSenter();
            }
            views.setInt(R.id.tombol, "setImageResource", iconBaru);
            appWidgetManager.updateAppWidget(new ComponentName(context, MySenter.class), views);
        }
    }

    public void nyalakanSenter() {

    }

    public void matikanSenter() {

    }

}

Menangani aksi untuk sebuah tombol di widget lebih rumit daripada di activity. Hal ini karena tombol di layar dapat langsung di-klik tanpa sebuah activity yang melekat padanya sehingga saya harus menggunakan PendingIntent. Selain itu, pengguna juga bisa menambahkan beberapa widget yang sama di dalam widget host yang berbeda (maupun sama) sehingga saya perlu menggunakan RemoteViews. Untuk menambahkan aksi yang akan dikerjakan pada saat tombol di-klik, saya menggunakan method setOnClickPendingIntent() milik RemoteViews. PendingIntent yang saya lewatkan akan melakukan broadcast yang dapat diterima melalui onReceive() (sebuah AppWidgetProvider adalah sebuah BroadcastReceiver!).

AppWidgetProvider sudah men-override method onReceive() untuk menangani broadcast penting bagi widget. Karena saya menambahkan broadcast baru saat tombol di-klik, maka saya perlu men-override onReceive() sambil tetap menyertakan super.onReceive(). Pada method ini, saya menyimpan status senter pada sebuah variabel statis bernama NYALA yang di-share bersama untuk seluruh ‘instance’ dari widget ini. Untuk mengubah icon dari warna kuning menjadi abu-abu, saya menggunakan RemoteViews.setInt() dengan melewatkan nama method berupa "setImageResource".

Untuk mengaktifkan lampu flash sebagai senter, pada dasarnya saya akan mengaktifkan preview kamera sambil menyalakan LED flash. Hasil preview tidak perlu ditampilkan pada UI karena saya hanya membutuhkan lampu flash saja. Bila preview kamera ini saya tutup, maka lampu flash juga akan ikut mati.

Karena memakai kamera, saya perlu menambahkan permission berikut ini di AndroidManifest.xml:

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

Setelah itu, saya menambahkan kode program seperti berikut ini:

public class MySenter extends AppWidgetProvider {

  public static Camera camera;

  ...

  public void nyalakanSenter() {
    if (camera==null) {
        camera = Camera.open();
        Camera.Parameters params = camera.getParameters();
        params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
        camera.setParameters(params);
        camera.startPreview();
    }
  }

  public void matikanSenter() {
    if (camera!=null) {
        camera.stopPreview();
        camera.release();
        camera = null;
    }
  }

}

Pada kode program di atas, saya menyimpan instance Camera sebagai sebuah variabel statis. Menyimpan state pada BroadcastReceiver bukanlah sesuatu yang disarankan, misalnya state pada BroadcastReceiver tidak thread-safe. Untuk sesuatu yang lebih aman (namun lebih kompleks), saya perlu membuat sebuah service dan memanggilnya. Selain itu, kode program di atas hanya berlaku untuk perangkat yang mendukung Parameters.FLASH_MODE_TORCH dimana LED flash dapat diaktifkan selama melakukan preview atau merekam video.

Untuk menjalankan aplikasi ini, saya perlu men-edit konfigurasi app dan memilih Do not launch Activity seperti pada gambar berikut ini:

Menjalankan Widget Dari Android Studio

Menjalankan Widget Dari Android Studio

Setelah aplikasi berhasil di-deploy pada perangkat, saya perlu men-install widget pada sebuah widget host. Sebagai contoh, saya bisa menyisipkan widget pada home launcher. Langkah ini bisa berbeda karena setiap perangkat dari produsen berbeda biasanya juga memiliki home launcher yang berbeda. Pada Samsung TouchWiz, saya dapat melakukan ini dengan menahan agak lama pada home launcher sehingga muncul menu Widget seperti pada gambar berikut ini:

Memilih menu Widget untuk men-install widget

Memilih menu Widget untuk men-install widget

Setelah itu, saya dapat memilih Widget seperti pada gambar berikut ini:

Mencari widget yang hendak di-install

Mencari widget yang hendak di-install

Perhatikan bawah gambar yang dipakai disini adalah nilai dari android:previewImage. Setelah men-drag widget ke home launcher, saya dapat menyentuh widget tersebut untuk menyalakan LED flash kamera belakang:

Menyalakan senter dengan menyentuh tombol di widget

Menyalakan senter dengan menyentuh tombol di widget

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: