Friday, March 1, 2013

Creating SoS Android Application in 30 minutes using TelephonyManager


There are number of free and paid 'SoS' applications in the Android market , which will help the user to send a message with location details to pre-configured numbers.

Let us check here how to develop such an Android application in less than 30 minutes.

Development Environment :
Android Development Tools Bundle
Build - v21.0.1-543035

We can start by creating an 'Android Application Project'



Click 'next' on this and the next two screens.
Select 'FullScreenActivity' in the 'Create Activity' page


Give the activity name as 'BachaoActivity' and click on 'Finish'
The generated activity will have some code for using systemUiHider, we can remove these as the functionality is not required.

After doing the cleanup , the activity code will be like below


package com.team.bachao;
package com.team.bachao;

import com.team.bachao.util.BachaoUtility;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

/**
 * An example full-screen activity that shows and hides the system UI (i.e.
 * status bar and navigation/system bar) with user interaction.
 * 
 */
public class BachaoActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_bachao);
 }

 @Override
 protected void onPostCreate(Bundle savedInstanceState) {
  super.onPostCreate(savedInstanceState);

 }

}

Next we need a add a on click listener to the button. This can be done by using android:onClick
Complete activity_bachao.xml layout content is given below

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#0099cc"
    tools:context=".BachaoActivity" >

    <!--
         This FrameLayout insets its children based on system windows using
         android:fitsSystemWindows.
    -->

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true" >

        <LinearLayout
            android:id="@+id/fullscreen_content_controls"
            style="?buttonBarStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical|center_horizontal"
            android:background="@color/black_overlay"
            android:orientation="horizontal"
            tools:ignore="UselessParent" >

            <Button
                android:id="@+id/dummy_button"
                style="?buttonBarButtonStyle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="@string/dummy_button"
                android:onClick="onBachao" />
        </LinearLayout>
    </FrameLayout>

</FrameLayout>

Now the basic stuff is available. Let us look at what needs to be done when user clicks on the SoS button.
First we need to read the network information like country code, operator ID and operator name, for this we  can use the TelephonyManager provided by android.


TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  if (telephonyManager != null) {
   operatorInfo.countryCode = telephonyManager.getNetworkCountryIso(); 
   operatorInfo.operatorID = telephonyManager.getNetworkOperator();
   operatorInfo.operatorName = telephonyManager.getNetworkOperatorName();
  } else {
   Toast.makeText(context, "Unable to read telephony manager"  
      , Toast.LENGTH_SHORT).show();
  }


Another information which will be useful to collect will be the cell location. But this will be possible only for the GSM connections

if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
    GsmCellLocation cellLocation = (GsmCellLocation)telephonyManager.getCellLocation();
    operatorInfo.cellID = cellLocation.getCid();
    operatorInfo.locationAreaCode = cellLocation.getLac();
   }


Next information we want to read is the location coordinates like Lat and Lon For this we can use the LocationManager provided by Android
final LocationManager locationManager;
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

Using the location manager instance, we can find the list of location providers available in the device. Some of the possible providers are GPS_PROVIDER - Use GPS to find the location NETWORK_PROVIDER - Use Network to find the location PASSIVE_PROVIDER - This is of not much use to us in this context.
List<String> providerNames = locationManager.getProviders(true);
  
  String providerName = null;
  if (providerNames != null && providerNames.size() > 0) {
   if (providerNames.contains(LocationManager.GPS_PROVIDER)) {
    providerName = LocationManager.GPS_PROVIDER;
   } else if (providerNames.contains(LocationManager.NETWORK_PROVIDER)) {
    providerName = LocationManager.NETWORK_PROVIDER;
   }
  }


We can also find the provider from location manager which is matching a set of conditions.
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
criteria.setAltitudeRequired(false);
criteria.setPowerRequirement(Criteria.POWER_LOW);
String providerName = locationManager.getBestProvider(criteria, true);

Once the provider is available we can request for the current location information. But for this we need to use a location listener.
location = locationManager.getLastKnownLocation(providerName);
   
   LocationListener locationListener = new LocationListener() {

   public void onLocationChanged(Location location) {
    Toast.makeText(context, "on location changed " , Toast.LENGTH_SHORT).show();
    
    locationManager.removeUpdates(this);
    processLocation(context, location);
   }

   public void onProviderDisabled(String arg0) {
    Log.e("WTF", "provider disabled!");
    Toast.makeText(context, "disabled " , Toast.LENGTH_SHORT).show();
   }

   public void onProviderEnabled(String arg0) {
    // do nothing.
   }

   public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
    // Do nothing.
   }
   
  };
  
  locationManager.requestLocationUpdates(providerName, 0, 0, locationListener);

onLocationChanged method will be called when the new location is available from the provider.

Now we have all the information available to send to the emergency contacts. So let us send an SMS with this information. For this we can use the SmsManager provided by Android
StringBuilder sb = new StringBuilder();
  sb.append("Emergency! Please reach out\n");
  if (location != null) {
   sb.append("Lat : " + location.getLatitude() + " Lon : " + location.getLongitude());
  }
  sb.append(" CountryCode : " + operatorInfo.countryCode);
  sb.append(" OperatorID : " + operatorInfo.operatorID);
  sb.append(" OperatorName : " + operatorInfo.operatorName);
  sb.append(" \nCellID : " + operatorInfo.cellID);
  sb.append(" LocationAreaCode : " + operatorInfo.locationAreaCode);

  SmsManager smsManager = SmsManager.getDefault();
  smsManager.sendTextMessage("%emergencyContactNumber%", null, sb.toString(), null, null);


Replace %emergencyContactNumber% with actual mobile number. Another way to send SMS is through using Intent, but this will only open the default SMS application with the message , will not send it.
Intent smsIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("sms:%emergencyContactNumber%"));
smsIntent.putExtra("sms_body", sb.toString());
context.startActivity(smsIntent);


But before we use test this application, required permission requests must be added in the AndroidManifest file
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION">
<uses-permission android:name="android.permission.READ_PHONE_STATE">
<uses-permission android:name="android.permission.SEND_SMS">
</uses-permission></uses-permission></uses-permission></uses-permission>


Complete code for BachaoActivity.java
package com.team.bachao;

import com.team.bachao.util.BachaoUtility;
import com.team.bachao.util.SystemUiHider;

import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

/**
 * An example full-screen activity that shows and hides the system UI (i.e.
 * status bar and navigation/system bar) with user interaction.
 * 
 * @see SystemUiHider
 */
public class BachaoActivity extends Activity {
 private BachaoUtility handler;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  setContentView(R.layout.activity_bachao);
  handler = new BachaoUtility(BachaoActivity.this);
  Toast.makeText(BachaoActivity.this, "Initialized", Toast.LENGTH_LONG).show();
 }

 @Override
 protected void onPostCreate(Bundle savedInstanceState) {
  super.onPostCreate(savedInstanceState);

 }

 public void onBachao(View view) {
  handler.bachao();
 }
 
}

Complete code for BachaoUtility.java
package com.team.bachao.util;

import java.util.List;

import com.team.bachao.BachaoActivity;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.widget.Toast;

public class BachaoUtility {
 
 private Location location;
 private OperatorInfo operatorInfo;
 private BachaoActivity context;
 
 public BachaoUtility(BachaoActivity context) {
  this.context = context;
 }
 
 public void bachao() {
  operatorInfo = new OperatorInfo(); 
  TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  if (telephonyManager != null) {
   operatorInfo.countryCode = telephonyManager.getNetworkCountryIso(); 
   operatorInfo.operatorID = telephonyManager.getNetworkOperator();
   operatorInfo.operatorName = telephonyManager.getNetworkOperatorName();
   if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
    GsmCellLocation cellLocation = (GsmCellLocation)telephonyManager.getCellLocation();
    operatorInfo.cellID = cellLocation.getCid();
    operatorInfo.locationAreaCode = cellLocation.getLac();
   }
  } else {
   Toast.makeText(context, "Unable to read telephony manager"  
      , Toast.LENGTH_SHORT).show();
  }
  
  //
  // Get the location service.
  
  final LocationManager locationManager;
  locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
  
  
  //
  // Get location coordinates
//  Criteria criteria = new Criteria();
//  criteria.setAccuracy(Criteria.ACCURACY_COARSE);
//  criteria.setAltitudeRequired(false);
//  criteria.setPowerRequirement(Criteria.POWER_LOW);
//  String providerName = locationManager.getBestProvider(criteria, true);
  List<String> providerNames = locationManager.getProviders(true);
  
  String providerName = null;
  if (providerNames != null && providerNames.size() > 0) {
   if (providerNames.contains(LocationManager.GPS_PROVIDER)) {
    providerName = LocationManager.GPS_PROVIDER;
   } else if (providerNames.contains(LocationManager.NETWORK_PROVIDER)) {
    providerName = LocationManager.NETWORK_PROVIDER;
   }
  }

   
  if (providerName != null) {
   location = locationManager.getLastKnownLocation(providerName);
   
   LocationListener locationListener = new LocationListener() {

   public void onLocationChanged(Location location) {
    Toast.makeText(context, "on location changed " , Toast.LENGTH_SHORT).show();
    
    locationManager.removeUpdates(this);
    processLocation(context, location);
   }

   public void onProviderDisabled(String arg0) {
    Log.e("WTF", "provider disabled!");
    Toast.makeText(context, "disabled " , Toast.LENGTH_SHORT).show();
   }

   public void onProviderEnabled(String arg0) {
    // do nothing.
   }

   public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
    // Do nothing.
   }
   
  };
  
  locationManager.requestLocationUpdates(providerName, 0, 0, locationListener);
  }
  
  sendMessage();

  //
  // Send facebook update
  
  //
  // Send twitter update
  
  //
  // record audio
  
  //
  // record video
  
  //
  // Send mail
 }
 
 public void processLocation(BachaoActivity context, Location location) {
  Toast.makeText(context, "Lat : " + location.getLatitude()
    + "\n Lon : " + location.getLatitude(), Toast.LENGTH_LONG).show();
  this.location = location;
  //
  // Send message
  sendMessage();
  
 }

 private void sendMessage() {
  StringBuilder sb = new StringBuilder();
  sb.append("Emergency! Please reach out\n");
  if (location != null) {
   sb.append("Lat : " + location.getLatitude() + " Lon : " + location.getLongitude());
  }
  sb.append(" CountryCode : " + operatorInfo.countryCode);
  sb.append(" OperatorID : " + operatorInfo.operatorID);
  sb.append(" OperatorName : " + operatorInfo.operatorName);
  sb.append(" \nCellID : " + operatorInfo.cellID);
  sb.append(" LocationAreaCode : " + operatorInfo.locationAreaCode);

//  Intent smsIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("sms:%emergencyContactNumber%"));
//  smsIntent.putExtra("sms_body", sb.toString());
//  context.startActivity(smsIntent);
  
  SmsManager smsManager = SmsManager.getDefault();
  smsManager.sendTextMessage("%emergencyContactNumber%", null, sb.toString(), null, null);
 }
}

In the next version I will integrate audio/ video recording, sending the data via mail , updating in facebook/twitter.

26 comments:

  1. This is interesting. Nice one Ratheesh...!
    @
    rajesh.m.nair88@gmail.com

    ReplyDelete
  2. what is OperatorInfo Class in this activity and can you give me complete source code link of this project

    ReplyDelete
    Replies
    1. OperatorInfo is a utility class I created for holding the different information. There is no other business logic in that.
      Will try to upload the full source code soon.

      Delete
    2. This comment has been removed by the author.

      Delete
    3. have you uploaded the full source code yet?

      Delete
  3. This comment has been removed by a blog administrator.

    ReplyDelete
  4. hello ratheesh.
    can u send mefull source code.
    this my email
    mohammadkamil1991@gmail.com

    ReplyDelete
  5. can u send me the full source code
    anu.meshram@gmail.com

    ReplyDelete
  6. This comment has been removed by the author.

    ReplyDelete
  7. hello,

    Can you please send the full source code? or can you contact me at yatinkundra@icloud.com so that we can discuss about more apps, I am looking for?

    Probably be a business deal?

    ReplyDelete
  8. Hi All,

    Thank you for your comments. I will upload the complete source code soon.

    Meanwhile if you are interested in forwarding all or selected text messages to email then try this Android app https://play.google.com/store/apps/details?id=com.fortytwo.mytextstoemail

    ReplyDelete
  9. What is operator info class? Can i get complete source code. Please

    ReplyDelete
  10. thanks for the post
    but can you send me the full source code at
    smeet337@gmail.com

    ReplyDelete
  11. thanks for the post
    but can you send me the full source code at
    smeet337@gmail.com

    ReplyDelete
  12. thanks for the post
    but can you send me the full source code at

    balubhaskar15@gmail.com

    ReplyDelete
  13. Can i get complete source code. Please

    indikagdf@gmail.com

    ReplyDelete
  14. caN u send The Full Source code at moonprasad22@gmail.com

    ReplyDelete
  15. nice 1 ..its pretty helpful.......

    ReplyDelete
  16. This comment has been removed by the author.

    ReplyDelete
  17. @author Is this exactly the same SOS service that most of the popular women safety apps uses?

    ReplyDelete
  18. Can you plz provide the complete source code

    ReplyDelete
  19. can anyone help with the operatorInfo....please

    ReplyDelete
  20. please send the source code to manjunath7068@gmail.com

    ReplyDelete
  21. I can pay for some edits and more functions in this app contact me at
    zoroasd92@gmail.com

    ReplyDelete