Showing posts with label Broadcast receivers in android. Show all posts
Showing posts with label Broadcast receivers in android. Show all posts

Tuesday, June 12, 2012

Broadcast Receiver tutorial


Broadcast Receivers
A broadcast receiver is a component of an Android process, along with activities, content providers, and services that responds to system-wide broadcast announcements. Many broadcasts originate from the system—for example, a broadcast announcing that the screen has turned off, the battery is low, or a picture was captured. Applications can also initiate broadcasts—for example, to let other applications know that some data has been downloaded to the device and is available for them to use. Although broadcast receivers don't display a user interface, they may create a status bar notification to alert the user when a broadcast event occurs. More commonly, though, a broadcast receiver is just a "gateway" to other components and is intended to do a very minimal amount of work. For instance, it might initiate a service to perform some work based on the event. As the name indicates, a broadcast receiver responds to a broadcast message sent by a client. The message itself is an Android broadcast intent. A broadcast intent (message) can invoke (or be responded to by) more than one receiver. A component such as an activity or a service uses the sendBroadcast() method available on the Context class to send a broadcast event. The argument to this method

A broadcast receiver is implemented as a subclass of BroadcastReceiver and each broadcast is delivered as an Intent object. These receiving components (broadcast receivers) then need to be registered in the manifest file through a receiver tag to indicate that the class is interested in responding to a certain type of broadcast intent.
. is an intent.
Receiver Lifecycle
A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.

Process Lifecycle

A process that is currently executing a BroadcastReceiver (that is, currently running the code in its onReceive(Context, Intent)method) is considered to be a foreground process and will be kept running by the system except under cases of extreme memory pressure.
Once you return from onReceive(), the BroadcastReceiver is no longer active, and its hosting process is only as important as any other application components that are running in it. This is especially important because if that process was only hosting the BroadcastReceiver (a common case for applications that the user has never or not recently interacted with), then upon returning from onReceive() the system will consider its process to be empty and aggressively kill it so that resources are available for other more important processes.
A Broadcast Receiver must return from its onReceive  handler within 10 seconds. as a part of Android responsiveness model.This means that for longer-running operations you will often use a Service in conjunction with a BroadcastReceiver to keep the containing process active for the entire time of your operation.

Broadcast receivers can be registered in two ways
1.    It can be registered in Android Manifest file
2.   Inside an activity registered at runtime dynamically via the Context.registerReceiver()method.  The First example SRM_BroadcastRecevierTutorial demonstrates  a standalone broadcast receiver application which gets invoked on system message on wallpaper being changed .
The Demonstrative project generates Notification when wallpaper in Home screen is changed ,portrays the Broadcastreceiver registered statically in Androidmanifest file,usage of Pending Intent passed with Notification.
 1.Create  Android project with details as listed in the table below.
Property name
Property value
Project name
SRM_Broadcast Receiver
Package name
in.ac.srmuniv.broadcastreceiver
Activity name
-
Layout xml name
-

Create a Android project SRM_BroadcastReceiverTutorial and unselect createActivity checkbox
Figure 1 Creating the Project

2.Make a WallPaperNotificationReceiver.java file which extends BroadcastReceiver
 packagein.ac.srmuniv.broadcastreceiver;

importandroid.app.Notification;
importandroid.app.NotificationManager;
importandroid.app.PendingIntent;
importandroid.content.BroadcastReceiver;
importandroid.content.Context;
import android.content.Intent;
importandroid.net.Uri;

import android.util.Log;
importandroid.widget.Toast;

public classWallPaperNotificationReceiver extendsBroadcastReceiver {

                @Override
                public voidonReceive(Context context, Intent intent) {
                                this.sendNotification(context, "You have changed Wallpaper");
    }
    private voidsendNotification(Context ctx, String message)
    {
                //Get the notification manager
                String ns = Context.NOTIFICATION_SERVICE;
                NotificationManager nm =
                                (NotificationManager)ctx.getSystemService(ns);
               
                //Create Notification Object
                                inticon = R.drawable.ic_launcher;
                                CharSequence tickerText = "Hello";
                                longwhen = System.currentTimeMillis();
                               
                                Notification notification =
                                                newNotification(icon, tickerText, when);

                                //Set ContentView using setLatestEvenInfo
                    Intent intent = newIntent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse("http://www.srmuniv.ac.in"));
                    PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, 0);
                    notification.setLatestEventInfo(ctx, "Intimation", message, pi);
                                               
                    //Send notification
                                nm.notify(1, notification);
                                Toast.makeText(ctx,"Hello Nawin",Toast.LENGTH_LONG).show();
    }

}

3.Make the entries in manifest file to register Broadcast receiver to specific intent message(s) to which it should respond
xml version="1.0" encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/android"
    package="in.ac.srmuniv"
    android:versionCode="1"
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
       
    <receiver android:name=".WallPaperNotificationReceiver">
           <intent-filter>
            <action android:name="android.intent.action.WALLPAPER_CHANGED"/>
            </intent-filter>
    </receiver>
 </application>
</manifest>

4.Run the Application after it has successfully installed in the emulator click menu button to select and wallpaper settings to change the wallpaper
 Figure 2 Selecting wall Paper
 5. Choose from wallpapers  and set one as wallpaper

 Figure 3 Setting Wallpaper
Figure 4.On selecting a wallpaper you can see a Toast message appearing and a notification appears at Notification bar
                           

Figure 5. Expanding the Notification and selecting the notification message will call the browser
                                                  
 Figure 6 Shows when Notification clicked.Pending intent calls the browser to open the web page.

         
                                       
CODE EXPLANATION : 
                                     The   WallPaperNotificationReceiver is a BroadcastReceiver  which overrides its base class method onReceive(). System service sends a broadcast with Intent message android.intent.action.WALLPAPER_CHANGED  when wallpaper is changed by the user .Our application which has registered for the Intent message in the manifest file for our WallPaperNotificationReceiver  class will get invoked,onReceive(Context context, Intent intent) method will be called .In the onReceive() method sendNotification local method is called which generates Notification .When user expands the notification in notification bar and interacts with Notification list item ,parceled intent in form of pending intent opens web browser through intent filtering action  which has Intent.ACTION_VIEW as action the data as  URL http://www.srmuniv.ac.in
Android generates  many in build messages by its System process which can be registered to be received for various actions to be performed in an application.A few are listed below.
android.intent.action.BATTERY_LOW
android.intent.action.DATA_SMSRECEIVED
android.intent.action.BOOT_COMPLETED
android.intent.action.CAMERA_BUTTON
For example  the Intent action ACTION_BATTERY_LOW broadcasts a warning when the battery is low.If your application has battery consuming service of some kind you might want to listen for the Broadcast and shutdown your service until battery power is sufficient.

Pending Intent:
            The PendingIntent can be handed to other applications so that they can perform the action you described on your behalf at a later time. By giving a PendingIntent to another application, you are granting it the right to perform the operation you have specified as if the other application was yourself (with the same permissions and identity).

String ns = Context.NOTIFICATION_SERVICE;
                NotificationManager nm =
                                (NotificationManager)ctx.getSystemService(ns);
                        ---------
Notification notification =
                                                newNotification(icon, tickerText, when);

                                //Set ContentView using setLatestEvenInfo
                    Intent intent = newIntent(Intent.ACTION_VIEW);
                    intent.setData(Uri.parse("http://www.srmuniv.ac.in"));
                    PendingIntent pi = PendingIntent.getActivity(ctx, 0, intent, 0);
                    notification.setLatestEventInfo(ctx, "Intimation", message, pi);
                    //Send notification
                                nm.notify(1, notification);
 "pi" is an intent to launch when the user clicks the expanded notification.
The above application will only work with Android versions up to Gingerbread 2.3.6
                                  An additional security feature is added to all Android versions from  Android 3.1  where Android system excludes all receiver from receiving intents by default if the corresponding application has never been started by the user or if the user explicitly stopped the application via the Android menu (in Manage  Application). This makes  user to be sure that only the applications he started will receive broadcast intents. This does not mean the user has to start the application again after a reboot. The Android system remembers that the user already started it. Only one start is required without a forced stop by the user.
In order to make the above application work in latest version of Android 4.x you need to add the following Modifications to the existing SRM_BroadcastReceiver project.
Add an Activity to the project SRM_BroadcastReceiver named MainActivity.java.


packagein.ac.srmuniv.broadcastreciver;

importandroid.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
           // TODO Auto-generated method stub
           super.onCreate(savedInstanceState);
           setContentView(R.layout.main);
     }

}.
Add the Layout main.xml in res/layout folder
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />


</LinearLayout>






Modify Androidmanifest.xml as shown below.                                  Figure 7 Shows the App invoked .
<?xml version="1.0"
encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.ac.srmuniv.broadcastreciver"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />
     <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <receiver android:name=".WallPaperNotificationReceiver">
            <intent-filter>
                <action android:name="android.intent.action.WALLPAPER_CHANGED"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>
CODE EXPLANATION : 
               The application needs to be launched at least once,after that when you change the wallpaper it will call the receiver, even after reboot.
                                  Dynamic Registration of BroadcastReceiver
       Receiver can be registered via the Android manifest file. You can also  register and unregister a receiver at runtime via the Context.registerReceiver() and Context.unregisterReceiver() methods
Do not forget to unregister a dynamically registered receiver by usingContext.unregisterReceiver() method. If you forget this, the Android system reports aleaked broadcast receiver error. For instance, if you registered a receive in onResume()methods of your activity, you should unregister it in the onPause() method
Services
            Services are long running process which does not need UI. Services are created when manually started (via an API call) or when some activity tries connecting to the service via interprocess communication (IPC). Services will live until specifically shut down or until Android is desperate for RAM and destroys them prematurely we will explore them more in detail in subsequent tutorial

Handler
               All Android application components  including Activities, Services, and Broadcast Receivers start on the main application thread. As a result,time-consuming process in any component will block all other components including Services and the Visible Activity. Using background threads is vital to avoid the “Application Unresponsive”.Android system pops up dialog box for Unresponsiveness  as Activities that don’t respond to an input event (such as a key press) within 5 seconds and Broadcast Receivers that don’t complete their onReceive handlers within 10 seconds.Android offers two alternatives for backgrounding your processing. The AsyncTask class lets you define an operation to be performed in the background, then provides event handlers you can use to monitor progress and post the results on the GUI thread. Alternatively, you can implement your own Threads and  use the Handler class to synchronize with the GUI thread before updating the UI The most flexible means of making an Android-friendly background thread is to create an instance of a Handler subclass. You need only one Handler object per activity, and you do not need to manually register it. Merely creating the instance is sufficient to register it with the Android threading  subsystem. Your background thread can communicate with the Handler, which will do all of its work on the activity’s UI thread. This is important, as UI changes, such as updating widgets,should occur only on the activity’s UI thread.You have two options for communicating with the Handler: Messages and Runnable objects.
Exercise 2 The Demonstrative project displays current time portrays Broadcast Receiver registered dynamically with in an Activity,how a Service will send message to Activity through Broadcast receiver. 
1.Create  Android project with details as listed in the table below.
Property name
Property value
Project name
SRM_BroadcastReceiverTutorial2
Package name
in.ac.srmuniv.broadcastreceiver_two
Activity name
ClockTimeActivity
Layout xml name
main
2.Create main.xml in res/layout folder for UI Design of ClockTimeActivity.Code listing is given below
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:background="@drawable/clockbg"
        android:paddingLeft="20dp" >

        <TextView
            android:id="@+id/hr"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:"
            android:textColor="#FFFFFF"
            android:textSize="16pt" >
        </TextView>

        <TextView
            android:id="@+id/mn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:"
            android:textColor="#FFFFFF"
            android:textSize="16pt" >
        </TextView>

        <TextView
            android:id="@+id/sc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00"
            android:textColor="#FFFFFF"
            android:textSize="16pt" >
        </TextView>
    </LinearLayout>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnset"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:text="Digital Clock" >
    </TextView>

</RelativeLayout>
3.Copy the code in ClockTimeActivity.java. Activity.
 package in.ac.srmuniv.broadcastreceiver_two;
import android.app.Activity;
importandroid.content.BroadcastReceiver;
importandroid.content.Context;
importandroid.content.Intent;
import android.content.IntentFilter;
importandroid.graphics.Typeface;
import android.os.Bundle;
import android.util.Log;
importandroid.widget.TextView;

public class ClockTimeActivity extends Activity {
    private static final String TAG="BroadcastTime";
    private Intent in;

    private BroadcastReceiver timerec=new BroadcastReceiver(){

        @Override
        public void onReceive(Context c, Intent i) {

            //Getting the values from intent and updating log

            int h=i.getIntExtra("hrs", 0);
            int m=i.getIntExtra("min", 0);
            int s=i.getIntExtra("sec", 0);
            Log.d(TAG, Integer.toString(h));
            Log.d(TAG, Integer.toString(m));
            Log.d(TAG, Integer.toString(s));

            //Printing Time to Textview

            TextView th=(TextView) findViewById(R.id.hr);
            TextView tm=(TextView) findViewById(R.id.mn);
            TextView ts=(TextView) findViewById(R.id.sc);
            if(h>=10)
                th.setText(Integer.toString(h)+":");
            else
                th.setText("0"+Integer.toString(h)+":");
            if(m>=10)
                tm.setText(Integer.toString(m)+":");
            else
                tm.setText("0"+Integer.toString(m)+":");
            if(s>=10)
                ts.setText(Integer.toString(s));
            else
                ts.setText("0"+Integer.toString(s));

        }

    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
         //Intent for starting the Time display service
         in=new Intent(this,BroadCastTimeService.class);
        startService(in);
        //(BroadcastTime.BROADCAST_TIME));
         //Code for opening alarm set activity

        TextView txth = (TextView) findViewById(R.id.hr);
        TextView txtm = (TextView) findViewById(R.id.mn);
        TextView txts = (TextView) findViewById(R.id.sc);

        Typeface fonth = Typeface.createFromAsset(getAssets(), "MyriadPro-Bold.otf");
        txth.setTypeface(fonth);
        txtm.setTypeface(fonth);
        txts.setTypeface(fonth);
     }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        unregisterReceiver(timerec);
    }

    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        registerReceiver(timerec, new IntentFilter("in.ac.srmuniv.displaytime"));
    }
}

4.Add clockbg.png  image to drawable folder and MyriadPro-Bold.otf  font  file in assets folder to have appropriate font for Digital Clock display
5.Add an BroadCastTimeService.java which extends Service following is the code listing

package in.ac.srmuniv.broadcastreceiver_two;
import java.util.Date;
import android.app.Service;
importandroid.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

public class BroadCastTimeService extends Service {
   private static final String TAG="BroadcastTime";
   static final String BROADCAST_TIME = "in.ac.srmuniv.displaytime";
   public final Handler handler = new Handler();
   Intent i;
      @Override
   public IBinder onBind(Intent arg0) {
         // TODO Auto-generated method stub
         return null;
   }
   @Override
   public void onCreate()
   {
         super.onCreate();
         i = new Intent(BROADCAST_TIME);
   }

  @Override

   public intonStartCommand(Intent intent, int flags, int startId) {
         // TODO Auto-generated method stub
         handler.removeCallbacks(update);
         handler.postDelayed(update, 1000);
         return Service.START_NOT_STICKY;
   }
   private Runnable update= new Runnable(){
         @Override
         public void run() {
               Log.d(TAG, "Updated Time");
               i.putExtra("hrs", new Date().getHours());
               i.putExtra("min", newDate().getMinutes());
               i.putExtra("sec", newDate().getSeconds());
               sendBroadcast(i);
               handler.postDelayed(this, 1000);
         }
    };
}
5.Modify the  manifest file as per the code listing shown below which should include the entry for BroadCastTimeService but no registration is done for the Broadcast receiver since it is dynamically registered through coding
<?xml version="1.0"encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="in.ac.srmuniv.broadcastreceiver_two"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk android:minSdkVersion="15"/>
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".ClockTimeActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name=".BroadCastTimeService"/>
    </application>

</manifest>

6.Run the application in the emulator you can see the Digital clock viewed in the ClockTimeActivity

Figure 10 Shows the screen shot of Digital clock
CODE EXPLANATION
            ClockTimeActivity contains a Broadcast receiver object instantiated as anonymous inner class with onReceive(0  method overridden.onCreate(0 method in ClockTimeActivity calls the service class through the following code.
in=newIntent(this,BroadCastTimeService.class);
                       startService(in);

BroadCastTimeService  is a Service which runs in background once started, used to generate time delay every one second through separate thread .An anonymous inner class Thread class  implementing  Runnable interface object update the time  by fetching  current system time .The time data is  send through broadcast message in run() method.  managed by  Handler object handler.Handler object spawns thread  every 1000 milliseconds(1 sec).
              The onCreate method of  BroadCastTimeServiceclass creates a intent to be broadcast with custom action message "in.ac.srmuniv.displaytime". onStartCommand(Intent intent, int flags, intstartId) is called after onCreate method where service is in active state .
                                    handler.removeCallbacks(update);
                        handler.postDelayed(update, 1000);
handler objects postDelayed method manages background thread of Runnable update object by calling it at specified interval of time in our example every 1000 seconds.
                                    i.putExtra("hrs", newDate().getHours());
                                    i.putExtra("min", newDate().getMinutes());
                                    i.putExtra("sec", newDate().getSeconds());
                                    sendBroadcast(i);
                        handler.postDelayed(this, 1000);
The above code in run() method adds hours, minutes and seconds of current time from Date class as extra message to the Intent  created in onCreate method through putExtramethod and sendBroadcast() method broadcasts the intent.
       ClockTimeActivity which has registered the receiver dynamically in onResume() method with custom Intent action "in.ac.srmuniv.displaytime",receives the broadcast message. Since the  Broadcast receiver is implemented inside the Activity in onReceive method  it is able to manipulate  TextViews for  displaying hours, minuite and seconds with the values taken from the Intent message through getExtra() method.
           The service will be sending broadcast message managed by handler object every one second. The broadcast receiver in activity will be called every one second which in turn updates Time display in the Activity through its TextViews.
                                    int h=i.getIntExtra("hrs", 0);
                                    intm=i.getIntExtra("min", 0);
                                    ints=i.getIntExtra("sec", 0);
                                   
                                    //Printing Time to Textview
                                   
                                    TextView th=(TextView) findViewById(R.id.hr);
                                    TextView tm=(TextView) findViewById(R.id.mn);
                        TextView ts=(TextView) findViewById(R.id.sc);