OsmAnd/OsmAnd-telegram/src/net/osmand/telegram/TelegramService.kt

346 lines
10 KiB
Kotlin
Raw Normal View History

2018-06-11 18:57:33 +02:00
package net.osmand.telegram
import android.annotation.SuppressLint
2018-06-13 16:18:54 +02:00
import android.app.AlarmManager
import android.app.PendingIntent
2018-06-11 18:57:33 +02:00
import android.app.Service
import android.content.Context
import android.content.Intent
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.*
import android.util.Log
import android.widget.Toast
import net.osmand.PlatformUtil
import net.osmand.telegram.helpers.TelegramHelper.TelegramIncomingMessagesListener
2018-06-13 16:18:54 +02:00
import net.osmand.telegram.notifications.TelegramNotification.NotificationType
import net.osmand.telegram.utils.AndroidUtils
2018-06-11 18:57:33 +02:00
import org.drinkless.td.libcore.telegram.TdApi
import java.util.*
2018-06-11 18:57:33 +02:00
import java.util.concurrent.Executors
class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener {
private fun app() = application as TelegramApplication
private val binder = LocationServiceBinder()
private val executor = Executors.newSingleThreadExecutor()
private var shouldCleanupResources: Boolean = false
2018-06-11 18:57:33 +02:00
var handler: Handler? = null
2018-06-13 16:18:54 +02:00
private set
2018-06-11 18:57:33 +02:00
var usedBy = 0
2018-06-13 16:18:54 +02:00
private set
var serviceOffProvider: String = LocationManager.GPS_PROVIDER
private set
var serviceOffInterval = 0L
private set
2018-06-14 14:21:58 +02:00
var serviceErrorInterval = 0L
2018-06-13 16:18:54 +02:00
private set
var sendLocationInterval = 0L
private set
2018-06-13 16:18:54 +02:00
private var lastLocationSentTime = 0L
2018-06-13 16:18:54 +02:00
private var pendingIntent: PendingIntent? = null
2018-06-11 18:57:33 +02:00
class LocationServiceBinder : Binder()
override fun onBind(intent: Intent): IBinder? {
return binder
}
fun stopIfNeeded(ctx: Context, usageIntent: Int) {
if (usedBy and usageIntent > 0) {
usedBy -= usageIntent
}
if (usedBy == 0) {
shouldCleanupResources = false
2018-06-11 18:57:33 +02:00
val serviceIntent = Intent(ctx, TelegramService::class.java)
ctx.stopService(serviceIntent)
2018-06-14 14:21:58 +02:00
} else if (isUsedByMyLocation(usedBy)) {
2018-06-13 16:18:54 +02:00
val app = app()
2018-06-14 14:21:58 +02:00
if (app.settings.sendMyLocationInterval >= OFF_INTERVAL_THRESHOLD && serviceOffInterval == 0L) {
2018-06-13 16:18:54 +02:00
serviceOffInterval = app.settings.sendMyLocationInterval
2018-06-14 14:21:58 +02:00
setupServiceErrorInterval()
setupAlarm()
2018-06-13 16:18:54 +02:00
}
app.notificationHelper.refreshNotification(NotificationType.LOCATION)
2018-06-11 18:57:33 +02:00
}
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
val app = app()
handler = Handler()
usedBy = intent.getIntExtra(USAGE_INTENT, 0)
2018-06-13 16:18:54 +02:00
serviceOffInterval = intent.getLongExtra(USAGE_OFF_INTERVAL, 0)
sendLocationInterval = intent.getLongExtra(SEND_LOCATION_INTERVAL, 0)
2018-06-14 14:21:58 +02:00
setupServiceErrorInterval()
2018-06-13 16:18:54 +02:00
2018-06-11 18:57:33 +02:00
app.telegramService = this
app.telegramHelper.addIncomingMessagesListener(this)
2018-06-11 18:57:33 +02:00
2018-06-14 14:21:58 +02:00
if (isUsedByMyLocation(usedBy)) {
initLocationUpdates()
2018-06-11 18:57:33 +02:00
}
if (isUsedByUsersLocations(usedBy)) {
app.telegramHelper.startLiveMessagesUpdates()
}
2018-06-11 18:57:33 +02:00
val locationNotification = app.notificationHelper.locationNotification
val notification = app.notificationHelper.buildNotification(locationNotification)
startForeground(locationNotification.telegramNotificationId, notification)
app.notificationHelper.refreshNotification(locationNotification.type)
return Service.START_REDELIVER_INTENT
}
2018-06-14 14:21:58 +02:00
private fun setupServiceErrorInterval() {
serviceErrorInterval = serviceOffInterval / 5
// 1. not more than 12 mins
serviceErrorInterval = Math.min(serviceErrorInterval, 12 * 60 * 1000)
// 2. not less than 30 seconds
serviceErrorInterval = Math.max(serviceErrorInterval, 30 * 1000)
// 3. not more than serviceOffInterval
serviceErrorInterval = Math.min(serviceErrorInterval, serviceOffInterval)
}
2018-06-11 18:57:33 +02:00
override fun onDestroy() {
super.onDestroy()
val app = app()
app.telegramHelper.stopLiveMessagesUpdates()
app.telegramHelper.removeIncomingMessagesListener(this)
2018-06-11 18:57:33 +02:00
app.telegramService = null
usedBy = 0
removeLocationUpdates()
2018-06-13 20:01:16 +02:00
if (!isContinuous()) {
val lock = getLock(this)
if (lock.isHeld) {
lock.release()
}
}
if (shouldCleanupResources) {
app.cleanupResources()
}
// remove notification
stopForeground(java.lang.Boolean.TRUE)
}
fun forceLocationUpdate() {
val location = getFirstTimeRunDefaultLocation()
app().shareLocationHelper.updateLocation(location)
}
private fun initLocationUpdates() {
val firstLocation = getFirstTimeRunDefaultLocation()
app().shareLocationHelper.updateLocation(firstLocation)
2018-06-13 20:01:16 +02:00
// requesting
if (isContinuous()) {
// request location updates
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
try {
locationManager.requestLocationUpdates(serviceOffProvider, 0, 0f, this@TelegramService)
} catch (e: SecurityException) {
Toast.makeText(this, R.string.no_location_permission, Toast.LENGTH_LONG).show()
Log.d(PlatformUtil.TAG, "Location service permission not granted") //$NON-NLS-1$
} catch (e: IllegalArgumentException) {
Toast.makeText(this, R.string.gps_not_available, Toast.LENGTH_LONG).show()
Log.d(PlatformUtil.TAG, "GPS location provider not available") //$NON-NLS-1$
}
} else {
setupAlarm()
}
}
@SuppressLint("MissingPermission")
private fun getFirstTimeRunDefaultLocation(): net.osmand.Location? {
val app = app()
if (!AndroidUtils.isLocationPermissionAvailable(app)) {
return null
}
val service = app.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val ps = service.getProviders(true) ?: return null
val providers = ArrayList(ps)
// note, passive provider is from API_LEVEL 8 but it is a constant, we can check for it.
// constant should not be changed in future
val passiveFirst = providers.indexOf("passive") // LocationManager.PASSIVE_PROVIDER
// put passive provider to first place
if (passiveFirst > -1) {
providers.add(0, providers.removeAt(passiveFirst))
}
// find location
for (provider in providers) {
val location = convertLocation(service.getLastKnownLocation(provider))
if (location != null) {
return location
}
}
return null
}
private fun setupAlarm() {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
pendingIntent = PendingIntent.getBroadcast(this, 0, Intent(this, OnTelegramServiceAlarmReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 500, serviceOffInterval, pendingIntent)
}
private fun removeLocationUpdates() {
2018-06-11 18:57:33 +02:00
// remove updates
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
try {
locationManager.removeUpdates(this)
} catch (e: SecurityException) {
Log.d(PlatformUtil.TAG, "Location service permission not granted")
}
}
2018-06-13 16:18:54 +02:00
private fun isContinuous(): Boolean {
return serviceOffInterval == 0L
}
2018-06-11 18:57:33 +02:00
override fun onLocationChanged(l: Location?) {
if (l != null) {
val location = convertLocation(l)
2018-06-13 20:01:16 +02:00
if (!isContinuous()) {
// unregister listener and wait next time
val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager
try {
locationManager.removeUpdates(this)
} catch (e: Throwable) {
2018-06-13 20:01:16 +02:00
Log.d(PlatformUtil.TAG, "Location service permission not granted") //$NON-NLS-1$
}
val lock = getLock(this)
if (lock.isHeld) {
lock.release()
}
app().shareLocationHelper.updateLocation(location)
} else if (System.currentTimeMillis() - lastLocationSentTime > sendLocationInterval) {
lastLocationSentTime = System.currentTimeMillis()
app().shareLocationHelper.updateLocation(location)
2018-06-13 20:01:16 +02:00
}
2018-06-11 18:57:33 +02:00
}
}
override fun onProviderDisabled(provider: String) {
Toast.makeText(this, getString(R.string.location_service_no_gps_available), Toast.LENGTH_LONG).show()
}
override fun onProviderEnabled(provider: String) {}
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onTaskRemoved(rootIntent: Intent) {
val app = app()
if (app.telegramService != null) {
shouldCleanupResources = true
2018-06-11 18:57:33 +02:00
// Do not stop service after UI task was dismissed
//this@TelegramService.stopSelf()
}
}
2018-07-13 13:02:59 +02:00
override fun onReceiveChatLocationMessages(chatId: Long, vararg messages: TdApi.Message) {
2018-06-11 18:57:33 +02:00
val app = app()
2018-07-13 13:02:59 +02:00
if (app.settings.isShowingChatOnMap(chatId)) {
ShowMessagesTask(app).executeOnExecutor(executor, *messages)
2018-06-11 18:57:33 +02:00
}
}
override fun updateLocationMessages() {
UpdateMessagesTask(app()).executeOnExecutor(executor)
}
private class ShowMessagesTask(private val app: TelegramApplication) : AsyncTask<TdApi.Message, Void, Void?>() {
2018-06-11 18:57:33 +02:00
override fun doInBackground(vararg messages: TdApi.Message): Void? {
for (message in messages) {
app.showLocationHelper.addLocationToMap(message)
2018-06-11 18:57:33 +02:00
}
return null
}
}
private class UpdateMessagesTask(private val app: TelegramApplication) : AsyncTask<Void, Void, Void?>() {
override fun doInBackground(vararg params: Void?): Void? {
app.showLocationHelper.updateLocationsOnMap()
return null
}
}
2018-06-11 18:57:33 +02:00
companion object {
const val USED_BY_MY_LOCATION: Int = 1
const val USED_BY_USERS_LOCATIONS: Int = 2
const val USAGE_INTENT = "SERVICE_USED_BY"
2018-06-13 16:18:54 +02:00
const val USAGE_OFF_INTERVAL = "SERVICE_OFF_INTERVAL"
const val SEND_LOCATION_INTERVAL = "SEND_LOCATION_INTERVAL"
2018-06-13 16:18:54 +02:00
2018-06-14 14:21:58 +02:00
const val OFF_INTERVAL_THRESHOLD: Long = 30000L
2018-06-13 16:18:54 +02:00
private var lockStatic: PowerManager.WakeLock? = null
@Synchronized
fun getLock(context: Context): PowerManager.WakeLock {
var lockStatic = lockStatic
2018-06-13 20:01:16 +02:00
return if (lockStatic == null) {
2018-06-13 16:18:54 +02:00
val mgr = context.getSystemService(Context.POWER_SERVICE) as PowerManager
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "OsmandServiceLock")
this.lockStatic = lockStatic
2018-06-13 20:01:16 +02:00
lockStatic
2018-06-13 16:18:54 +02:00
} else {
2018-06-13 20:01:16 +02:00
lockStatic
2018-06-13 16:18:54 +02:00
}
}
2018-06-11 18:57:33 +02:00
2018-06-14 14:21:58 +02:00
fun isUsedByMyLocation(usedBy: Int): Boolean {
return (usedBy and USED_BY_MY_LOCATION) > 0
}
fun isUsedByUsersLocations(usedBy: Int): Boolean {
return (usedBy and USED_BY_USERS_LOCATIONS) > 0
}
2018-06-14 14:21:58 +02:00
fun isOffIntervalDepended(usedBy: Int): Boolean {
return isUsedByMyLocation(usedBy)
}
fun normalizeOffInterval(interval: Long): Long {
return if (interval < OFF_INTERVAL_THRESHOLD) 0 else interval
}
2018-06-11 18:57:33 +02:00
fun convertLocation(l: Location?): net.osmand.Location? {
if (l == null) {
return null
}
val r = net.osmand.Location(l.provider)
r.latitude = l.latitude
r.longitude = l.longitude
r.time = l.time
if (l.hasAccuracy()) {
r.accuracy = l.accuracy
}
if (l.hasSpeed()) {
r.speed = l.speed
}
if (l.hasAltitude()) {
r.altitude = l.altitude
}
if (l.hasBearing()) {
r.bearing = l.bearing
}
if (l.hasAltitude()) {
r.altitude = l.altitude
}
return r
}
}
}