08. Services and Broadcast Receivers

Services

Services:

Examples:

Basic:

Starting services:

Stopping services:

Responsiveness:

Service types:

Broadcast Receivers

A component that response to system-wide broadcast announcements.

Some broadcasts built-in to Android: screen turned off, battery low, storage low, picture captured, SMS received, SMS sent.

Permissions:

Receiving broadcasts:

During development, broadcasts can be spoofed with adb e.g. adb shell 'am broadcast -a android.intent.action.BOOT_COMPLETED'

Initiating broadcasts:

Live Demo

Daily notification to take a picture.

<manifest ...>
  <application ...>
    <receiver
      android:name=".AlarmReceiver"
      android:enable="true"
      android:exported="true" /* Broadcast scope is global, not within the application */
    />
  </application>
</manifest>

fun Bundle.toParamsString() = keySet().map { "$it -> ${get(it)}" }.joinToString("\n")

// When the scheduled alarm event occurs, show a notification to the user
class AlarmReceiver: BroadcastReceiver() {
  override fun onReceive(context: Context, intent: Intent) {
    Log.d(TAG, "${intent.action} with extras ${intent.extras.toBundleString()}")

    val intent: PendingIndent = Intent(context, MainActivity::class.java).run {
      // Open the main activity
       PendingIntent.getActivity(
         context,
         requestCode = 0,
         intent = this,
         flags = 0
      )
    }

    val notification = Notification.Builder(
      context,
      Notification.CATEGORY_REMINDER
    ).run {
      setSmallIcon(R.drawable.camera)
      setContentTitle("Notification title")
      setContentText("Notification text")
      // Run intent on click
      setContentIntent(intent)
      // Remove the notification on click
      setAutoCancel(true)
      build()
    }

    // May need to add `@SuppressLint("ServiceCast")` annotation to the method
    val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    manager.notify(0, notification);
  }
}


class MainActivity: AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    createNotificationChannel();
  }

  // Used so that users can disable notifications or set preferences on
  // how they are delivered (e.g. deliver silently)
  private fun createNotificationChannel()  {
    val channel = NotificationChannel(
      Notification.CATEGORY_ALARM,
      "Daily reminders",
      NotificationManager.IMPORTANCE_DEFAULT
    ) {
      description = "Send daily reminders"
    }
    val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(channel)
  }
}

// Also need to schedule the reminder on boot
object Utilities {
  // Notifications fire immediately: use AlarmManager to schedule them
  fun scheduleReminder(context: Context, hour: Int, minute: Int) {
    val today = Calendar.getInstance().apply {
      set(Calendar.HOUR_OF_DAY, hour)
      set(Calendar.MINUTE, minute)
    }

    val intent = Intent(context, AlarmReceiver::class.java).let {
      PendingIntent.getBroadcast(context, 0, it, FLAG_IMMUTABLE)
    }

    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    alarmManager.setInexactRepeating(
      AlarmManager.RTC,
      today.timeInMillis,
      AlarmManager.INTERVAL_DAY,
      intent)
  }
}