0

I am making an SOS app, where I have defined a function in mainactivty ( sendSOS() ) class that gets user location and sends a sms to contacts saved. Now i have created a sensor service class that detects a shake event and runs in foreground, and when a shake happens it is supposed to again invoke the (sendSOS() ) method. But its not working properly. Below is the error and code. Any guidance on how to implement it would be appreciated.

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
                                                                                                    at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:135)
                                                                                                    at com.google.android.gms.common.api.GoogleApi.<init>(com.google.android.gms:play-services-base@@18.1.0:7)
                                                                                                    at com.google.android.gms.common.api.GoogleApi.<init>(com.google.android.gms:play-services-base@@18.1.0:1)
                                                                                                    at com.google.android.gms.internal.location.zzbp.<init>(com.google.android.gms:play-services-location@@21.0.1:1)
                                                                                                    at com.google.android.gms.location.LocationServices.getFusedLocationProviderClient(com.google.android.gms:play-services-location@@21.0.1:1)
                                                                                                    at com.chrizlove.helpapp.MainActivity.sendSOS(MainActivity.kt:142)
                                                                                                    at com.chrizlove.helpapp.SensorService$onCreate$1.onShake(SensorService.kt:67)
                                                                                                    at com.chrizlove.helpapp.ShakeDetector.onSensorChanged(ShakeDetector.kt:51)

The SensorService code part which will be used to call the sendSOS() of MainActivty when device is shaken.

override fun onShake(count: Int) {
            // check if the user has shacked
            // the phone for 3 time in a row
            if (count == 3) {

                // vibrate the phone
                vibrate()

                //here i want to call the sendSos method that is in the mainActivity
                val mActivity = MainActivity()
                mActivity.sendSOS(applicationContext)
                Toast.makeText(applicationContext,"Shaken", Toast.LENGTH_SHORT).show()
            }
        }

The MainActivity sendSOS() method along with rest of the methods used for its functioning.

public fun sendSOS(context: Context) {
        fusedLocationProviderClient=LocationServices.getFusedLocationProviderClient(this)
        getCurrentLocationAndSendSMS(context)
}

private fun sendSMS() {
    Log.d(TAG,"2")
    if(checkSMSPermission(this)){
        val smsManager: SmsManager
        smsManager = SmsManager.getDefault()
        for (contact in contactViewModel.contacts.value!!){
            smsManager.sendTextMessage(contact.c_number, null,
                "Hi, I am in an emergency! This is my location https://www.google.com/maps/?q="+location.latitude+","+location.longitude,
                null, null)
        }
        Toast.makeText(applicationContext,"SMS Sent",Toast.LENGTH_SHORT).show()
    }
    else{
        requestSMSPermission()
    }
}

private fun requestSMSPermission() {
    ActivityCompat.requestPermissions(this,
        arrayOf(Manifest.permission.SEND_SMS), PERMISSION_REQUEST_SMS)
}

private fun checkSMSPermission(context: Context): Boolean {
    return ActivityCompat.checkSelfPermission(context,Manifest.permission.SEND_SMS)==PackageManager.PERMISSION_GRANTED
}

public fun getCurrentLocationAndSendSMS(context: Context) {
    Log.d(TAG,"1")
    if(checkLocationPermission(context)){
       if(locationEnabled()){
            fusedLocationProviderClient.lastLocation.addOnCompleteListener(this) {task->
                location=task.result
                if(location==null){
                //do something later on
                }
                else{
                    //SEND sms when gotten location
                    sendSMS()
                }
            }
        }
        else{
            //send last known location
        }
    }
    else{
        //request permission
        requestLocationPermission()
    }
}

companion object{
    private const val PERMISSION_REQUEST_ACCESS_LOCATION=100
    private const val  PERMISSION_REQUEST_SMS=99
}

private fun requestLocationPermission() {
        ActivityCompat.requestPermissions(this,
            arrayOf( Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION),
            PERMISSION_REQUEST_ACCESS_LOCATION)

}

private fun locationEnabled(): Boolean {
    val locationManager: LocationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
    return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}


private fun checkLocationPermission(context: Context): Boolean {
    if(ActivityCompat.checkSelfPermission(context,Manifest.permission.ACCESS_COARSE_LOCATION)==PackageManager.PERMISSION_GRANTED
        && ActivityCompat.checkSelfPermission(context,Manifest.permission.ACCESS_FINE_LOCATION)==PackageManager.PERMISSION_GRANTED)
    {
        return true
    }
    return false
}

If anything else is needed just ask, it would be very helpful to get any sort of lead as to how to implement it.

This question explains and and provides the solution to this provlem:---https://stackoverflow.com/questions/14896574/how-to-call-a-method-in-activity-from-a-service

Chrizlove
  • 57
  • 7

1 Answers1

2

Method 1: To call method from MainActivty you need to write this in SensorService in onShake():

(applicationContext as MainActivity).sendSOS(applicationContext)

Method 2:

You can use BroadcastReceiver to get data from Services.

In your MainActivity add sosReceiver which calls sendSos function:

private val sosReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        sendSOS(context)
    }
}

To receive broadcasts in your MainActivity in onCreate function add:

registerReceiver(sosReceiver, IntentFilter.create(SensorService.ACTION_SOS_SEND, null))

And then in onDestroy() function in MainActivity unregister receiver:

override fun onDestroy() {
    super.onDestroy()
    unregisterReceiver(sosReceiver)
}

In SensorService to send a broadcast:

override fun onShake(count: Int) {
        if (count == 3) {
            // other code
            val sosIntent = Intent(ACTION_SOS_SEND)
            this.sendBroadcast(sosIntent)
        }
    }

companion object {
    const val ACTION_SOS_SEND = "com.company.package.ACTION_SOS_SEND"
}

NOTE: You can not create instance of Activities including MainActivity. Android itself is responsible for this task

Injent
  • 479
  • 11
  • I am going to try the 2nd method though the first method is giving this error ---"java.lang.ClassCastException: android.app.Application cannot be cast to com.chrizlove.helpapp.MainActivity at com.chrizlove.helpapp.SensorService$onCreate$1.onShake(SensorService.kt:68) at com.chrizlove.helpapp.ShakeDetector.onSensorChanged(ShakeDetector.kt:51)" – Chrizlove May 28 '23 at 13:09
  • @Chrizlove wait a minute. I'm going to fix this problem – Injent May 28 '23 at 13:12
  • @Chrizlove Just checked. It fails because of the special Context of Service, which cannot be cast to MainActivity as opposed to View context – Injent May 28 '23 at 13:25
  • No problem, I fixed it using the 2nd method and another similar question, thanks a lot for your help :) – Chrizlove May 28 '23 at 13:30