Add live tracks via gpx aidl import

This commit is contained in:
Chumva 2019-07-17 17:23:27 +03:00
parent caac552822
commit 43797bae3c
7 changed files with 173 additions and 44 deletions

View file

@ -21,6 +21,7 @@ import org.drinkless.td.libcore.telegram.TdApi
import java.util.*
private const val UPDATE_LIVE_MESSAGES_INTERVAL_MS = 10000L // 10 sec
private const val UPDATE_LIVE_TRACKS_INTERVAL_MS = 30000L // 30 sec
class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener,
TelegramOutgoingMessagesListener {
@ -32,6 +33,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
private var updateShareInfoHandler: Handler? = null
private var mHandlerThread = HandlerThread("SharingServiceThread")
private var updateTracksHandler: Handler? = null
private var tracksHandlerThread = HandlerThread("TracksUpdateServiceThread")
var handler: Handler? = null
private set
var usedBy = 0
@ -53,7 +57,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
override fun onCreate() {
super.onCreate()
mHandlerThread.start()
tracksHandlerThread.start()
updateShareInfoHandler = Handler(mHandlerThread.looper)
updateTracksHandler = Handler(tracksHandlerThread.looper)
}
override fun onBind(intent: Intent): IBinder? {
@ -103,6 +109,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
}
if (isUsedByUsersLocations(usedBy)) {
app.telegramHelper.startLiveMessagesUpdates(app.settings.sendMyLocInterval)
startTracksUpdates()
}
val locationNotification = app.notificationHelper.locationNotification
@ -130,6 +137,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
app.telegramHelper.removeOutgoingMessagesListener(this)
app.settings.save()
app.telegramService = null
tracksHandlerThread.quit()
mHandlerThread.quit()
usedBy = 0
@ -190,6 +198,17 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
}
}, UPDATE_LIVE_MESSAGES_INTERVAL_MS)
}
private fun startTracksUpdates() {
updateTracksHandler?.postDelayed({
if (isUsedByUsersLocations(usedBy)) {
if (app().settings.hasAnyLiveTracksToShowOnMap()) {
app().showLocationHelper.startUpdateTracksTask()
}
startTracksUpdates()
}
}, UPDATE_LIVE_TRACKS_INTERVAL_MS)
}
@SuppressLint("MissingPermission")
private fun getFirstTimeRunDefaultLocation(): net.osmand.Location? {

View file

@ -164,6 +164,19 @@ class TelegramSettings(private val app: TelegramApplication) {
fun hasAnyChatToShowOnMap() = !hiddenOnMapChats.containsAll(getLiveNowChats())
fun hasAnyLiveTracksToShowOnMap(): Boolean {
val time = System.currentTimeMillis() - locHistoryTime * 1000
val locations = app.locationMessages.getLastLocationMessagesSinceTime(time)
locations.forEach { loc ->
if (liveTracksInfo.any { it.userId == loc.userId && it.chatId == loc.chatId && it.deviceName == loc.deviceName }) {
return true
}
}
return false
}
fun getLiveTracksInfo() = liveTracksInfo
fun isShowingChatOnMap(chatId: Long) = !hiddenOnMapChats.contains(chatId)
fun isLiveTrackEnabled(userId: Int, chatId: Long, deviceName: String) =

View file

@ -79,6 +79,8 @@ class LocationMessages(val app: TelegramApplication) {
fun getLastLocationInfoForUserInChat(userId: Int, chatId: Long, deviceName: String) =
lastLocationPoints.sortedByDescending { it.time }.firstOrNull { it.userId == userId && it.chatId == chatId && it.deviceName == deviceName }
fun getLastLocationMessagesSinceTime(time: Long) = lastLocationPoints.filter { it.time > time }
fun addBufferedMessage(message: BufferMessage) {
log.debug("addBufferedMessage $message")
val messages = mutableListOf(*this.bufferedMessages.toTypedArray())

View file

@ -211,6 +211,25 @@ class OsmandAidlHelper(private val app: TelegramApplication) {
return null
}
/**
* Get list of all imported GPX files.
*
* @return list of imported gpx files.
*/
val importedGpxFiles: List<AGpxFile>?
get() {
if (mIOsmAndAidlInterface != null) {
try {
val files = mutableListOf<AGpxFile>()
mIOsmAndAidlInterface!!.getImportedGpx(files)
return files
} catch (e: RemoteException) {
e.printStackTrace()
}
}
return null
}
init {
connectOsmand()
}

View file

@ -4,6 +4,9 @@ import android.content.Intent
import android.graphics.Color
import android.os.AsyncTask
import android.text.TextUtils
import net.osmand.GPXUtilities
import net.osmand.PlatformUtil
import net.osmand.aidl.gpx.AGpxFile
import net.osmand.aidl.map.ALatLon
import net.osmand.aidl.maplayer.point.AMapPoint
import net.osmand.aidl.mapmarker.AMapMarker
@ -24,6 +27,8 @@ import java.util.concurrent.Executors
class ShowLocationHelper(private val app: TelegramApplication) {
private val log = PlatformUtil.getLog(ShowLocationHelper::class.java)
companion object {
const val MAP_LAYER_ID = "telegram_layer"
@ -32,11 +37,14 @@ class ShowLocationHelper(private val app: TelegramApplication) {
const val MAP_CONTEXT_MENU_BUTTON_ID = 1
const val MAP_CONTEXT_MENU_BUTTONS_PARAMS_ID = "DIRECTION"
const val DIRECTION_ICON_ID = "ic_action_start_navigation"
const val LIVE_TRACKS_DIR = "livetracks"
}
private val telegramHelper = app.telegramHelper
private val osmandAidlHelper = app.osmandAidlHelper
private val executor = Executors.newSingleThreadExecutor()
private val liveTracksExecutor = Executors.newSingleThreadExecutor()
private val points = ConcurrentHashMap<String, TdApi.Message>()
private val markers = ConcurrentHashMap<String, AMapMarker>()
@ -246,6 +254,72 @@ class ShowLocationHelper(private val app: TelegramApplication) {
osmandAidlHelper.removeContextMenuButtons(MAP_CONTEXT_MENU_BUTTONS_PARAMS_ID)
}
private fun updateTracksOnMap() {
val startTime = System.currentTimeMillis()
osmandAidlHelper.execOsmandApi {
val gpxFiles = getLiveGpxFiles()
if (gpxFiles.isEmpty()) {
return@execOsmandApi
}
val importedGpxFiles = osmandAidlHelper.importedGpxFiles
gpxFiles.forEach {
if (!isGpxAlreadyImported(importedGpxFiles, it)) {
val listener = object : OsmandLocationUtils.SaveGpxListener {
override fun onSavingGpxFinish(path: String) {
log.debug("LiveTracks onSavingGpxFinish $path time ${startTime - System.currentTimeMillis()}")
val uri = AndroidUtils.getUriForFile(app, File(path))
val destinationPath = "$LIVE_TRACKS_DIR/${it.metadata.name}.gpx"
osmandAidlHelper.importGpxFromUri(uri, destinationPath, GPXUtilities.GPXColor.AQUA.name, true)
log.debug("LiveTracks importGpxFromUri finish time ${startTime - System.currentTimeMillis()}")
}
override fun onSavingGpxError(error: Exception) {
log.error(error)
}
}
OsmandLocationUtils.saveGpx(app, it, listener)
}
}
}
}
private fun isGpxAlreadyImported(importedGpxFiles: List<AGpxFile>?, gpxFile: GPXUtilities.GPXFile): Boolean {
if (importedGpxFiles != null && importedGpxFiles.isNotEmpty()) {
val name = "${gpxFile.metadata.name}.gpx"
val aGpxFile = importedGpxFiles.firstOrNull { it.fileName == name }
if (aGpxFile != null) {
val startTimeImported = aGpxFile.details?.startTime
val endTimeImported = aGpxFile.details?.endTime
if (startTimeImported != null && endTimeImported != null) {
val startTime = gpxFile.findPointToShow()?.time
val endTime = gpxFile.lastPoint?.time
if (aGpxFile.details?.startTime == startTime && endTimeImported == endTime) {
return true
}
}
}
}
return false
}
private fun getLiveGpxFiles(): List<GPXUtilities.GPXFile> {
val currentTime = System.currentTimeMillis()
val start = currentTime - app.settings.locHistoryTime * 1000
val locationMessages = mutableListOf<LocationMessages.LocationMessage>()
app.settings.getLiveTracksInfo().forEach {
val messages = app.locationMessages.getMessagesForUserInChat(it.userId, it.chatId, it.deviceName, start, currentTime)
if (messages.isNotEmpty()) {
locationMessages.addAll(messages)
}
}
return OsmandLocationUtils.convertLocationMessagesToGpxFiles(app, locationMessages)
}
fun startShowingLocation() {
if (!showingLocation && !forcedStop) {
showingLocation = if (isUseOsmandCallback() && !app.settings.monitoringEnabled) {
@ -316,6 +390,10 @@ class ShowLocationHelper(private val app: TelegramApplication) {
fun startUpdateMessagesTask() {
UpdateMessagesTask(app).executeOnExecutor(executor)
}
fun startUpdateTracksTask() {
UpdateTracksTask(app).executeOnExecutor(liveTracksExecutor)
}
private class ShowMessagesTask(private val app: TelegramApplication) : AsyncTask<TdApi.Message, Void, Void?>() {
@ -345,6 +423,14 @@ class ShowLocationHelper(private val app: TelegramApplication) {
}
}
private class UpdateTracksTask(private val app: TelegramApplication) : AsyncTask<Void, Void, Void?>() {
override fun doInBackground(vararg params: Void?): Void? {
app.showLocationHelper.updateTracksOnMap()
return null
}
}
private fun generatePointDetails(bearing: Float?, altitude: Float?, precision: Float?): List<String> {
val details = mutableListOf<String>()
if (bearing != null && bearing != 0.0f) {

View file

@ -34,7 +34,6 @@ import net.osmand.telegram.helpers.TelegramUiHelper
import net.osmand.telegram.utils.AndroidUtils
import net.osmand.telegram.utils.OsmandFormatter
import net.osmand.telegram.utils.OsmandLocationUtils
import net.osmand.telegram.utils.TRACKS_DIR
import net.osmand.util.Algorithms
import java.io.File
import java.text.SimpleDateFormat
@ -105,10 +104,8 @@ class UserGpxInfoFragment : BaseDialogFragment() {
openGpx(path)
}
override fun onSavingGpxError(warning: String?) {
warning?.also {
Toast.makeText(app, it, Toast.LENGTH_LONG).show()
}
override fun onSavingGpxError(error: Exception) {
Toast.makeText(app, error.message, Toast.LENGTH_LONG).show()
}
})
}
@ -204,10 +201,8 @@ class UserGpxInfoFragment : BaseDialogFragment() {
(activity as MainActivity).shareGpx(path)
}
override fun onSavingGpxError(warning: String?) {
warning?.also {
Toast.makeText(app, it, Toast.LENGTH_LONG).show()
}
override fun onSavingGpxError(error: Exception) {
Toast.makeText(app, error.message, Toast.LENGTH_LONG).show()
}
})
}
@ -271,10 +266,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
}
private fun saveCurrentGpxToFile(listener: OsmandLocationUtils.SaveGpxListener) {
if (!gpxFile.isEmpty) {
val file = File(app.getExternalFilesDir(null), TRACKS_DIR)
OsmandLocationUtils.saveGpx(app, listener, file, gpxFile)
}
OsmandLocationUtils.saveGpx(app, gpxFile, listener)
}
private fun readFromBundle(bundle: Bundle?) {
@ -335,7 +327,7 @@ class UserGpxInfoFragment : BaseDialogFragment() {
checkTime()
locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId, deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis)
gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app.packageName, locationMessages).firstOrNull() ?: GPXUtilities.GPXFile(app.packageName)
gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app, locationMessages).firstOrNull() ?: GPXUtilities.GPXFile(app.packageName)
updateGPXStatisticRow()
updateDateAndTimeButtons()
updateGPXMap()
@ -405,8 +397,8 @@ class UserGpxInfoFragment : BaseDialogFragment() {
}
}
override fun onSavingGpxError(warning: String?) {
log.debug("onSavingGpxError $warning")
override fun onSavingGpxError(error: Exception) {
log.error(error)
}
})
}

View file

@ -414,7 +414,7 @@ object OsmandLocationUtils {
return TdApi.InputMessageText(TdApi.FormattedText(textMessage, entities.toTypedArray()), true, true)
}
fun convertLocationMessagesToGpxFiles(author:String, items: List<LocationMessage>, newGpxPerChat: Boolean = true): List<GPXUtilities.GPXFile> {
fun convertLocationMessagesToGpxFiles(app: TelegramApplication, items: List<LocationMessage>, newGpxPerChat: Boolean = true): List<GPXUtilities.GPXFile> {
val dataTracks = ArrayList<GPXUtilities.GPXFile>()
var previousTime: Long = -1
@ -434,10 +434,9 @@ object OsmandLocationUtils {
}
countedLocations++
if (previousUserId != userId || (newGpxPerChat && previousChatId != chatId)) {
gpx = GPXUtilities.GPXFile(author).apply {
gpx = GPXUtilities.GPXFile(app.packageName).apply {
metadata = GPXUtilities.Metadata().apply {
extensionsToWrite["chatId"] = chatId.toString()
extensionsToWrite["userId"] = userId.toString()
name = getGpxFileNameForUserId(app, userId, time)
}
}
previousTime = 0
@ -478,13 +477,27 @@ object OsmandLocationUtils {
return dataTracks
}
fun saveGpx(app: TelegramApplication, listener: SaveGpxListener, dir: File, gpxFile: GPXUtilities.GPXFile) {
fun saveGpx(app: TelegramApplication, gpxFile: GPXUtilities.GPXFile, listener: SaveGpxListener) {
if (!gpxFile.isEmpty) {
val task = SaveGPXTrackToFileTask(app, listener, gpxFile, dir, 0)
val dir = File(app.getExternalFilesDir(null), TRACKS_DIR)
val task = SaveGPXTrackToFileTask(listener, gpxFile, dir)
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
}
}
fun getGpxFileNameForUserId(app: TelegramApplication, userId: Int, time: Long): String {
var userName = userId.toString()
try {
val user = app.telegramHelper.getUser(userId)
if (user != null) {
userName = TelegramUiHelper.getUserName(user)
}
} catch (e: NumberFormatException) {
//ignore
}
return "${userName}_${UTC_DATE_FORMAT.format(Date(time))}"
}
abstract class MessageLocation : TdApi.MessageContent() {
var lat: Double = Double.NaN
@ -524,8 +537,9 @@ object OsmandLocationUtils {
}
private class SaveGPXTrackToFileTask internal constructor(
private val app: TelegramApplication, private val listener: SaveGpxListener?,
private val gpxFile: GPXUtilities.GPXFile, private val dir: File, private val userId: Int
private val listener: SaveGpxListener?,
private val gpxFile: GPXUtilities.GPXFile,
private val dir: File
) :
AsyncTask<Void, Void, java.lang.Exception>() {
@ -535,25 +549,9 @@ object OsmandLocationUtils {
if (dir.exists()) {
// save file
if (!gpxFile.isEmpty) {
val pt = gpxFile.findPointToShow()
val userId = gpxFile.metadata.extensionsToRead["userId"]
val user = if (!userId.isNullOrEmpty()) {
try {
app.telegramHelper.getUser(userId.toInt())
} catch (e: NumberFormatException) {
null
}
} else {
null
}
val fileName: String
fileName = if (user != null) {
(TelegramUiHelper.getUserName(user) + "_" + SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date(pt.time)))
} else {
userId.toString() + "_" + SimpleDateFormat("yyyy-MM-dd", Locale.US).format(Date(pt.time))
}
gpxFile.metadata?.name = fileName
val fileName = gpxFile.metadata.name
val fout = File(dir, "$fileName.gpx")
return GPXUtilities.writeGpxFile(fout, gpxFile)
}
}
@ -566,7 +564,7 @@ object OsmandLocationUtils {
if (warning == null) {
listener.onSavingGpxFinish(gpxFile.path)
} else {
listener.onSavingGpxError(warning.message)
listener.onSavingGpxError(warning)
}
}
}
@ -576,6 +574,6 @@ object OsmandLocationUtils {
fun onSavingGpxFinish(path: String)
fun onSavingGpxError(warning: String?)
fun onSavingGpxError(error: Exception)
}
}