Menambahkan Nearby Messages ke aplikasi

Bayangkan Anda berada di mal yang ramai dan melihat teman Anda, Juwita. Anda mungkin akan cenderung melambaikan tangan dan berteriak sambil mencoba mendapatkan perhatian Juwita. Nearby Messages API dari Google memungkinkan aplikasi Anda melakukan teriakan yang menarik perhatian untuk pengguna, sehingga pengguna dan temannya dapat saling bertemu dengan mudah saat berada dalam jarak fisik yang dekat. Codelab ini mengajarkan cara menggunakan Nearby Messages API untuk memungkinkan interaksi pengguna berdasarkan kedekatan fisik. Agar codelab tetap sederhana, setiap pengguna akan mengungkapkan model build ponselnya: android.os.Build.MODEL. Namun, dalam kenyataannya, Anda dapat meminta setiap pengguna mengungkapkan userId miliknya, atau informasi lain yang sesuai dengan kasus penggunaan Anda, kepada teman-temannya di sekitar. Nearby Messages API menggabungkan konektivitas internet, Bluetooth, dan teknologi lainnya untuk menghadirkan fitur ini.

Prasyarat

  • Pengetahuan dasar tentang pengembangan Kotlin dan Android
  • Cara membuat dan menjalankan aplikasi di Android Studio
  • Dua perangkat Android atau lebih untuk menjalankan dan menguji kode

Yang akan Anda pelajari

  • Cara menambahkan library Nearby ke aplikasi
  • Cara menyiarkan pesan ke pihak yang berminat
  • Cara mendeteksi pesan dari lokasi minat
  • Cara mendapatkan Kunci API untuk pesan
  • Praktik terbaik untuk masa pakai baterai

Yang akan Anda butuhkan

  • Akun Google (yaitu alamat Gmail) untuk mendapatkan Kunci Google API
  • Versi terbaru Android Studio
  • Dua perangkat Android yang telah menginstal Layanan Google Play (dengan kata lain, Play Store)
  • Koneksi internet (Tidak seperti Nearby Connections API yang tidak memerlukannya)

Yang akan Anda build

Satu aplikasi Activity yang memungkinkan pengguna memberitahukan informasi perangkat dan menerima informasi tentang perangkat di sekitar. Aplikasi memiliki dua tombol yang dapat dialihkan oleh pengguna: tombol pertama adalah untuk menemukan atau menghentikan penemuan pesan di sekitar; tombol kedua adalah untuk memublikasikan atau membatalkan publikasi pesan. Untuk aplikasi ini, kita ingin publikasi dan penemuan berhenti setelah sekitar 120 detik. Untuk itu, kita akan menggali sedikit lebih dalam tentang API, membuat objek PublishOptions dan SubscribeOptions, serta menggunakan callback onExpired() untuk menonaktifkan tombol UI publikasikan dan berlangganan.

56bd91ffed49ec3d.png

  1. Mulai project Android Studio baru.
  2. Pilih Empty Activity.

f2936f15aa940a21.png

  1. Beri nama project Nearby Messages Example dan setel bahasa ke Kotlin.

3220c65e598bf6af.png

  1. Tambahkan versi terbaru dependensi Nearby ke dalam file build.gradle tingkat aplikasi Anda. Penambahan ini memungkinkan Anda menggunakan Nearby Messages API untuk mengirim dan mendeteksi pesan dari perangkat di sekitar.
implementation 'com.google.android.gms:play-services-nearby:18.0.0'
  1. Tetapkan opsi build viewBinding ke true dalam blok android untuk mengaktifkan ViewBinding.
android {
   ...
   buildFeatures {
       viewBinding true
   }
}
  1. Klik Sync Now atau tombol palu hijau untuk mengaktifkan Android Studio guna mendaftarkan perubahan Gradle ini.

57995716c771d511.png

  1. Tambahkan tombol beralih "Discover nearby devices" dan "Share device information" serta RecycleView yang akan berisi daftar perangkat. Di file activity_main.xml, ganti kode dengan kode berikut.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/activity_main_container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical"
   android:padding="16dp"
   tools:context=".MainActivity">

   <androidx.appcompat.widget.SwitchCompat
       android:id="@+id/subscribe_switch"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="Discover nearby devices" />

   <androidx.appcompat.widget.SwitchCompat
       android:id="@+id/publish_switch"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="Share device information" />

   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/nearby_msg_recycler_view"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:transcriptMode="alwaysScroll" />
</LinearLayout>

Catatan: Di project Anda sendiri, ubah nilai seperti 16dp ke resource seperti @dimen/activity_vertical_margin.

Menentukan variabel

  1. Di dalam aktivitas utama (MainActivity.kt), di atas fungsi onCreate(), tentukan variabel berikut dengan menempelkan cuplikan kode ini.
/**
* For accessing layout variables
*/
private lateinit var binding: ActivityMainBinding

/**
* Sets the time to live in seconds for the publish or subscribe.
*/
private val TTL_IN_SECONDS = 120 // Two minutes.

/**
* Choose of strategies for publishing or subscribing for nearby messages.
*/
private val PUB_SUB_STRATEGY = Strategy.Builder().setTtlSeconds(TTL_IN_SECONDS).build()

/**
* The [Message] object used to broadcast information about the device to nearby devices.
*/
private lateinit var message: Message

/**
* A [MessageListener] for processing messages from nearby devices.
*/
private lateinit var messageListener: MessageListener

/**
* MessageAdapter is a custom class that we will define later. It's for adding
* [messages][Message] to the [RecyclerView]
*/
private lateinit var msgAdapter: MessageAdapter

Kita menentukan Strategy karena ingin menyesuaikan seberapa lama siaran harus berlangsung. Untuk codelab ini, kita akan memilih 120 detik. Jika Anda tidak menentukan strategi, API akan menggunakan setelan default untuk Anda. Selain itu, meskipun kita menggunakan Strategy yang sama untuk publikasi dan langganan dalam codelab ini, Anda tidak diharuskan melakukannya.

  1. Ubah fungsi onCreate() untuk meneruskan objek ViewBinding ke setContentView(). Ini akan menampilkan isi file tata letak activity_main.xml.
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   binding = ActivityMainBinding.inflate(layoutInflater)
   setContentView(binding.root)
}

Menghubungkan tombol UI

Aplikasi ini akan melakukan tiga hal: memublikasikan pesan dengan cepat, menemukan pesan dengan cepat, dan menampilkan pesan dalam RecyclerView.

  1. Kita ingin pengguna memublikasikan dan membatalkan publikasi pesan serta menemukan (yaitu berlangganan) pesan. Untuk saat ini, buat metode stub untuk tindakan ini, yang disebut publish(), unpublish(), subscribe(), unsubscribe(). Kita akan membuat implementasi tersebut pada langkah berikutnya.
private fun publish() {
   TODO("Not yet implemented")
}

private fun unpublish() {
   TODO("Not yet implemented")
}

private fun subscribe() {
   TODO("Not yet implemented")
}

private fun unsubscribe() {
   TODO("Not yet implemented")
}
  1. Pengguna dapat memublikasikan atau menemukan (yaitu berlangganan) pesan menggunakan tombol yang ditambahkan ke tata letak aktivitas. Hubungkan kedua Switches untuk memanggil metode yang telah kita tentukan di akhir fungsi onCreate().
   binding.subscribeSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
       if (isChecked) {
           subscribe()
       } else {
           unsubscribe()
       }
   }

   binding.publishSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
       if (isChecked) {
           publish()
       } else {
           unpublish()
       }
   }
  1. Setelah Anda menambahkan kode UI untuk memublikasikan dan menemukan pesan, siapkan RecyclerView untuk menampilkan dan menghapus pesan. RecyclerView akan menampilkan pesan yang sedang aktif dipublikasikan. Pelanggan akan memproses pesan. Ketika pesan ditemukan, pelanggan akan menambahkannya ke RecyclerView; saat pesan hilang, dengan kata lain penerbit berhenti memublikasikannya, pelanggan akan menghapusnya dari RecyclerView.
private fun setupMessagesDisplay() {
   msgAdapter = MessageAdapter()
   with(binding.nearbyMsgRecyclerView) {
       layoutManager = LinearLayoutManager(context)
       this.adapter = msgAdapter
   }
}

class MessageAdapter : RecyclerView.Adapter<MessageAdapter.MessageVH>() {
   private var itemsList: MutableList<String> = arrayListOf()

   override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageVH {
       return MessageVH(TextView(parent.context))
   }

   override fun onBindViewHolder(holder: MessageVH, position: Int) {
       holder.bind(getItem(position))
   }

   override fun getItemCount(): Int = itemsList.size

   private fun getItem(pos: Int): String? = if (itemsList.isEmpty()) null else itemsList[pos]

   fun addItem(item: String) {
       itemsList.add(item)
       notifyItemInserted(itemsList.size)
   }

   fun removeItem(item: String) {
       val pos = itemsList.indexOf(item)
       itemsList.remove(item)
       notifyItemRemoved(pos)
   }

   inner class MessageVH(private val tv: TextView) : RecyclerView.ViewHolder(tv) {
       fun bind(item: String?) {
           item?.let { tv.text = it }
       }
   }
}
  1. Di akhir fungsi onCreate(), tambahkan panggilan ke fungsi setupMessagesDisplay().
override fun onCreate(savedInstanceState: Bundle?) {
   ...
   setupMessagesDisplay()
}

Setelah UI siap, kita siap untuk mulai memublikasikan pesan agar ditemukan oleh perangkat lain di sekitar. Berikut adalah tampilan aplikasi Anda pada tahap ini:

56bd91ffed49ec3d.png

Menambahkan kode publikasi dan penemuan

  1. Untuk mengirim pesan, pertama-tama kita memerlukan objek Message. Karena ini adalah demo, kita hanya akan mengirimkan model perangkat. Tambahkan kode ini ke fungsi onCreate() untuk membuat pesan yang akan dikirim.
override fun onCreate(savedInstanceState: Bundle?) {
   ...

   // The message being published is simply the Build.MODEL of the device. But since the
   // Messages API is expecting a byte array, you must convert the data to a byte array.
   message = Message(Build.MODEL.toByteArray(Charset.forName("UTF-8")))

}
  1. Untuk memublikasikan Message yang dapat ditemukan oleh perangkat lain di sekitar, cukup panggil Nearby.getMessagesClient(activity).publish(message). Namun, sebaiknya buat objek PublishOptions Anda sendiri sebelum ke langkah berikutnya. Hal ini memungkinkan Anda menentukan Strategy kustom Anda sendiri dan memanfaatkan PublishCallback, yang akan memberi tahu saat masa berlaku pesan yang dipublikasikan telah berakhir. Dalam kode berikut, kita akan membuat opsi agar dapat menonaktifkan tombol untuk pengguna saat masa berlaku TTL yang dipublikasikan berakhir. Selanjutnya, kita meneruskan opsi saat memanggil publish(). Update fungsi publish() dengan kode berikut.
private fun publish() {
   val options = PublishOptions.Builder()
       .setStrategy(PUB_SUB_STRATEGY)
       .setCallback(object : PublishCallback() {
           override fun onExpired() {
               super.onExpired()
               // flick the switch off since the publishing has expired.
               // recall that we had set expiration time to 120 seconds
               // Use runOnUiThread to force the callback
               // to run on the UI thread
               runOnUiThread{
                   binding.publishSwitch.isChecked = false
               }
           }
       }).build()

   Nearby.getMessagesClient(this).publish(message, options)
}

Kode ini berjalan setiap kali pengguna MENGAKTIFKAN tombol publish.

  1. Publikasi memerlukan Message, sedangkan langganan memerlukan MessageListener. Namun, sebaiknya Anda membuat objek SubscribeOptions meskipun tidak diperlukan untuk membuat API berfungsi. Dengan membuat SubscriptionOption sendiri, Anda dapat menentukan, misalnya, berapa lama Anda ingin berada dalam mode penemuan.

Tambahkan kode MessageListener berikut ke fungsi onCreate(). Saat pesan terdeteksi, pemroses akan menambahkannya ke RecyclerView. Saat pesan hilang, pemroses akan menghapusnya dari RecyclerView.

messageListener = object : MessageListener() {
   override fun onFound(message: Message) {
       // Called when a new message is found.
       val msgBody = String(message.content)
       msgAdapter.addItem(msgBody)
   }

   override fun onLost(message: Message) {
       // Called when a message is no longer detectable nearby.
       val msgBody = String(message.content)
       msgAdapter.removeItem(msgBody)
   }
}
  1. Secara teknis, pelanggan tidak memerlukan TTL (atau dapat menyetel TTL ke tak terhingga). Namun, dalam codelab ini, kita ingin menghentikan penemuan setelah 120 detik. Dengan demikian, kita akan membuat SubscribeOptions kita sendiri dan menggunakan callback onExpired() untuk menonaktifkan Switch UI berlangganan. Update fungsi berlangganan dengan kode ini.
private fun subscribe() {
   val options = SubscribeOptions.Builder()
       .setStrategy(PUB_SUB_STRATEGY)
       .setCallback(object : SubscribeCallback() {
           override fun onExpired() {
               super.onExpired()
               // flick the switch off since the subscribing has expired.
               // recall that we had set expiration time to 120 seconds
               // Use runOnUiThread to force the callback
               // to run on the UI thread
               runOnUiThread {
                   binding.subscribeSwitch.isChecked = false
               }
           }
       }).build()

   Nearby.getMessagesClient(this).subscribe(messageListener, options)
}
  1. Penting untuk memungkinkan pengguna menonaktifkan fungsi berbagi informasi. Artinya, penerbit dapat menghentikan publikasi dan memungkinkan pelanggan berhenti berlangganan. Untuk berhenti memublikasikan, penerbit harus menentukan pesan yang ingin dihentikan publikasinya. Oleh karena itu, jika Anda memiliki sepuluh pesan yang sedang disiarkan, Anda dapat menghentikan publikasi satu pesan, lalu membiarkan sembilan pesan tetap dipublikasikan.
private fun unpublish() {
   Nearby.getMessagesClient(this).unpublish(message)
}
  1. Meskipun dapat memublikasikan beberapa pesan sekaligus, aplikasi hanya dapat memiliki satu MessageListener dalam satu waktu. Oleh karena itu, berhenti berlangganan bersifat lebih umum. Untuk berhenti berlangganan, pelanggan harus menentukan pemroses.
private fun unsubscribe() {
   Nearby.getMessagesClient(this).unsubscribe(messageListener)
}
  1. Sebagai catatan, meskipun API harus menghentikan prosesnya ketika proses klien berhenti, Anda mungkin ingin berhenti berlangganan (dan publikasi jika mungkin) dalam metode siklus proses onDestroy().
override fun onDestroy() {
   super.onDestroy()
   // although the API should shutdown its processes when the client process dies,
   // you may want to stop subscribing (and publishing if convenient)
   Nearby.getMessagesClient(this).unpublish(message)
   Nearby.getMessagesClient(this).unsubscribe(messageListener)
}

Pada tahap ini, kode kita seharusnya sudah terkompilasi. Namun, aplikasi tidak akan berfungsi seperti yang diharapkan karena kunci API tidak ada. MainActivity Anda akan terlihat seperti ini:

package com.example.nearbymessagesexample

import android.os.Build
import android.os.Bundle
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.nearbymessagesexample.databinding.ActivityMainBinding
import com.google.android.gms.nearby.Nearby
import com.google.android.gms.nearby.messages.Message
import com.google.android.gms.nearby.messages.MessageListener
import com.google.android.gms.nearby.messages.PublishCallback
import com.google.android.gms.nearby.messages.PublishOptions
import com.google.android.gms.nearby.messages.Strategy
import com.google.android.gms.nearby.messages.SubscribeCallback
import com.google.android.gms.nearby.messages.SubscribeOptions
import java.nio.charset.Charset

class MainActivity : AppCompatActivity() {

   /**
    * For accessing layout variables
    */
   private lateinit var binding: ActivityMainBinding

   /**
    * Sets the time to live in seconds for the publish or subscribe.
    */
   private val TTL_IN_SECONDS = 120 // Two minutes.

   /**
    * Choose of strategies for publishing or subscribing for nearby messages.
    */
   private val PUB_SUB_STRATEGY = Strategy.Builder().setTtlSeconds(TTL_IN_SECONDS).build()

   /**
    * The [Message] object used to broadcast information about the device to nearby devices.
    */
   private lateinit var message: Message

   /**
    * A [MessageListener] for processing messages from nearby devices.
    */
   private lateinit var messageListener: MessageListener

   /**
    * For adding [messages][Message] to the [RecyclerView]
    */
   private lateinit var msgAdapter: MessageAdapter

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       binding = ActivityMainBinding.inflate(layoutInflater)
       setContentView(binding.root)

       binding.subscribeSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
           if (isChecked) {
               subscribe()
           } else {
               unsubscribe()
           }
       }

       binding.publishSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
           if (isChecked) {
               publish()
           } else {
               unpublish()
           }
       }
       setupMessagesDisplay()

       // The message being published is simply the Build.MODEL of the device. But since the
       // Messages API is expecting a byte array, you must convert the data to a byte array.
       message = Message(Build.MODEL.toByteArray(Charset.forName("UTF-8")))

       messageListener = object : MessageListener() {
           override fun onFound(message: Message) {
               // Called when a new message is found.
               val msgBody = String(message.content)
               msgAdapter.addItem(msgBody)
           }

           override fun onLost(message: Message) {
               // Called when a message is no longer detectable nearby.
               val msgBody = String(message.content)
               msgAdapter.removeItem(msgBody)
           }
       }

   }

   override fun onDestroy() {
       super.onDestroy()
       // although the API should shutdown its processes when the client process dies,
       // you may want to stop subscribing (and publishing if convenient)
       Nearby.getMessagesClient(this).unpublish(message)
       Nearby.getMessagesClient(this).unsubscribe(messageListener)
   }

   private fun publish() {
       val options = PublishOptions.Builder()
           .setStrategy(PUB_SUB_STRATEGY)
           .setCallback(object : PublishCallback() {
               override fun onExpired() {
                   super.onExpired()
                   // flick the switch off since the publishing has expired.
                   // recall that we had set expiration time to 120 seconds
                   runOnUiThread {
                       binding.publishSwitch.isChecked = false
                   }
                   runOnUiThread() {
                       binding.publishSwitch.isChecked = false
                   }
               }
           }).build()

       Nearby.getMessagesClient(this).publish(message, options)
   }

   private fun unpublish() {
       Nearby.getMessagesClient(this).unpublish(message)
   }

   private fun subscribe() {
       val options = SubscribeOptions.Builder()
           .setStrategy(PUB_SUB_STRATEGY)
           .setCallback(object : SubscribeCallback() {
               override fun onExpired() {
                   super.onExpired()
                   runOnUiThread {
                       binding.subscribeSwitch.isChecked = false
                   }
               }
           }).build()

       Nearby.getMessagesClient(this).subscribe(messageListener, options)
   }

   private fun unsubscribe() {
       Nearby.getMessagesClient(this).unsubscribe(messageListener)
   }

   private fun setupMessagesDisplay() {
       msgAdapter = MessageAdapter()
       with(binding.nearbyMsgRecyclerView) {
           layoutManager = LinearLayoutManager(context)
           this.adapter = msgAdapter
       }
   }

   class MessageAdapter : RecyclerView.Adapter<MessageAdapter.MessageVH>() {
       private var itemsList: MutableList<String> = arrayListOf()

       override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MessageVH {
           return MessageVH(TextView(parent.context))
       }

       override fun onBindViewHolder(holder: MessageVH, position: Int) {
           holder.bind(getItem(position))
       }

       override fun getItemCount(): Int = itemsList.size

       private fun getItem(pos: Int): String? = if (itemsList.isEmpty()) null else itemsList[pos]

       fun addItem(item: String) {
           itemsList.add(item)
           notifyItemInserted(itemsList.size)
       }

       fun removeItem(item: String) {
           val pos = itemsList.indexOf(item)
           itemsList.remove(item)
           notifyItemRemoved(pos)
       }

       inner class MessageVH(private val tv: TextView) : RecyclerView.ViewHolder(tv) {
           fun bind(item: String?) {
               item?.let { tv.text = it }
           }
       }
   }
}

Menambahkan Kunci API dari Google ke file manifes

Nearby Messages API memiliki komponen server yang disediakan Google untuk Anda. Saat Anda memublikasikan pesan, Nearby Messages API akan mengirimkan pesan ke server Google, tempat pelanggan kemudian dapat mengkueri pesan tersebut. Agar Google dapat mengenali aplikasi, Anda perlu menambahkan API_KEY dari Google ke file Manifest.xml. Setelah itu, kita dapat langsung menjalankan dan mencoba menggunakan aplikasi.

Mendapatkan Kunci API merupakan proses tiga langkah:

  1. Buka Google Developers Console.
  2. Klik + Create Credentials lalu pilih API Key*.*
  3. Salin kunci API yang dibuat dan tempel ke file manifes project Android Anda.

Tambahkan item metadata com.google.android.nearby.messages.API_KEY di dalam aplikasi di file manifes Anda. File tersebut akan terlihat seperti ini.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.nearbymessagesexample">

   <application
       android:allowBackup="true"
       android:icon="@mipmap/ic_launcher"
       android:label="@string/app_name"
       android:roundIcon="@mipmap/ic_launcher_round"
       android:supportsRtl="true"
       android:theme="@style/Theme.NearbyMessagesExample">

       <meta-data
           android:name="com.google.android.nearby.messages.API_KEY"
           android:value="ADD_KEY_HERE" />

       <activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />

               <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
   </application>

</manifest>

Setelah Anda menambahkan Kunci API, jalankan aplikasi pada dua perangkat atau lebih untuk melihatnya saling berkomunikasi.

ba105a7c853704ac.gif

  • Untuk melindungi privasi pengguna dan menghemat masa pakai baterai, Anda harus berhenti memublikasikan dan berlangganan segera setelah pengguna meninggalkan fitur tempat fungsi tersebut diperlukan.
  • Anda harus menggunakan Nearby Messages untuk membangun kedekatan di antara perangkat, tetapi tidak untuk komunikasi berkelanjutan. Komunikasi berkelanjutan dapat menguras baterai perangkat dengan kecepatan 2,5 hingga 3,5 kali lipat dari rasio konsumsi baterai normal.

Selamat! Anda sekarang telah mengetahui cara mengirim dan menemukan pesan antara perangkat di sekitar menggunakan Nearby Messages API.

Singkatnya, untuk menggunakan Nearby Messages API, Anda perlu menambahkan dependensi untuk play-services-nearby dan mendapatkan Kunci API dari Google Developers Console, lalu menambahkannya ke file Manifest.xml Anda. API memerlukan koneksi internet agar penerbit dapat mengirimkan pesannya ke server Google sehingga dapat diambil oleh pelanggan.

  • Anda telah mempelajari cara mengirim pesan
  • Anda telah mempelajari cara berlangganan untuk menemukan pesan
  • Anda telah mempelajari cara menggunakan pesan (dalam hal ini Anda cukup menampilkannya di RecyclerView)

Apa Selanjutnya?

Lihat rangkaian blog dan contoh kami