How to Build a SOS Mobile Application in Android Studio? (original) (raw)

Last Updated : 30 May, 2026

An SOS mobile application is an emergency assistance app designed to help users quickly contact trusted people during dangerous or critical situations. These apps provide fast communication and safety features that can be triggered without manually operating the phone for a long time.

Pre-requisites:

Step by Step Implementation

Follow these steps to create a Mobile Application in Androide Studio.

**Step 1: Create a New Project

Step 2: Creating the Contacts Module

Create a Contacts folder to manage all contact-related files used for storing and displaying emergency contacts in the ListView.

Step 2.1: Creating model class for Contact

We create this class to store contact details like name and phone number in a structured format. It also ensures the phone number is properly validated before use.

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 use this class to store and manage emergency contacts using SQLite database. It ensures contacts remain saved permanently even after app restart.

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

We create this to connect contact data with ListView for proper display. It also handles actions like showing and deleting contacts from the list.

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 {

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

We design this layout to define how each contact will look in the ListView. It improves UI by showing name and phone number in a structured format.

XML `

<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>

`

Step 3: Creating the Service Module

We create this module to handle background tasks like shake detection and SOS alerts. It ensures the app works even when it is not open.

**Step 3.1: Creating ShakeDetector class

We use this to detect device shaking using accelerometer sensor. It triggers SOS only when a specific shake pattern is detected.

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

We create this to run continuous background monitoring of shake events. It sends emergency SMS with location even if the app is closed.

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);
}

}

`

We use FusedLocationProviderClient to get the user’s current location using getCurrentLocation(). It requires GPS/location services to be ON, otherwise it returns null. From Android O onwards, location access only works when the user explicitly enables location for privacy and awareness.

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() { @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) { ... } });

`

After getting the location successfully, we use SmsManager to send emergency messages to all saved contacts from the database. If location is not available, we send a fallback message without coordinates so receivers still get alert and can contact help or authorities for assistance.

Java `

SmsManager smsManager = SmsManager.getDefault(); DbHelper db = new DbHelper(SensorService.this); List 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/maps?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.

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/maps?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

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 `

<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" />

`

**Step 5: Working with AndroidManifest.xml

XML `

<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>

`

Steps To Run SOS App in Android Studio (Final Execution Steps)

Step 1: Open Project

Step 2: Build Project

Step 3: Connect Device / Emulator

You can run app in 2 ways:

**1. Emulator

**2. Real Device (Recommended)

Step 4: Install & Run App

Step 5: First Launch Setup (Very Important)

When app opens first time:

**Allow all permissions:

**Go to Settings if asked:

**Output:

**Future Scope

**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.