Objectives

Incorporate Broadcast Event Senders/Receivers into the application. Use BootReciever to recieve boot event to start application service on launch. Use NetworkReveicer to stop/start service when network is starting/stopping. Use Broadcast Events for status updates from background service to TimelineActivity

BroadcastReciever - Boot

Introduce this class to receive bootloader events:

YambaApplication.xtend

class BootReceiver extends BroadcastReceiver
{
  override onReceive(Context context, Intent intent) 
  {
    context.startService(new Intent(context, typeof(UpdaterService)))
  }
}

This will start the updater service when the device boots. It requires this permission in the manifest:

AndroidManifest

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
      <receiver android:name="com.marakana.yambax.BootReceiver">
        <intent-filter>
          <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter> 
      </receiver>

We can change our default service state to running:

YambaApplication.xtend

  @Property boolean                serviceRunning = true

.. and also make some adjustments to the toggle service action:

BaseActivity.xtend

  val toggleService = [ | intent = new Intent(this, typeof(UpdaterService))
                          if (app.isServiceRunning)
                            stopService(intent)
                           else
                            startService(intent) 
                          app.serviceRunning = !app.serviceRunning] as Command

You can inspect the list of running services on the device:


Source


BaroadcastReciever - tweets

We can employ Android Broadcast events for the status updates, removing the custom event mechanism we have hand coded.

We first define some resources to describe the events:

res/values/strings.xml

  <string name="send_timeline_notifications_permission_label">Sends Timeline Notifications</string>
  <string name="send_timeline_notifications_permission_description">Allow this application to send timeline notifications to other applications</string>
  <string name="receive_timeline_notifications_permission_label">Receive Timeline Notifications</string>
  <string name="receive_timeline_notifications_permission_description">Allow this application to receive timeline notifications from other applications</string>

.. and then declare them in the manifest:

AndroidManifest.xml

    <uses-permission android:name="com.marakana.yamba.SEND_TIMELINE_NOTIFICATIONS" />
    <uses-permission android:name="com.marakana.yamba.RECEIVE_TIMELINE_NOTIFICATIONS" />

    <permission android:name="com.marakana.yamba.SEND_TIMELINE_NOTIFICATIONS"
      android:label="@string/send_timeline_notifications_permission_label"
      android:description="@string/send_timeline_notifications_permission_description"
      android:permissionGroup="android.permission-group.PERSONAL_INFO"
      android:protectionLevel="normal" />

    <permission android:name="com.marakana.yamba.RECEIVE_TIMELINE_NOTIFICATIONS"
      android:label="@string/receive_timeline_notifications_permission_label"
      android:description="@string/receive_timeline_notifications_permission_description"
      android:permissionGroup="android.permission-group.PERSONAL_INFO"
      android:protectionLevel="normal" />

The strings need to be replicated in the UpdaterService class:

UpdaterService

  public static final String NEW_STATUS_INTENT              = "com.marakana.yamba.NEW_STATUS"
  public static final String SEND_TIMELINE_NOTIFICATIONS    = "com.marakana.yamba.SEND_TIMELINE_NOTIFICATIONS";
  public static final String RECEIVE_TIMELINE_NOTIFICATIONS = "com.marakana.yamba.RECEIVE_TIMELINE_NOTIFICATIONS"

.. and then dispatched :

  override def void doBackgroundTask()
  {
    try
    {
      val List<Twitter.Status> timeline  = twitter.getFriendsTimeline
      newTweets = if (app.timeline.size == 0) timeline else timeline.filter [it.id > app.timeline.get(0).id]
      Log.e("YAMBA", "number of new tweets= " + newTweets.size)      

      app.updateTimeline(newTweets)
      sendBroadcast(new Intent(NEW_STATUS_INTENT), RECEIVE_TIMELINE_NOTIFICATIONS);      
    }
    catch (TwitterException e)
    {
      Log.e("YAMBA", "Failed to connect to twitter service", e); 
    }
  }

The knock on changes involve changes to the main application object:

YambaApplication.xtend

  def updateTimeline(Iterable<Twitter.Status> newTweets)
  {
    newTweets.forEach[timeline.add(0, it)]
  }

  def clearTimeline()
  {
    timeline.clear
    // todo - clear timeline view
  }
}

.. and the TimelineActivity, which gets an new receiver object:

TimelineActivity

class TimelineReceiver extends BroadcastReceiver
{
  var TimelineActivity timelineActivity

  new (TimelineActivity activity)
  {
      timelineActivity = activity;
  }

  override onReceive(Context context, Intent intent) 
  {
      timelineActivity.timelineAdapter.notifyDataSetChanged
  }
} 

class TimelineActivity extends BaseActivity
{  
  @Property TimelineAdapter timelineAdapter
  var TimelineReceiver receiver
  var IntentFilter     filter

  override onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.timeline)
    timelineAdapter    = new TimelineAdapter(this, R.layout.row, app.timeline)

    receiver = new TimelineReceiver (this)
    filter   = new IntentFilter( UpdaterService.NEW_STATUS_INTENT )
  }

  override onStart()
  { 
    super.onStart    
    val listTimeline = findViewById(R.id.listTimeline) as ListView
    listTimeline.setAdapter(timelineAdapter);
  }

  override onResume()
  {
      super.onResume
    super.registerReceiver(receiver, filter, UpdaterService.SEND_TIMELINE_NOTIFICATIONS, null);
  }

  override onPause() 
  {
    super.onPause();
    unregisterReceiver(receiver) 
  }
}

Source


BroadcastReceiver - Network Events

We can also potentially improve the energy deficiency of our application by disabling the background task when the network is unavailable:

class NetworkReceiver extends BroadcastReceiver 
{ 
  override onReceive(Context context, Intent intent) 
  {
    val isNetworkDown = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); 

    if (isNetworkDown) 
    {
      if (YambaApplication.serviceRunning)
      {
        Log.d("YAMBA", "onReceive: NOT connected, stopping UpdaterService");
        context.stopService(new Intent(context, typeof(UpdaterService)))
      }
    }
    else 
    {
      if (!YambaApplication.serviceRunning)
      {
        Log.d("YAMBA", "onReceive: connected, starting UpdaterService");
        context.startService(new Intent(context, typeof(UpdaterService)))
      }
    }
  }
}

This will involves setting our initial service back to false:

YambaApplication.xtend

  public static boolean            serviceRunning = false

.. and other adjustments:

BaseActivity.xtend

  val toggleService = [ | intent = new Intent(this, typeof(UpdaterService))
                          if (YambaApplication.serviceRunning)
                            stopService(intent)
                           else
                            startService(intent) 
                          YambaApplication.serviceRunning = !YambaApplication.serviceRunning] as Command
  override onMenuOpened(int featureId, Menu menu)
  { 
    val toggleItem = menu.findItem(R.id.itemToggleService)
    toggleItem.title = if (YambaApplication.serviceRunning) R.string.titleServiceStop         else R.string.titleServiceStart
    toggleItem.icon  = if (YambaApplication.serviceRunning) android.R.drawable.ic_media_pause else android.R.drawable.ic_media_play
    true
  }

UpdaterService

  override onStartCommand(Intent intent, int flags, int startId)
  {
    super.onStartCommand(intent, flags, startId)
    startBackgroundTask
    YambaApplication.serviceRunning = true
    START_STICKY;
  }

  override onDestroy()
  {
    super.onDestroy
    stopBackgroundTask
    YambaApplication.serviceRunning = false
  }

AndroidManifest

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      <receiver android:name="com.marakana.yambax.NetworkReceiver">
        <intent-filter>
          <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        </intent-filter>
      </receiver>

Source


Purge Timeline

Finally, our timeline should be cleared if a purge action received:

YambaApplication.xtend

  def clearTimeline()
  {
    timeline.clear
    sendBroadcast(new Intent(UpdaterService.NEW_STATUS_INTENT), UpdaterService.RECEIVE_TIMELINE_NOTIFICATIONS);   
  }

Source


Exercises (Assignment Ideas)

These labs are based on the application developed using this text:

The Yamba application here uses android API largely compatible with Android 3.0 and earlier.

However, it has been substantially revised to use a full suite of modern facilities introduced in subsequent releases:

A full set up repos for all the code in the revised application is here:

(Toc is reproduce at the end of this page)

Specifically, the revised application includes:

  • Fragments
  • Actions and Action Bars
  • Content Providers
  • Intent Services
  • Alarms
  • Live Wallpapers

  • a number of minor improvements.

A version of YambaX (written in xtend) - fully updated to include the above features, would be an interesting and useful assignment topic

ToC for Learning Android 2nd Edition

Chapter 1 Android Overview

  • Android Overview
  • History
  • Android Versions
  • Android Flavors
  • Summary

Chapter 2 Java Review

  • Comments
  • Data Types: Primitives and Objects
  • Modifiers
  • Arrays
  • Operators
  • Control Flow Statements
  • Error/Exception Handling
  • Complex Example
  • Interfaces and Inheritance
  • Collections
  • Generics
  • Threads
  • Summary

Chapter 3 The Stack

  • Stack Overview
  • Linux
  • Native Layer
  • Dalvik
  • Application Framework
  • Applications
  • Summary

Chapter 4 Installing and Beginning Use of Android Tools

  • Installing Java Development Kit
  • Installing the Android SDK
  • Hello World!
  • Anatomy of an Android Project
  • Drawable Resources
  • Building the Project
  • Android Emulator
  • Summary

Chapter 5 Main Building Blocks

  • A Real- World Example
  • Activities
  • Intents
  • Services
  • Content Providers
  • Broadcast Receivers
  • Application Context
  • Summary

Chapter 6 Yamba Project Overview

  • The Yamba Application
  • Design Philosophy
  • Project Design
  • Part 1: Android User Interface
  • Part 2: Intents, ActionBar, and More
  • Part 3: Android Services
  • Part 4: Content Providers
  • Part 5: Lists and Adapters
  • Part 6: Broadcast Receivers
  • Part 7: App Widgets
  • Part 8: Networking and the Web (HTTP)
  • Part 9: Live Wallpaper and Handlers
  • Summary

Chapter 7 Android User Interface

  • Two Ways to Create a User Interface
  • Views and Layouts
  • Starting the Yamba Project
  • The StatusActivity Layout
  • The StatusActivity Java Class
  • Logging Messages in Android
  • Threading in Android
  • Other UI Events
  • Alternative Resources
  • Summary

Chapter 8 Fragments

  • Fragment Example
  • Fragment Life Cyle
  • Dynamically Adding Fragments
  • Summary

Chapter 9 Intents, Action Bar, and More

  • Preferences
  • The Action Bar
  • Shared Preferences and Updating Status Fragment
  • The Filesystem Explained
  • Summary

Chapter 10 Services

  • Our Example Service: RefreshService
  • Pulling Data from Yamba
  • Summary

Chapter 11 Content Providers

  • Databases on Android
  • Status Contract Class
  • Update RefreshService
  • Content Providers
  • Creating a Content Provider
  • Summary

Chapter 12 Lists and Adapters

  • MainActivity
  • Basic MainActivity
  • Timeline Fragment
  • About Adapters
  • Loading the Data
  • Custom Logic via ViewBinder
  • Details View
  • Summary

Chapter 13 Broadcast Receivers

  • About Broadcast Receivers
  • BootReceiver
  • Alarms and System Services
  • Broadcasting Intents
  • Summary

Chapter 14 App Widgets

  • Using Content Providers Through Widgets
  • Summary Chapter 15 Networking and Web Overview
  • Quick Example
  • Networking Basics
  • HTTP API
  • Apache HTTP Client
  • HttpUrlConnection
  • Networking in the Background using AsyncTask and AsyncTaskLoader
  • Summary

Chapter 16 Interaction and Animation: Live Wallpaper and Handlers

  • Live Wallpaper
  • Handler
  • Summary