Saturday, November 24, 2012

Silence And More!!

“Time:11.30am-12.30pm Software Release Plan Meeting” popped up from my calendar. Before I get into the meeting I answer a call to make a quick conversation with my colleague. In the middle of the meeting my phone buzzes out again. Stumbling with my phone I give an apologetic look to all the gentlemen in the room and obediently put my mobile on "Silent Mode".

The meeting gets over in an hour and I get into discussion with my team on some of the key issues. Lunch time I realize that my phone was muted way long back. I quickly un-mute my phone and in the process come across couple of missed calls.

In this blog I introduce an application which helps me to
  • Mute the phone for the period that has been set
  • Automatically un-mutes the phone once the period has expired
When the phone was set in silent mode all the incoming calls are muted. Other key features supported during this silence period are
  • Automatic SMS notification to your phone contacts when they try to reach you. This means SMS notification is not send out to numbers that are not listed in your contacts.
  • Duplicate SMS notifications are suppressed if the same contact tries to reach you again.
  • Displays an alert in the mobile's notification tray if any of your phone contact tried to reach you.
  • Cancel the silence period at any point of time and the phone gets un-muted.
  • Track the calls that are listed in my phone and store them as part of its history

Below is a sample snapshot and the details shown by this application. 


    
  Following are the details one can see:
  • Notification icon indicating that a contact had made a call when the call was in silent mode.
  • Notification icon indicating that the phone is in mute.
  • Placeholder for setting the silence interval.
  • History indicating the period of silence mode.
  • And list of contacts who have called in this interval.
    
Let's briefly walk over different frameworks and API that I used to build this application. 

  •  Presentation Layer
  • Being a part time web developer, the user interface is made of html, css and javascripts. All my presentation logic are composed of these three main UI elements. This includes deriving the time, rendering history data to the "li" elements of html. When the user selects the interval and submits, a javascript call is made into the phone gap plugin which is discussed below.  
  • Cordova or PhoneGap Plugins
  • The presentation layer uses the Cordova or PhoneGap plugin to interact with the android platform. The javascript calls from the presentation layer uses the Cordova plug-in to interact with the android platform related operations. Since there are numerous examples in the internet showing how this plugin can be installed, configured into your eclipse IDE and used with android platform I will not be repeating this.
    Below is a quick view on how Cordova is used and how the plugin can be defined.
    import org.apache.cordova.DroidGap;
    public class myActivity extends DroidGap {
       @Override
        public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
            //do all activity logic...
            //invoke the presentation layer
            super.loadUrl("file:///android_asset/www/index.html");
         }
    }
    import org.apache.cordova.api.Plugin;
    import org.apache.cordova.api.PluginResult;


    public class silenceAndMore extends Plugin { 
       @Override 
       public PluginResult execute(String action, JSONArray data, String callbackId) { 
          //all actions from presentation layer to platform are written here
         return new PluginResult(PluginResult.Status.OK, "Success!!");
       }
    }
  • Alarm Manager
  • Provides an inbuilt functionality of “timers” in android. This is basically used to set the period of silence and to wake up when the period has expired. The main reason for choosing the Alarm Manager is because of its ability to run in the background even when the main activity is removed by the android platform. Below example shows how typically an interval can be set in Alarm Manager 
    Calendar cal = Calendar.getInstance();             
    // Set the calendar with your date and time
    cal.set(Calendar.DAY_OF_MONTH, date);
    cal.set(Calendar.HOUR_OF_DAY, hour);
    cal.set(Calendar.MINUTE, min);
    //create an intent for the alarm
    Intent alarmIntent = new Intent(context, AlarmReceiver.class);
    //set an action in the intent
    alarmIntent.setAction("ALARM");         alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEWTASK);
    //cancel all old and pending intents
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,   alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
    //Get the alarm manager and set the time
    AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
    alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);

  • Broadcast Receiver
  • Android’s broadcast receiver receives all the callbacks that are registered. The callbacks for alarms and calls are registered respectively. When the timer has expired the system wakes up the broadcast receiver. Similarly the incoming calls with various phone states are triggered for call registrations.
    public class myReceiver extends BroadcastReceiver {
      String PHONESTATE="android.intent.action.PHONE_STATE";
      @Override
      public void onReceive(Context context, Intent intent) {
        //Switch the usage based on action on the intent 
        if (intent.getAction().equals("ALARM")){    
           //do logic for expiry of interval
        }else if (intent.getAction().equals(PHONESTATE)){    
             //check the phone states using Telephony manager
             //i.e if it is idle or ringing
        }
      }
    }
  • Telephony Manager
  • This "Phone State" intent is thrown to broadcast receiver for an incoming call. This intent will have calling party number and at the same time the state of the call. The telephony manager helps to validate  the state of the call. An example shows below how an incoming call can be detected.

    if(intent.getAction().equals(PHONESTATE)) {   
    //get the phone state
    String state= intent.getStringExtra(TelephonyManager.EXTRA_STATE);
    //get the phone number
    String number= intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
    if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)){
        //call state is ringing. Do your logic here.
    }


  • Audio Manager 
  • Helps in setting the phone in silent or remove from silence mode. When the user sets the interval, phone is moved into silent mode. And when the timer is expired, the phone is removed from the silent mode. Below example shows how a phone can be set to a "Silent" mode.

    AudioManager mAM = getSystemService(context.AUDIO_SERVICE);
    mAM.setRingerMode(AudioManager.RINGER_MODE_SILENT);

  • Contacts Provider
  • The contacts provider class lets you access the contacts in the phone. Along with the content resolver one can access the phone contact book and access the "Name" from the incoming number. Below is a typical query into the phone contact book to get the display name from a phone number.
    ContentResolver localContentResolver = context.getContentResolver();
    Cursor contactLookupCursor =  localContentResolver.query(
                Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, 
                Uri.encode(number)), 
                new String[]{PhoneLookup.DISPLAY_NAME},                             
                null, null , null);                  
  • Notification Manager
  • This class helps to set a notification icon on the phone’s notification tray. When the incoming call is from one of the phone contacts, the notification tray is enabled with the application's icon. The sample for this notification manager is present in the android development site. I followed most of it to set the notification.
  • SMS Manager 
  • To send SMS text message to a recipient. The application after retrieving the contact name for the incoming number will send SMS to this number.
    //set the SMS text message to be send
    String payload = "Sorry I am busy right now";
    //Get the SMS Manager instance
    SmsManager smsManager = SmsManager.getDefault();
    //send the text to the number
    smsManager.sendTextMessage(number, null, payload, null, null);
  • File Storage
  • The information like time of last silence period, contacts who called are persisted in a file. The data was persisted using the basic FileInputstream and FileOutputstream. A typical file read operation using an FileInputStream is shown below.
      FileInputStream fIn = null;
      StringBuffer strContent = new StringBuffer("");
      int ch;
      try {
            //open the file under this context
            fIn = openFileInput("data.txt");
            while ((ch = fIn.read()) != -1)
                  strContent.append((char) ch);
            fIn.close();
          } catch (Exception e) {
            System.out.println(e);
            return null;
          }
      String data = strContent.toString();


    Conclusion

    My earlier design was making use of javascripts for functions like setting intervals, maintaining history etc. Additionally I used the Cordova plugin to retrieve the contact names from the phone number. But when the application goes into background, the android platform removes the old application and relinquishes its memory to new applications that come into the foreground. So when you set a silence interval of say an hour or two its very likely that the application gets removed. Now that the silence mode was set the phone would never un-mute as the application was removed by android platform.   

    With this I conclude the session of describing the use-cases and frameworks for this application that I have designed and implemented. I will be very glad to present any more free sample for this application or answer any queries that you have here. 

    Friday, September 16, 2011

    Location based app triggered by an SMS


    Location based services has been the favorites for many application developers right from the day when poor man’s GPS evolved. Following this the handsets matured further in supporting GPS. We are in the world of smart phones which have definitely been the talk of the town and have been penetrating into various market segments at a very high pace. There still seems to be a window of handsets out in the market which are not “smart” enough. Challenge here is to find applications where both these segments can interact and benefit. Is it possible? Well, yes to some extent and the main weapon being the SMS.

    While exploring this I came across many articles and examples that talk about SMS and location information processing. I take this opportunity in combining these two behaviors and come up with this application which finds the location or rather the street address whenever an SMS is received. Here the originator sends an SMS to a handset on which this android application is running. The app processes this request and finds the street address. The street detail is now communicated back to the originator using SMS. This way I have the street address of my friend by just using my legacy phone. Technically speaking, the application runs on the android phones. Originator need not have this android client installed.

    Now, let’s dive deep to understand how this application can be written.
    Listening to SMS:
    SMS is the trigger or an event on which the app will begin the processing. This calls for an event handling mechanism. This is achieved in the android framework with the help of intent receivers. In the android manifest file the receivers and the intent filters will be defined as below.
    <receiver android:enabled="true" android:name=".phonetracer">
    <intent-filter> <action  android:name="android.provider.Telephony.SMS_RECEIVED">  
    </intent-filter>
    </receiver>
    Application will need the permission from the android platform to receive SMS. This is done by defining the permissions in the manifest file.
    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
    Heart of this app is a broadcast receiver (here phonetracer) which will receive the broadcast messages that are delivered as intents. This class can be outlined as follows.
    public class phonetracer extends BroadcastReceiver {
        //Callback will be called whenever SMS is received.
        @Override
        public void onReceive(Context context, Intent intent) {
                //Take action here.
        }
    }
    Broadcast receiver's "onReceive" method needs to be overridden with the implementation specific to the application. Once the intent is received SMS message is parsed to extract the payload and the originating address. The originating address is the address on which the application will respond back once the processing is completed.
    Private static final String SMS_RECIEVED=”android.provider.Telephoney.SMS_RECEIVED”;/
    /---get the SMS message passed in---
    Bundle bundle = intent.getExtras();       
    SmsMessage[] msgs = null;

    String payload = null;
    if (intent.getAction().equals(SMS_RECEIVED)) {
        if (bundle != null){
         //---retrieve the SMS message received---
         Object[] pdus = (Object[]) bundle.get("pdus");
         msgs = new SmsMessage[pdus.length];           
         for (int i=0; i<msgs.length; i++){
            msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
            OrigAddress = msgs[i].getOriginatingAddress();
            payload = msgs[i].getMessageBody().toString().trim();
                            
         }
        }
    }
    Registering Location:
    Following permissions are required to be defined in the manifest file for operating the location services.

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    As discussed earlier, the location is derived when an SMS is received. But you don’t want this application to process on any SMS payload that is received. A keyword can be agreed upon and defined in the app. The keyword can be a simple text that a user can type in, for eg. “FindMe”. Application will filter the SMS processing based on this keyword.
    Registration of the location services is done with the help of a Location Manager handle that is obtained from the ‘getSystemService’ method call. A location listener is registered with the location manager which gets notified about the location. This listener will also get notified if the location changes.

    //Define a specific SMS code
    String code =“FindMe”                                                
    if(payload.equals(code)){
      LocationManager mlocManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    if(mlocManager != null )
       mlocManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, 5000, 0, mlocListener);
    }            
    MyLocationListener implements the interface LocationListener. On detection of location first time or any time there is a change in the location the onLocationChanged will be called with Location object. The latitude and longitude is now available in the Location object.

    public class MyLocationListener implements LocationListener {
                public void onLocationChanged(Location loc)
                {
                      if( loc != null ){
                            String result  = decodeLatLong(loc)   
                            sendSMS(OrigAddress,result);
                           }
                                                         
                   }
                public void onProviderDisabled(String provider){}          
                public void onProviderEnabled(String provider){}
                public void onStatusChanged(String provider, int status, Bundle extras){}
          }
    Decoding Location and Sending SMS
    The Lat and long information that is present in the Location data object should be reverse decoded to get the proper street address. This is done using android’s Geocoder.

        private String decodeLatLong(Location loc){
          String result = null;        
                Geocoder geocoder = new Geocoder(context, Locale.getDefault());
            
                try {
                       List<Address> list = geocoder.getFromLocation(
                                      loc.getLatitude(), loc.getLongitude(), 1);
                if (list != null && list.size() > 0) {
                    Address address = list.get(0);
                    // sending back first address line and locality
                    result = address.getAddressLine(0) + ", " + address.getLocality();
                 }
            } catch (IOException e) {
                //exception
              }
            return result;
       } 
    The decoded address will be sent out to the originating address by SMS. SMSManager provides a ‘sendTextMessage’ method to facilitate this.

    private void sendSMS(String phoneNumber, String message)
        {       
            SmsManager sms = SmsManager.getDefault();
            sms.sendTextMessage(phoneNumber, null, message, null, null);
     }  
    The android manifest file will need to have two more permission to perform the geo-decoding and the sending SMS out of the application.

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.SEND_SMS"/> 
    Before concluding this let me put a note on the list of imports required for this project.

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.location.Address;
    import android.location.Geocoder;
    import android.location.Location;
    import android.location.LocationListener;
    import android.location.LocationManager;
    import android.os.Bundle;
    import android.telephony.SmsManager;
    import android.telephony.SmsMessage;
    import android.widget.Toast;
    The last import in the above list i.e the android.widget.Toast can be used for debugging the flow in the form of alerts. A sample eg. of using “Toast” is as follows.

    Toast.makeText(context, “Your message”, Toast.LENGTH_SHORT).show();
    If you are no longer interested in location updates you may deregister using the Location Manager's "removeUpdates" call.

    As a concluding remark, this application can be extended further to make more interesting location based services. Please feel free to try this out and in case you face any problems do post in your queries.

    Happy coding…