mirror of https://github.com/tasks/tasks
Add LocationManager proximity alerts
parent
598cbba8c1
commit
99dee06c64
@ -1,12 +0,0 @@
|
|||||||
package org.tasks.location
|
|
||||||
|
|
||||||
import org.tasks.data.Place
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
class GeofenceApi @Inject constructor() {
|
|
||||||
fun registerAll() {}
|
|
||||||
fun update(place: Place?) {}
|
|
||||||
fun update(place: String?) {}
|
|
||||||
fun update(taskId: Long) {}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
package org.tasks.location
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.location.LocationManager
|
||||||
|
import android.net.Uri
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import org.tasks.data.MergedGeofence
|
||||||
|
import org.tasks.data.Place
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GeofenceClient @Inject constructor(@ApplicationContext private val context: Context) {
|
||||||
|
private val client = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
fun addGeofences(@Suppress("UNUSED_PARAMETER") geofence: MergedGeofence) {
|
||||||
|
client.addProximityAlert(
|
||||||
|
geofence.latitude,
|
||||||
|
geofence.longitude,
|
||||||
|
geofence.radius.toFloat(),
|
||||||
|
-1,
|
||||||
|
createPendingIntent(geofence.place.id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeGeofences(@Suppress("UNUSED_PARAMETER") place: Place) {
|
||||||
|
client.removeProximityAlert(createPendingIntent(place.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createPendingIntent(place: Long) =
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
Intent(context, GeofenceTransitionsIntentService.Broadcast::class.java)
|
||||||
|
.setData(Uri.parse("tasks://geofence/$place")),
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
package org.tasks.location
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.location.LocationManager
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.Notifier
|
||||||
|
import org.tasks.data.LocationDao
|
||||||
|
import org.tasks.injection.InjectingJobIntentService
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class GeofenceTransitionsIntentService : InjectingJobIntentService() {
|
||||||
|
@Inject lateinit var locationDao: LocationDao
|
||||||
|
@Inject lateinit var notifier: Notifier
|
||||||
|
|
||||||
|
override suspend fun doWork(intent: Intent) {
|
||||||
|
val arrival = intent.getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, false)
|
||||||
|
Timber.d("geofence[${intent.data}] arrival[$arrival]")
|
||||||
|
val place = intent.data?.lastPathSegment?.toLongOrNull()?.let { locationDao.getPlace(it) }
|
||||||
|
if (place == null) {
|
||||||
|
Timber.e("Failed to find place ${intent.data}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val geofences = if (arrival) {
|
||||||
|
locationDao.getArrivalGeofences(place.uid!!)
|
||||||
|
} else {
|
||||||
|
locationDao.getDepartureGeofences(place.uid!!)
|
||||||
|
}
|
||||||
|
notifier.triggerNotifications(place.id, geofences, arrival)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Broadcast : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
enqueueWork(
|
||||||
|
context,
|
||||||
|
GeofenceTransitionsIntentService::class.java,
|
||||||
|
JOB_ID_GEOFENCE_TRANSITION,
|
||||||
|
intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<bool name="support_geofences">false</bool>
|
|
||||||
</resources>
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
package org.tasks.location
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import com.google.android.gms.location.Geofence
|
|
||||||
import com.google.android.gms.location.GeofencingRequest
|
|
||||||
import com.google.android.gms.location.LocationServices
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import org.tasks.data.LocationDao
|
|
||||||
import org.tasks.data.MergedGeofence
|
|
||||||
import org.tasks.data.Place
|
|
||||||
import org.tasks.preferences.PermissionChecker
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
class GeofenceApi @Inject constructor(
|
|
||||||
@param:ApplicationContext private val context: Context,
|
|
||||||
private val permissionChecker: PermissionChecker,
|
|
||||||
private val locationDao: LocationDao) {
|
|
||||||
|
|
||||||
suspend fun registerAll() = locationDao.getPlacesWithGeofences().forEach { update(it) }
|
|
||||||
|
|
||||||
suspend fun update(taskId: Long) = update(locationDao.getPlaceForTask(taskId))
|
|
||||||
|
|
||||||
suspend fun update(place: String) = update(locationDao.getPlace(place))
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
suspend fun update(place: Place?) {
|
|
||||||
if (place == null || !permissionChecker.canAccessBackgroundLocation()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
val client = LocationServices.getGeofencingClient(context)
|
|
||||||
val geofence = locationDao.getGeofencesByPlace(place.uid!!)
|
|
||||||
if (geofence != null) {
|
|
||||||
Timber.d("Adding geofence for %s", geofence)
|
|
||||||
client.addGeofences(
|
|
||||||
GeofencingRequest.Builder().addGeofence(toGoogleGeofence(geofence)).build(),
|
|
||||||
PendingIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
0,
|
|
||||||
Intent(context, GeofenceTransitionsIntentService.Broadcast::class.java),
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT))
|
|
||||||
} else {
|
|
||||||
Timber.d("Removing geofence for %s", place)
|
|
||||||
client.removeGeofences(listOf(place.id.toString()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun toGoogleGeofence(geofence: MergedGeofence): Geofence {
|
|
||||||
var transitionTypes = 0
|
|
||||||
if (geofence.arrival) {
|
|
||||||
transitionTypes = transitionTypes or GeofencingRequest.INITIAL_TRIGGER_ENTER
|
|
||||||
}
|
|
||||||
if (geofence.departure) {
|
|
||||||
transitionTypes = transitionTypes or GeofencingRequest.INITIAL_TRIGGER_EXIT
|
|
||||||
}
|
|
||||||
return Geofence.Builder()
|
|
||||||
.setCircularRegion(geofence.latitude, geofence.longitude, geofence.radius.toFloat())
|
|
||||||
.setRequestId(geofence.uid)
|
|
||||||
.setTransitionTypes(transitionTypes)
|
|
||||||
.setExpirationDuration(Geofence.NEVER_EXPIRE)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
package org.tasks.location
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import com.google.android.gms.location.Geofence
|
||||||
|
import com.google.android.gms.location.GeofencingRequest
|
||||||
|
import com.google.android.gms.location.LocationServices
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import org.tasks.data.MergedGeofence
|
||||||
|
import org.tasks.data.Place
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GeofenceClient @Inject constructor(@ApplicationContext private val context: Context) {
|
||||||
|
private val client = LocationServices.getGeofencingClient(context)
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
fun addGeofences(geofence: MergedGeofence) {
|
||||||
|
client.addGeofences(
|
||||||
|
GeofencingRequest.Builder().addGeofence(toGoogleGeofence(geofence)).build(),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
Intent(context, GeofenceTransitionsIntentService.Broadcast::class.java),
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeGeofences(place: Place) {
|
||||||
|
client.removeGeofences(listOf(place.id.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toGoogleGeofence(geofence: MergedGeofence): Geofence {
|
||||||
|
var transitionTypes = 0
|
||||||
|
if (geofence.arrival) {
|
||||||
|
transitionTypes = transitionTypes or GeofencingRequest.INITIAL_TRIGGER_ENTER
|
||||||
|
}
|
||||||
|
if (geofence.departure) {
|
||||||
|
transitionTypes = transitionTypes or GeofencingRequest.INITIAL_TRIGGER_EXIT
|
||||||
|
}
|
||||||
|
return Geofence.Builder()
|
||||||
|
.setCircularRegion(geofence.latitude, geofence.longitude, geofence.radius.toFloat())
|
||||||
|
.setRequestId(geofence.uid)
|
||||||
|
.setTransitionTypes(transitionTypes)
|
||||||
|
.setExpirationDuration(Geofence.NEVER_EXPIRE)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<bool name="support_geofences">true</bool>
|
|
||||||
</resources>
|
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package org.tasks.location
|
||||||
|
|
||||||
|
import org.tasks.data.LocationDao
|
||||||
|
import org.tasks.data.Place
|
||||||
|
import org.tasks.preferences.PermissionChecker
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class GeofenceApi @Inject constructor(
|
||||||
|
private val permissionChecker: PermissionChecker,
|
||||||
|
private val locationDao: LocationDao,
|
||||||
|
private val client: GeofenceClient
|
||||||
|
) {
|
||||||
|
suspend fun registerAll() = locationDao.getPlacesWithGeofences().forEach { update(it) }
|
||||||
|
|
||||||
|
suspend fun update(taskId: Long) = update(locationDao.getPlaceForTask(taskId))
|
||||||
|
|
||||||
|
suspend fun update(place: String) = update(locationDao.getPlace(place))
|
||||||
|
|
||||||
|
suspend fun update(place: Place?) {
|
||||||
|
if (place == null || !permissionChecker.canAccessBackgroundLocation()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
locationDao
|
||||||
|
.getGeofencesByPlace(place.uid!!)?.let {
|
||||||
|
Timber.d("Adding geofence for %s", it)
|
||||||
|
client.addGeofences(it)
|
||||||
|
}
|
||||||
|
?: place.let {
|
||||||
|
Timber.d("Removing geofence for %s", it)
|
||||||
|
client.removeGeofences(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue