Skip to content

Commit

Permalink
Merge pull request #84 from keepalivedev/AddFeature-AlertWebhook
Browse files Browse the repository at this point in the history
Add feature alert webhook
  • Loading branch information
keepalivedev authored Jul 30, 2024
2 parents a785775 + 49ca6bb commit e61c435
Show file tree
Hide file tree
Showing 18 changed files with 1,288 additions and 45 deletions.
4 changes: 3 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,11 @@ android {

dependencies {

implementation 'com.squareup.okhttp3:okhttp:4.12.0'

// next versions require compile sdk 34+
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.activity:activity-ktx:1.9.0'
implementation 'androidx.activity:activity-ktx:1.9.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'

implementation 'androidx.appcompat:appcompat:1.7.0'
Expand Down
15 changes: 10 additions & 5 deletions app/src/fDroid/java/io/keepalive/android/LocationHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import java.util.concurrent.CopyOnWriteArraySet
// implementation for use with android.location APIs so that its compatible with F-Droid
class LocationHelper(
context: Context,
myCallback: (Context, String) -> Unit
myCallback: (Context, LocationResult) -> Unit
) : LocationHelperBase(context, myCallback) {


Expand Down Expand Up @@ -59,7 +59,8 @@ class LocationHelper(

// if the location was null or there was an error then execute the callback
// with an error message
executeCallback(context.getString(R.string.location_invalid_message))
locationResult.formattedLocationString = context.getString(R.string.location_invalid_message)
executeCallback(locationResult)
}

// check all available providers to try to get the best last known location
Expand Down Expand Up @@ -101,7 +102,7 @@ class LocationHelper(
// class to handle everything related to getting the current location
inner class CurrentLocationProcessor {
private val locations = ConcurrentHashMap<String, Location>()
private val timeoutHandler = Handler(context.mainLooper)
private val timeoutHandler = Handler(backgroundHandler.looper)
private val cancellationSignal = CancellationSignal()
private val receivedProviders = CopyOnWriteArraySet<String>()

Expand All @@ -120,7 +121,7 @@ class LocationHelper(

locationManager.getCurrentLocation(
provider, cancellationSignal,
context.mainExecutor, ::processProviderLocationResult
backgroundExecutor, ::processProviderLocationResult
)
} else {

Expand All @@ -129,7 +130,7 @@ class LocationHelper(
locationManager.requestSingleUpdate(
provider,
locationListener,
context.mainLooper
backgroundHandler.looper
)
}
}
Expand Down Expand Up @@ -220,6 +221,10 @@ class LocationHelper(
"${bestLoc.accuracy}acc at ${getDateTimeStrFromTimestamp(bestLoc.time)}"
)

locationResult.latitude = bestLoc.latitude
locationResult.longitude = bestLoc.longitude
locationResult.accuracy = bestLoc.accuracy

// try to geocode the location and then execute the callback
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
GeocodingHelperAPI33Plus().geocodeLocationAndExecute(bestLoc)
Expand Down
15 changes: 11 additions & 4 deletions app/src/googlePlay/java/io/keepalive/android/LocationHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import com.google.android.gms.tasks.Task
// implementation for use with Google Play Services Location APIs
class LocationHelper(
context: Context,
myCallback: (Context, String) -> Unit
myCallback: (Context, LocationResult) -> Unit
) : LocationHelperBase(context, myCallback) {

private val fusedLocationClient: FusedLocationProviderClient =
Expand All @@ -33,7 +33,8 @@ class LocationHelper(

DebugLogger.d("globalTimeoutRunnable", context.getString(R.string.debug_log_timeout_reached_getting_location_from_google_play))

myCallback(context, context.getString(R.string.location_invalid_message))
locationResult.formattedLocationString = context.getString(R.string.location_invalid_message)
myCallback(context, locationResult)
}

// get the current location the Google Play Services location API
Expand Down Expand Up @@ -73,7 +74,8 @@ class LocationHelper(
DebugLogger.d("getLastLocation", context.getString(R.string.debug_log_failed_getting_last_location), e)

// if we failed to get the last location then just send the error message
executeCallback(context.getString(R.string.location_invalid_message))
locationResult.formattedLocationString = context.getString(R.string.location_invalid_message)
executeCallback(locationResult)
}
}

Expand All @@ -92,6 +94,10 @@ class LocationHelper(
"(${location.latitude}, ${location.longitude}) ${location.accuracy}acc"
)

locationResult.latitude = location.latitude
locationResult.longitude = location.longitude
locationResult.accuracy = location.accuracy

// try to geocode the location and then execute the callback
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
GeocodingHelperAPI33Plus().geocodeLocationAndExecute(location)
Expand All @@ -114,7 +120,8 @@ class LocationHelper(
// otherwise if we fail to get the last location then just send the error message
} else {
Log.d("processLocationResult", "Unable to determine location, executing callback")
executeCallback(context.getString(R.string.location_invalid_message))
locationResult.formattedLocationString = context.getString(R.string.location_invalid_message)
executeCallback(locationResult)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
android:name="android.hardware.telephony"
android:required="true" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.SEND_SMS" />
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/io/keepalive/android/AlertFunctions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ fun getSMSManager(context: Context): SmsManager? {
}
}
} catch (e: Exception) {
DebugLogger.d("sendAlertMessage", context.getString(R.string.debug_log_exception_while_getting_sms_manager, e.message), e)
DebugLogger.d("sendAlertMessage", context.getString(R.string.debug_log_exception_while_getting_sms_manager, e.localizedMessage), e)

// if the above fails default to the normal method of getting the SMS manager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Expand Down Expand Up @@ -255,7 +255,7 @@ class AlertMessageSender(private val context: Context) {
}

} catch (e: Exception) {
DebugLogger.d("sendAlertMessage", context.getString(R.string.debug_log_failed_sending_sms, contact.phoneNumber, e.message), e)
DebugLogger.d("sendAlertMessage", context.getString(R.string.debug_log_failed_sending_sms, contact.phoneNumber, e.localizedMessage), e)

// if we failed while sending the SMS then send a notification
// to let the user know
Expand Down
81 changes: 77 additions & 4 deletions app/src/main/java/io/keepalive/android/AlertService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,62 @@ class AlertService : Service() {
.build()
}

private fun sendWebhookRequest(context: Context, locationResult: LocationResult?) {
try {
val webhookConfigManager = WebhookConfigManager(this, null)
val webhookConfig = webhookConfigManager.getWebhookConfig()

val webhookSender = WebhookSender(this, webhookConfig)
webhookSender.sendRequest(locationResult, callback = webhookCallback(context))

} catch (e: Exception) {
DebugLogger.d("sendAlert", context.getString(R.string.debug_log_sending_webhook_failed, e.localizedMessage), e)
}
}

private fun webhookCallback(context: Context) : WebhookCallback {
return object : WebhookCallback {
override fun onSuccess(responseCode: Int) {

AlertNotificationHelper(context).sendNotification(
context.getString(R.string.webhook_request_success_notification_title),
String.format(
context.getString(R.string.webhook_request_success_notification_text),
responseCode
),
AppController.WEBHOOK_ALERT_SENT_NOTIFICATION_ID,
true
)
}

override fun onFailure(responseCode: Int) {

AlertNotificationHelper(context).sendNotification(
context.getString(R.string.webhook_request_failure_notification_title),
String.format(
context.getString(R.string.webhook_request_failure_code_notification_text),
responseCode
),
AppController.WEBHOOK_ALERT_SENT_NOTIFICATION_ID,
true
)
}

override fun onError(errorMessage: String) {

AlertNotificationHelper(context).sendNotification(
context.getString(R.string.webhook_request_failure_notification_title),
String.format(
context.getString(R.string.webhook_request_failure_error_notification_text),
errorMessage
),
AppController.WEBHOOK_ALERT_SENT_NOTIFICATION_ID,
true
)
}
}
}

private fun sendAlert(context: Context, prefs: SharedPreferences) {
DebugLogger.d("sendAlert", context.getString(R.string.debug_log_sending_alert))

Expand All @@ -156,16 +212,27 @@ class AlertService : Service() {
val alertSender = AlertMessageSender(context)
alertSender.sendAlertMessage()

// only get the location if the user has enabled it for at least one
if (prefs.getBoolean("location_enabled", false)) {
// only get the location if the user has enabled it for at least one contact
// or for the webhook
if (prefs.getBoolean("location_enabled", false) || prefs.getBoolean("webhook_location_enabled", false)) {

// just add an extra layer try/catch in case anything unexpected
// happens when trying to get the location
try {

// attempt to get the location and then execute sendLocationAlertMessage
val locationHelper = LocationHelper(context) { _, locationStr ->
alertSender.sendLocationAlertMessage(locationStr)
val locationHelper = LocationHelper(context) { _, locationResult ->

// this will still check whether location is enabled for each contact
// (in case location is only enabled for the webhook but not any SMS contacts)

// for the SMS alerts we just need the formatted location string
alertSender.sendLocationAlertMessage(locationResult.formattedLocationString)

// if enabled, send a request to the webhook with the location result
if (prefs.getBoolean("webhook_enabled", false)) {
sendWebhookRequest(context, locationResult)
}

// stop the service after the location alert is sent
stopService()
Expand All @@ -180,6 +247,12 @@ class AlertService : Service() {

stopService()
}
} else {

// if enabled, send the webhook without the location
if (prefs.getBoolean("webhook_enabled", false)) {
sendWebhookRequest(context, null)
}
}

// also make the phone call (if enabled)
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/io/keepalive/android/AppController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ class AppController : Application() {
const val CALL_SENT_NOTIFICATION_CHANNEL_ID = "CallSentNotificationChannel"
const val SMS_SENT_NOTIFICATION_CHANNEL_ID = "SMSSentNotificationChannel"
const val ALERT_SERVICE_NOTIFICATION_CHANNEL_ID = "AlertServiceNotificationChannel"
const val WEBHOOK_SENT_NOTIFICATION_CHANNEL_ID = "WebhookSentNotificationChannel"

// notification IDs
const val ARE_YOU_THERE_NOTIFICATION_ID = 1
const val SMS_ALERT_SENT_NOTIFICATION_ID = 2
const val CALL_ALERT_SENT_NOTIFICATION_ID = 3
const val SMS_ALERT_FAILURE_NOTIFICATION_ID = 4
const val ALERT_SERVICE_NOTIFICATION_ID = 4
const val ALERT_SERVICE_NOTIFICATION_ID = 5
const val WEBHOOK_ALERT_SENT_NOTIFICATION_ID = 6

// when doing a sanity check to see if we can see ANY events, this is the # of hours
// to use with getLastDeviceActivity(). if the user has a higher value set it
Expand Down
Loading

0 comments on commit e61c435

Please sign in to comment.