Skip to content
geeksforgeeks
  • Tutorials
    • Python
    • Java
    • Data Structures & Algorithms
    • ML & Data Science
    • Interview Corner
    • Programming Languages
    • Web Development
    • CS Subjects
    • DevOps And Linux
    • School Learning
    • Practice Coding Problems
  • Courses
    • DSA to Development
    • Get IBM Certification
    • Newly Launched!
      • Master Django Framework
      • Become AWS Certified
    • For Working Professionals
      • Interview 101: DSA & System Design
      • Data Science Training Program
      • JAVA Backend Development (Live)
      • DevOps Engineering (LIVE)
      • Data Structures & Algorithms in Python
    • For Students
      • Placement Preparation Course
      • Data Science (Live)
      • Data Structure & Algorithm-Self Paced (C++/JAVA)
      • Master Competitive Programming (Live)
      • Full Stack Development with React & Node JS (Live)
    • Full Stack Development
    • Data Science Program
    • All Courses
  • Java Arrays
  • Java Strings
  • Java OOPs
  • Java Collection
  • Java 8 Tutorial
  • Java Multithreading
  • Java Exception Handling
  • Java Programs
  • Java Project
  • Java Collections Interview
  • Java Interview Questions
  • Java MCQs
  • Spring
  • Spring MVC
  • Spring Boot
  • Hibernate
Open In App
Next Article:
How to Build a SOS Mobile Application in Android Studio?
Next article icon

How to Build a SOS Mobile Application in Android Studio?

Last Updated : 23 May, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

The SOS applications are basically advanced emergency apps that can rescue you and/or your loved ones if you and/or they find themselves in a life-threatening emergency situation and need immediate assistance. When you need some personal assistance, you can actually turn on your phone and can call or message someone for help. But in a life-threatening emergency like attack, sexual assault, robbery, harassment, accident, fire, birth assistance, we don’t have time to open our phone, instead, we need some accessibility methods by which we can reach out for help without actually operating the phone. In this article, we would be building such an application for android.

How-to-Build-a-SOS-Mobile-Application-in-Android-Studio

Can you think of some easiest ways of stimulating some functions in your phone, without actually turning ON your phone’s screen? One such way is by shaking your phone. We will be creating a service, and in that service, we would listen for a Shake Event by the phone. When we register a shake event i.e., when the user shakes the phone, we would send the user’s location with a predefined message to all the contacts which the user has previously added to the app. Now with every release of Android, Google has made some strict regulations regarding fetching of the User’s location, and also it is important when it comes to data security. And this makes it difficult to create such an SOS application for newer versions of Android.

Pre-requisites

  • Services,
  • Runtime Permission Management,
  • Populating ListView,
  • Broadcast Receiver,
  • Database Management in Android

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.

Step 2: Creating the Contacts Module

Create a folder Contacts, in this, we will handle all the files which would populate the ListView by the contacts that the user selects to send messages at the time of emergency.

Step 2.1: Creating model class for Contact

Create a model class that will hold the data of Contact, mainly Name and Phone Number. Apart from the usual constructor and getters and setters, we have an additional validate(String) method. This method checks if the retrieved phone number is in the correct format(+91XXXXXXXXXX) or not. If not, then it converts the string and returns the formatted string. Below is the code for the ContactModel.java file.

Java
public class ContactModel {     private int id;     private String phoneNo;     private String name;      // constructor     public ContactModel(int id, String name, String phoneNo) {         this.id = id;         this.phoneNo = validate(phoneNo);         this.name = name;     }      // validate the phone number, and reformat is necessary     private String validate(String phone) {                  // creating StringBuilder for both the cases         StringBuilder case1 = new StringBuilder("+91");         StringBuilder case2 = new StringBuilder("");          // check if the string already has a "+"         if (phone.charAt(0) != '+') {             for (int i = 0; i < phone.length(); i++) {                 // remove any spaces or "-"                 if (phone.charAt(i) != '-' && phone.charAt(i) != ' ') {                     case1.append(phone.charAt(i));                 }             }             return case1.toString();         } else {             for (int i = 0; i < phone.length(); i++) {                 // remove any spaces or "-"                 if (phone.charAt(i) != '-' || phone.charAt(i) != ' ') {                     case2.append(phone.charAt(i));                 }             }             return case2.toString();         }      }      public String getPhoneNo() {         return phoneNo;     }      public int getId() {         return id;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     } } 

 
 

Step 2.2: Creating a Database Helper class


 

We need to store all the contacts, the user selects, into a Database so that it is available every time the app needs it. We will populate the ListView with the help of this database and also at the time of sending messages, we will retrieve the contacts in a list from this database. Below is the code for the DbHelper.java file.


 

Java
import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;  import java.util.ArrayList; import java.util.List;  public class DbHelper extends SQLiteOpenHelper {          // Database Version     private static final int DATABASE_VERSION = 1;      // Database Name     private static final String DATABASE_NAME = "contactdata";      // Country table name     private static final String TABLE_NAME= "contacts";      // Country Table Columns names     private static final String KEY_ID = "id";     private static final String NAME = "Name";     private static final String PHONENO = "PhoneNo";       public DbHelper(Context context){         super(context, DATABASE_NAME, null, DATABASE_VERSION);     }      @Override     public void onCreate(SQLiteDatabase db) {          // create the table for the first time         String CREATE_COUNTRY_TABLE = "CREATE TABLE " + TABLE_NAME + "("                 + KEY_ID + " INTEGER PRIMARY KEY," + NAME + " TEXT,"                 + PHONENO + " TEXT" + ")";         db.execSQL(CREATE_COUNTRY_TABLE);     }      @Override     public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {      }      // method to add the contact     public void addcontact(ContactModel contact){         SQLiteDatabase db=this.getWritableDatabase();         ContentValues c=new ContentValues();         c.put(NAME,contact.getName());         c.put(PHONENO,contact.getPhoneNo());         db.insert(TABLE_NAME,null,c);         db.close();     }      // method to retrieve all the contacts in List     public List<ContactModel> getAllContacts(){         List<ContactModel> list=new ArrayList<>();         String query="SELECT * FROM "+TABLE_NAME;         SQLiteDatabase db=this.getReadableDatabase();         Cursor c=db.rawQuery(query,null);         if(c.moveToFirst()) {             do {                  list.add(new ContactModel(c.getInt(0),c.getString(1),c.getString(2)));              } while (c.moveToNext());         }         return list;     }      // get the count of data, this will allow user     // to not add more that five contacts in database     public int count(){         int count=0;         String query="SELECT COUNT(*) FROM "+TABLE_NAME;         SQLiteDatabase db = this.getReadableDatabase();         Cursor c=db.rawQuery(query,null);         if(c.getCount()>0){             c.moveToFirst();             count=c.getInt(0);         }         c.close();         return count;     }      // Deleting single country     public void deleteContact(ContactModel contact) {         SQLiteDatabase db = this.getWritableDatabase();         int i=db.delete(TABLE_NAME,KEY_ID + " = ?",                 new String[] { String.valueOf(contact.getId()) });          db.close();     } } 

 
 

Step 2.3: Creating a CustomAdapter.java


 

In order to handle the data in ListView, we will need a Customised Adapter. We will add a LongClickListener on the LinerLayout so that whenever a user wants to delete an existing item from ListView he can simply long-press that item. And in return, we would show a dialog asking for confirmation. As the user confirms, we will also delete that item from the database too. Below is the code for the CustomAdapter.java file.


 

Java
import android.content.Context; import android.content.DialogInterface; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast;  import androidx.annotation.NonNull;  import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.raghav.sos.R;  import java.util.List;  public class CustomAdapter extends ArrayAdapter<ContactModel> {      Context context;     List<ContactModel> contacts;      public CustomAdapter(@NonNull Context context, List<ContactModel> contacts) {         super(context, 0, contacts);         this.context = context;         this.contacts = contacts;     }      @Override     public View getView(int position, View convertView, ViewGroup parent) {          // create a database helper object          // to handle the database manipulations         DbHelper db = new DbHelper(context);          // Get the data item for this position         ContactModel c = getItem(position);                  // Check if an existing view is being reused, otherwise inflate the view         if (convertView == null) {             convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, parent, false);         }          LinearLayout linearLayout = (LinearLayout) convertView.findViewById(R.id.linear);          // Lookup view for data population         TextView tvName = (TextView) convertView.findViewById(R.id.tvName);         TextView tvPhone = (TextView) convertView.findViewById(R.id.tvPhone);                  // Populate the data into the template         // view using the data object         tvName.setText(c.getName());         tvPhone.setText(c.getPhoneNo());          linearLayout.setOnLongClickListener(new View.OnLongClickListener() {             @Override             public boolean onLongClick(View view) {                 // generate an MaterialAlertDialog Box                 new MaterialAlertDialogBuilder(context)                         .setTitle("Remove Contact")                         .setMessage("Are you sure want to remove this contact?")                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {                             @Override                             public void onClick(DialogInterface dialogInterface, int i) {                                 // delete the specified contact from the database                                 db.deleteContact(c);                                 // remove the item from the list                                 contacts.remove(c);                                 // notify the listview that dataset has been changed                                 notifyDataSetChanged();                                 Toast.makeText(context, "Contact removed!", Toast.LENGTH_SHORT).show();                             }                         })                         .setNegativeButton("NO", new DialogInterface.OnClickListener() {                             @Override                             public void onClick(DialogInterface dialogInterface, int i) {                              }                         })                         .show();                 return false;             }         });         // Return the completed view to render on screen         return convertView;     }      // this method will update the ListView     public void refresh(List<ContactModel> list) {         contacts.clear();         contacts.addAll(list);         notifyDataSetChanged();     } } 

 
 

Step 2.4: item_user.xml


 

Layout file for each item in ListView.


 

XML
<?xml version="1.0" encoding="utf-8"?> <LinearLayout      xmlns:android="http://schemas.android.com/apk/res/android"     android:id="@+id/linear"     android:layout_width="match_parent"     android:layout_height="wrap_content"     android:orientation="vertical"     android:padding="4dp">      <androidx.cardview.widget.CardView         android:id="@+id/cardview"         android:layout_width="match_parent"         android:layout_height="wrap_content">          <LinearLayout             android:layout_width="match_parent"             android:layout_height="match_parent"             android:orientation="vertical">              <TextView                 android:id="@+id/tvName"                 style="@style/TextAppearance.AppCompat.Medium"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:text="Name"                 android:textColor="@color/design_default_color_secondary" />              <TextView                 android:id="@+id/tvPhone"                 style="@style/TextAppearance.AppCompat.Large"                 android:layout_width="match_parent"                 android:layout_height="wrap_content"                 android:text="Phone"                 android:textColor="@color/design_default_color_secondary_variant"                 android:textStyle="bold" />                      </LinearLayout>              </androidx.cardview.widget.CardView>      </LinearLayout> 

 
 

Step 3: Creating the Service Module


 

This module would contain all the necessary functionality for shake detection, running services, and registering a Broadcast Receiver.


 

Step 3.1: Creating ShakeDetector class


 

Here we implement the SensorEventListener which is used for receiving notifications from the SensorManager when there is a new or change in sensor data. Now, in order to register the shake event, the G-Force by which the sensor experience when the user shakes the phone must be greater than 1G. This is because there may be cases when the phone might shake while in the pocket, or in the car, etc. And also to resolve this drawback we include a count mechanism that would count the number of shakes, i.e. if the user shakes the device 3 consecutive times, then we would register a shake event. And to do so the time between the two successive shakes should be minimum i.e. around 500ms. We will also make the shake count to zero after 3 seconds of inactivity. This would allow the user to again shake the phone to send messages. Below is the code for the ShakeDetector.java file.


 

Java
import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager;  public class ShakeDetector implements SensorEventListener {      /*      * The gForce that is necessary to register as shake.      * Must be greater than 1G (one earth gravity unit).      * You can install "G-Force", by Blake La Pierre      * from the Google Play Store and run it to see how      *  many G's it takes to register a shake      */     private static final float SHAKE_THRESHOLD_GRAVITY = 2.7F;     private static final int SHAKE_SLOP_TIME_MS = 500;     private static final int SHAKE_COUNT_RESET_TIME_MS = 3000;      private OnShakeListener mListener;     private long mShakeTimestamp;     private int mShakeCount;      public void setOnShakeListener(OnShakeListener listener) {         this.mListener = listener;     }      public interface OnShakeListener {         public void onShake(int count);     }      @Override     public void onAccuracyChanged(Sensor sensor, int accuracy) {         // ignore     }      @Override     public void onSensorChanged(SensorEvent event) {          if (mListener != null) {             float x = event.values[0];             float y = event.values[1];             float z = event.values[2];              float gX = x / SensorManager.GRAVITY_EARTH;             float gY = y / SensorManager.GRAVITY_EARTH;             float gZ = z / SensorManager.GRAVITY_EARTH;              // gForce will be close to 1 when there is no movement.             Float f = new Float(gX * gX + gY * gY + gZ * gZ);             Double d = Math.sqrt(f.doubleValue());             float gForce = d.floatValue();              if (gForce > SHAKE_THRESHOLD_GRAVITY) {                 final long now = System.currentTimeMillis();                 // ignore shake events too close to each other (500ms)                 if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now) {                     return;                 }                  // reset the shake count after 3 seconds of no shakes                 if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now) {                     mShakeCount = 0;                 }                  mShakeTimestamp = now;                 mShakeCount++;                  mListener.onShake(mShakeCount);             }         }     } } 

 
 

Step 3.2: Creating the SensorService


 

Creating a sensor service. From the commencement of Android 6, Google has included some extra security checks regarding background services. Now handling of services is completely different as it was done previously. 


 

Our main focus while building this app should be on how we can keep the service running even when the app is not running (even removed from the recent).


 

For service to run while the host application is Dead, is called Service to run in the background. And to make a service run in the background we need some extra permissions. In Android O and above we can’t have background service, instead, we can use the Foreground Services. Foreground services perform operations that are noticeable to the user.  


 

A status bar warning with a priority of PRIORITY LOW or higher must be shown for each foreground operation. Users would be conscious that the app is running in the foreground and using machine resources in this manner. Unless the service is discontinued or withdrawn from the foreground, the message cannot be ignored.


 

Java
@RequiresApi(Build.VERSION_CODES.O) private void startMyOwnForeground() {     String NOTIFICATION_CHANNEL_ID = "example.permanence";     String channelName = "Background Service";     NotificationChannel chan = new NotificationChannel(         NOTIFICATION_CHANNEL_ID, channelName,         NotificationManager.IMPORTANCE_MIN);      NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);     assert manager != null;     manager.createNotificationChannel(chan);      NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);     Notification notification = notificationBuilder.setOngoing(true)               .setContentTitle("You are protected.")               .setContentText("We are there for you")                // this is important, otherwise the               // notification will show the way you want i.e.               // it will show some default notification               .setSmallIcon(R.drawable.ic_launcher_foreground)               .setPriority(NotificationManager.IMPORTANCE_MIN)               .setCategory(Notification.CATEGORY_SERVICE)               .build();     startForeground(2, notification); } 

 
 

If you start a service starts with the START STICKY return type, it will run in the background even if the host activity is not running in the foreground. If Android has to forcibly close a program due to a memory error or other reasons, the service will be restarted without the user's intervention.


 

Java
    @Override     public int onStartCommand(Intent intent, int flags, int startId) {         super.onStartCommand(intent, flags, startId);          return START_STICKY;     } 

 
 

In order to make the user aware that the Shake event has been registered or say the messages have been delivered, we create a vibrate() method. This will make the phone vibrate in a defined wave format.


 

Java
// method to vibrate the phone public void vibrate() {     final Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);        VibrationEffect vibEff;     // Android Q and above have some predefined vibrating     // patterns     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {         vibEff = VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK);         vibrator.cancel();         vibrator.vibrate(vibEff);     }     else {         vibrator.vibrate(500);     } } 

 
 

Now, in order to fetch the user location, we will use FusedLocationProviderClient. The FusedLocationProviderClient has a function named getCurremtLocation(). This method provides the current location of the user when asked for. But this method requires the mobile phone’s GPS to be turned ON. Otherwise, it will return a null location. From Android O and above, in order to fetch location or anything which would reveal the user’s location to the app, the location services or the GPS should be turned ON. So, that the user is aware of the location usages by the app.


 

Java
FusedLocationProviderClient fusedLocationClient = LocationServices.getFusedLocationProviderClient(getApplicationContext());  fusedLocationClient.getCurrentLocation(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY, new CancellationToken() {             @Override             public boolean isCancellationRequested()             {                 return false;             }             @NonNull             @Override             public CancellationToken onCanceledRequested(                 @NonNull OnTokenCanceledListener                     onTokenCanceledListener)             {                 return null;             }         })     .addOnSuccessListener(         new OnSuccessListener<Location>() {             @Override             public void onSuccess(Location location)             {                 // check if location is null                 // for both the cases we will create                 // different messages                 if (location != null) {                     ...                 }                 else {                     ...                 }             }         })     .addOnFailureListener(new OnFailureListener() {         @Override         public void onFailure(@NonNull Exception e)         {             ...         }     }); 

 
 

Further, when we retrieve the location successfully, we Create a SMSManager object which will help us to send messages to all the contacts from the database. In case the user’s location services are not ON, we can generate a different message without the coordinates. So that one receiving the emergency message knows that the host device didn’t have location services on at that moment. He can then directly coordinate with the nearby Police department who can then track the person’s device location.


 

Java
SmsManager smsManager = SmsManager.getDefault(); DbHelper db = new DbHelper(SensorService.this); List<ContactModel> list = db.getAllContacts(); for (ContactModel c : list) {     String message         = "Hey, " + c.getName()           + "I am in DANGER, i need help. Please urgently reach me out. Here are my coordinates.\n "           + "http://maps.google.com/?q="           + location.getLatitude() + ","           + location.getLongitude();     smsManager.sendTextMessage(c.getPhoneNo(), null,                                message, null, null); } 

 
 

Till now whatever we have done will work until the activity is in Foreground or Running. But what when the user kills the application? Or locks the phone? For this, we create a BroadcastReceiver.


 

Below is the complete code for the SensorService.java file.


 

Java
import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.hardware.Sensor; import android.hardware.SensorManager; import android.location.Location; import android.os.Build; import android.os.IBinder; import android.os.VibrationEffect; import android.os.Vibrator; import android.telephony.SmsManager; import android.util.Log;  import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.app.NotificationCompat;  import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import com.google.android.gms.tasks.CancellationToken; import com.google.android.gms.tasks.OnFailureListener; import com.google.android.gms.tasks.OnSuccessListener; import com.google.android.gms.tasks.OnTokenCanceledListener; import com.raghav.sos.Contacts.ContactModel; import com.raghav.sos.Contacts.DbHelper; import com.raghav.sos.R;  import java.util.List;  public class SensorService extends Service {      private SensorManager mSensorManager;     private Sensor mAccelerometer;     private ShakeDetector mShakeDetector;      public SensorService() {     }      @Override     public IBinder onBind(Intent intent) {         // TODO: Return the communication channel to the service.         throw new UnsupportedOperationException("Not yet implemented");     }      @Override     public int onStartCommand(Intent intent, int flags, int startId) {         super.onStartCommand(intent, flags, startId);          return START_STICKY;     }      @Override     public void onCreate() {                  super.onCreate();          // start the foreground service         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)             startMyOwnForeground();         else             startForeground(1, new Notification());          // ShakeDetector initialization         mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);         mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);         mShakeDetector = new ShakeDetector();         mShakeDetector.setOnShakeListener(new ShakeDetector.OnShakeListener() {              @SuppressLint("MissingPermission")             @Override             public void onShake(int count) {                 // check if the user has shacked                 // the phone for 3 time in a row                 if (count == 3) {                                          // vibrate the phone                     vibrate();                      // create FusedLocationProviderClient to get the user location                     FusedLocationProviderClient fusedLocationClient = LocationServices.getFusedLocationProviderClient(getApplicationContext());                                          // use the PRIORITY_BALANCED_POWER_ACCURACY                     // so that the service doesn't use unnecessary power via GPS                     // it will only use GPS at this very moment                     fusedLocationClient.getCurrentLocation(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY, new CancellationToken() {                         @Override                         public boolean isCancellationRequested() {                             return false;                         }                          @NonNull                         @Override                         public CancellationToken onCanceledRequested(@NonNull OnTokenCanceledListener onTokenCanceledListener) {                             return null;                         }                     }).addOnSuccessListener(new OnSuccessListener<Location>() {                         @Override                         public void onSuccess(Location location) {                             // check if location is null                             // for both the cases we will                             // create different messages                             if (location != null) {                                                                  // get the SMSManager                                 SmsManager smsManager = SmsManager.getDefault();                                                                  // get the list of all the contacts in Database                                 DbHelper db = new DbHelper(SensorService.this);                                 List<ContactModel> list = db.getAllContacts();                                                                  // send SMS to each contact                                 for (ContactModel c : list) {                                     String message = "Hey, " + c.getName() + "I am in DANGER, i need help. Please urgently reach me out. Here are my coordinates.\n " + "http://maps.google.com/?q=" + location.getLatitude() + "," + location.getLongitude();                                     smsManager.sendTextMessage(c.getPhoneNo(), null, message, null, null);                                 }                             } else {                                 String message = "I am in DANGER, i need help. Please urgently reach me out.\n" + "GPS was turned off.Couldn't find location. Call your nearest Police Station.";                                 SmsManager smsManager = SmsManager.getDefault();                                 DbHelper db = new DbHelper(SensorService.this);                                 List<ContactModel> list = db.getAllContacts();                                 for (ContactModel c : list) {                                     smsManager.sendTextMessage(c.getPhoneNo(), null, message, null, null);                                 }                             }                         }                     }).addOnFailureListener(new OnFailureListener() {                         @Override                         public void onFailure(@NonNull Exception e) {                             Log.d("Check: ", "OnFailure");                             String message = "I am in DANGER, i need help. Please urgently reach me out.\n" + "GPS was turned off.Couldn't find location. Call your nearest Police Station.";                             SmsManager smsManager = SmsManager.getDefault();                             DbHelper db = new DbHelper(SensorService.this);                             List<ContactModel> list = db.getAllContacts();                             for (ContactModel c : list) {                                 smsManager.sendTextMessage(c.getPhoneNo(), null, message, null, null);                             }                         }                     });                  }             }         });          // register the listener         mSensorManager.registerListener(mShakeDetector, mAccelerometer, SensorManager.SENSOR_DELAY_UI);     }      // method to vibrate the phone     public void vibrate() {                  final Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);         VibrationEffect vibEff;                  // Android Q and above have some predefined vibrating patterns         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {             vibEff = VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK);             vibrator.cancel();             vibrator.vibrate(vibEff);         } else {             vibrator.vibrate(500);         }       }      // For Build versions higher than Android Oreo, we launch     // a foreground service in a different way. This is due to the newly     // implemented strict notification rules, which require us to identify     // our own notification channel in order to view them correctly.     @RequiresApi(Build.VERSION_CODES.O)     private void startMyOwnForeground() {         String NOTIFICATION_CHANNEL_ID = "example.permanence";         String channelName = "Background Service";         NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_MIN);          NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);         assert manager != null;         manager.createNotificationChannel(chan);          NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);         Notification notification = notificationBuilder.setOngoing(true)                 .setContentTitle("You are protected.")                 .setContentText("We are there for you")                  // this is important, otherwise the notification will show the way                 // you want i.e. it will show some default notification                 .setSmallIcon(R.drawable.ic_launcher_foreground)                  .setPriority(NotificationManager.IMPORTANCE_MIN)                 .setCategory(Notification.CATEGORY_SERVICE)                 .build();         startForeground(2, notification);     }      @Override     public void onDestroy() {         // create an Intent to call the Broadcast receiver         Intent broadcastIntent = new Intent();         broadcastIntent.setAction("restartservice");         broadcastIntent.setClass(this, ReactivateService.class);         this.sendBroadcast(broadcastIntent);         super.onDestroy();     }  } 

 
 

Step 3.3: Creating the Broadcast Receiver


 

Whenever a service is destroyed, the onDestroy method is called, and we will use that method to call a broadcast receiver before the service is actually destroyed. 


 

The broadcast receiver in return again starts the service. And our problem is resolved!! The service now runs without the host activity in the background.


 

Below is the complete code for the ReactivateService.java file.


 

Java
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Build; import android.util.Log;  public class ReactivateService extends BroadcastReceiver {      @Override     public void onReceive(Context context, Intent intent) {         Log.d("Check: ","Receiver Started");         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {             context.startForegroundService(new Intent(context, SensorService.class));         } else {             context.startService(new Intent(context, SensorService.class));         }     } } 

 
 

Step 4: Working with the MainActivity


 

Go to the MainActivity.java file and refer to the following code. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.


 

Java
import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.PowerManager; import android.provider.ContactsContract; import android.provider.Settings; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.ListView; import android.widget.Toast;  import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat;  import com.raghav.sos.Contacts.ContactModel; import com.raghav.sos.Contacts.CustomAdapter; import com.raghav.sos.Contacts.DbHelper; import com.raghav.sos.ShakeServices.ReactivateService; import com.raghav.sos.ShakeServices.SensorService;  import java.util.List;  public class MainActivity extends AppCompatActivity {      private static final int IGNORE_BATTERY_OPTIMIZATION_REQUEST = 1002;     private static final int PICK_CONTACT = 1;      // create instances of various classes to be used     Button button1;     ListView listView;     DbHelper db;     List<ContactModel> list;     CustomAdapter customAdapter;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          // check for runtime permissions         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {             if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_DENIED) {                 requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.SEND_SMS, Manifest.permission.READ_CONTACTS}, 100);             }         }          // this is a special permission required only by devices using         // Android Q and above. The Access Background Permission is responsible         // for populating the dialog with "ALLOW ALL THE TIME" option         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {             requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 100);         }          // check for BatteryOptimization,         PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {             if (pm != null && !pm.isIgnoringBatteryOptimizations(getPackageName())) {                 askIgnoreOptimization();             }         }          // start the service         SensorService sensorService = new SensorService();         Intent intent = new Intent(this, sensorService.getClass());         if (!isMyServiceRunning(sensorService.getClass())) {             startService(intent);         }           button1 = findViewById(R.id.Button1);         listView = (ListView) findViewById(R.id.ListView);         db = new DbHelper(this);         list = db.getAllContacts();         customAdapter = new CustomAdapter(this, list);         listView.setAdapter(customAdapter);          button1.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 // calling of getContacts()                 if (db.count() != 5) {                     Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);                     startActivityForResult(intent, PICK_CONTACT);                 } else {                     Toast.makeText(MainActivity.this, "Can't Add more than 5 Contacts", Toast.LENGTH_SHORT).show();                 }             }         });     }      // method to check if the service is running     private boolean isMyServiceRunning(Class<?> serviceClass) {         ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);         for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {             if (serviceClass.getName().equals(service.service.getClassName())) {                 Log.i("Service status", "Running");                 return true;             }         }         Log.i("Service status", "Not running");         return false;     }      @Override     protected void onDestroy() {         Intent broadcastIntent = new Intent();         broadcastIntent.setAction("restartservice");         broadcastIntent.setClass(this, ReactivateService.class);         this.sendBroadcast(broadcastIntent);         super.onDestroy();     }      @Override     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {         super.onRequestPermissionsResult(requestCode, permissions, grantResults);         if (requestCode == 100) {             if (grantResults[0] == PackageManager.PERMISSION_DENIED) {                 Toast.makeText(this, "Permissions Denied!\n Can't use the App!", Toast.LENGTH_SHORT).show();             }         }     }      @Override     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {         super.onActivityResult(requestCode, resultCode, data);                  // get the contact from the PhoneBook of device         switch (requestCode) {             case (PICK_CONTACT):                 if (resultCode == Activity.RESULT_OK) {                      Uri contactData = data.getData();                     Cursor c = managedQuery(contactData, null, null, null, null);                     if (c.moveToFirst()) {                          String id = c.getString(c.getColumnIndexOrThrow(ContactsContract.Contacts._ID));                         String hasPhone = c.getString(c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));                         String phone = null;                         try {                             if (hasPhone.equalsIgnoreCase("1")) {                                 Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id, null, null);                                 phones.moveToFirst();                                 phone = phones.getString(phones.getColumnIndex("data1"));                             }                             String name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));                             db.addcontact(new ContactModel(0, name, phone));                             list = db.getAllContacts();                             customAdapter.refresh(list);                         } catch (Exception ex) {                         }                     }                 }                 break;         }     }      // this method prompts the user to remove any      // battery optimisation constraints from the App     private void askIgnoreOptimization() {          if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {             @SuppressLint("BatteryLife") Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);             intent.setData(Uri.parse("package:" + getPackageName()));             startActivityForResult(intent, IGNORE_BATTERY_OPTIMIZATION_REQUEST);         }      }  } 

 
 

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Below is the code for the activity_main.xml file. 


 

XML
<?xml version="1.0" encoding="utf-8"?> <LinearLayout      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:orientation="vertical"     tools:context=".MainActivity">      <Button         android:id="@+id/Button1"         android:layout_width="match_parent"         android:layout_height="wrap_content"         android:layout_margin="12dp"         android:background="#0F9D58"         android:text="Add Emergency Contact "         android:textColor="#FFFFFF" />      <ListView         android:id="@+id/ListView"         android:layout_width="match_parent"         android:layout_height="match_parent" />  </LinearLayout> 

 
 

Step 5: Working with AndroidManifest.xml


 

XML
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"     xmlns:tools="http://schemas.android.com/tools"     package="com.raghav.sos">      <uses-permission android:name="android.permission.READ_CONTACTS"/>     <uses-permission android:name="android.permission.VIBRATE"/>     <uses-permission android:name="android.permission.SEND_SMS"/>     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>        <!--This permission is necessary for devices        with Android O and above, so that        we can use the location ALL THE TIME-->     <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>       <!-- We also ask user to remove any battery optimization constraints during runtime -->     <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>      <application         android:allowBackup="true"         android:icon="@mipmap/ic_launcher"         android:label="@string/app_name"         android:roundIcon="@mipmap/ic_launcher_round"         android:supportsRtl="true"         android:theme="@style/Theme.SOS">                <!-- register the receiver -->         <receiver             android:name=".ShakeServices.ReactivateService"             android:enabled="true"             android:exported="true"/>                <!-- register the service -->         <service             android:name=".ShakeServices.SensorService"             android:enabled="true"             android:exported="true" />          <activity android:name=".MainActivity">             <intent-filter>                 <action android:name="android.intent.action.MAIN" />                  <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>     </application>  </manifest> 

 
 

Output:


 

Future Scope

  1. You can add options for personal assistance emergencies like a map that indicates nearby police stations, hospitals, cabs, etc.
  2. You create a logic that sends the user location every 1 or 2 minutes without the user shaking the device again.
  3. You can add voice or/and video recording functionality.
  4. Also, you can add a call to multiple or a single person at the time of shake.
  5. You can try to add OpenCellId, this will allow you to fetch the location of the nearest mobile tower. But again remember that in order to calculate the location you would require some device information(no GPS) and that would require your GPS or location services to be turned ON.
  6. Doors to changes in the UI are always open.

Notes:

1. Allow the app to autostart, in order to use the app while the screen is off.

2. Remove any battery optimization constraints on the app. This might make Android kill the service.

3. Allow all the permissions, especially allow location permissions by Allowing the app to use the device location all the time. This would allow the service to use the device location when the shake event is registered.


 


 


Next Article
How to Build a SOS Mobile Application in Android Studio?

R

raghav14
Improve
Article Tags :
  • Java
  • Android
  • Android-Studio
  • Android Projects
Practice Tags :
  • Java

Similar Reads

    How to Build an Application to Test Motion Sensors in Android?
    In this article, we will be building a Motion Sensor Testing App Project using Java and XML in Android. The application will be using the hardware of the device to detect the movements. The components required to detect the motion are Accelerometer and Gyroscope. The Accelerometer is an electronic s
    6 min read
    How to Build Android Applications with Gradle?
    There are several contemporary build automation technologies available nowadays that may be utilized to create a quicker build when creating a project. Gradle is one of these modern-day automation technologies. Gradle is now used by Android to automate and control the build process while also defini
    8 min read
    How to Create a Social Media App on Android Studio?
    Social media is not a new term for us. Our daily life is incomplete, or we can say we human beings survive on food, water, air, and social media. We are dependent to such an extent that we tend to share every bit of information about ourselves on social media platforms. Similarly, Android Studio is
    8 min read
    How to Build Live Train Status App in Android?
    Imagine you are traveling via Indian railways which is one of the biggest rail networks in the world. You want to get real-time updates for the train on which you are traveling so that you can plan accordingly. In this article, we will take a look at How to build a live train status checker applicat
    8 min read
    How to Create Google Lens Application in Android?
    We have seen the new Google Lens application in which we can capture images of any product and from that image, we can get to see the search results of that product which we will display inside our application. What are we going to build in this article? We will be building a simple application in w
    10 min read
geeksforgeeks-footer-logo
Corporate & Communications Address:
A-143, 7th Floor, Sovereign Corporate Tower, Sector- 136, Noida, Uttar Pradesh (201305)
Registered Address:
K 061, Tower K, Gulshan Vivante Apartment, Sector 137, Noida, Gautam Buddh Nagar, Uttar Pradesh, 201305
GFG App on Play Store GFG App on App Store
Advertise with us
  • Company
  • About Us
  • Legal
  • Privacy Policy
  • In Media
  • Contact Us
  • Advertise with us
  • GFG Corporate Solution
  • Placement Training Program
  • Languages
  • Python
  • Java
  • C++
  • PHP
  • GoLang
  • SQL
  • R Language
  • Android Tutorial
  • Tutorials Archive
  • DSA
  • Data Structures
  • Algorithms
  • DSA for Beginners
  • Basic DSA Problems
  • DSA Roadmap
  • Top 100 DSA Interview Problems
  • DSA Roadmap by Sandeep Jain
  • All Cheat Sheets
  • Data Science & ML
  • Data Science With Python
  • Data Science For Beginner
  • Machine Learning
  • ML Maths
  • Data Visualisation
  • Pandas
  • NumPy
  • NLP
  • Deep Learning
  • Web Technologies
  • HTML
  • CSS
  • JavaScript
  • TypeScript
  • ReactJS
  • NextJS
  • Bootstrap
  • Web Design
  • Python Tutorial
  • Python Programming Examples
  • Python Projects
  • Python Tkinter
  • Python Web Scraping
  • OpenCV Tutorial
  • Python Interview Question
  • Django
  • Computer Science
  • Operating Systems
  • Computer Network
  • Database Management System
  • Software Engineering
  • Digital Logic Design
  • Engineering Maths
  • Software Development
  • Software Testing
  • DevOps
  • Git
  • Linux
  • AWS
  • Docker
  • Kubernetes
  • Azure
  • GCP
  • DevOps Roadmap
  • System Design
  • High Level Design
  • Low Level Design
  • UML Diagrams
  • Interview Guide
  • Design Patterns
  • OOAD
  • System Design Bootcamp
  • Interview Questions
  • Inteview Preparation
  • Competitive Programming
  • Top DS or Algo for CP
  • Company-Wise Recruitment Process
  • Company-Wise Preparation
  • Aptitude Preparation
  • Puzzles
  • School Subjects
  • Mathematics
  • Physics
  • Chemistry
  • Biology
  • Social Science
  • English Grammar
  • Commerce
  • World GK
  • GeeksforGeeks Videos
  • DSA
  • Python
  • Java
  • C++
  • Web Development
  • Data Science
  • CS Subjects
@GeeksforGeeks, Sanchhaya Education Private Limited, All rights reserved
We use cookies to ensure you have the best browsing experience on our website. By using our site, you acknowledge that you have read and understood our Cookie Policy & Privacy Policy
Lightbox
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
geeksforgeeks-suggest-icon
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.
geeksforgeeks-improvement-icon
Suggest Changes
min 4 words, max Words Limit:1000

Thank You!

Your suggestions are valuable to us.

What kind of Experience do you want to share?

Interview Experiences
Admission Experiences
Career Journeys
Work Experiences
Campus Experiences
Competitive Exam Experiences